Component trong Vue có thể được tạo ra với phương thức Vue.component() với đầu vào là một đối tượng chứa các tùy chọn như data, template… Trong template có thể sử dụng dữ liệu đã được khai báo trong data của component và không thể sử dụng được các dữ liệu được đăng ký trong data của Vue instance cũng như của các component khác một cách trực tiếp.

1.Phạm vi dữ liệu của component

Chúng ta cùng xem xét một ví dụ tạo ra một Hello component và component này sẽ lấy dữ liệu tên người dùng để hiển thị câu chào.

<!DOCTYPE html>
<html>
<head>
   <title>Phạm vi dữ liệu Component Vue.js</title>
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
   <template id="hello-template">
       <h1>{{ message }} {{ userName }}</h1>
   </template>
   <div class="container" id="app">
       <hello-component></hello-component>
   </div>
   <script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
   <script type="text/javascript">
       Vue.component('hello-component',{
           data: function () {
               return {
                   message: 'Xin chào'
               }
           },
           template: '#hello-template'
       })
       new Vue({
           el: '#app',
           data: {
               userName: 'VietnamLab Center'
           }
       });
   </script>
</body>
</html>

Chúng ta kỳ vọng rằng màn hình sẽ hiển thị câu chào “Xin chào VietnamLab Center” nhưng không nó chỉ hiển thị “Xin chào” mà không in ra tên. Mở tab console lên xem có lỗi gì không?

enter image description here

Chúng ta thấy có thông báo lỗi: “Thuộc tính userName không được khai báo nhưng vẫn tham chiếu đến khi render”. Như vậy trong template chỉ được tham chiếu đến các thuộc tính dữ liệu được khai báo trong data của component ? Template chỉ có thể tham chiếu đến các thuộc tính trực tiếp nếu thuộc tính đó của component. Với các thuộc tính, dữ liệu ở phạm vi ngoài component chúng ta phải sử dụng đến một cách khác sẽ được giới thiệu trong phần tiếp theo.

2.Truyền dữ liệu vào một component

Component là một khối mã nguồn bao gồm cả HTML, Javascript, CSS giống như function trong các ngôn ngữ lập trình. Vậy nó cũng cần có các tham số để có thể truyền dữ liệu từ ngoài vào trong khối mã nguồn đó. Vue.component() làm việc này thông qua thuộc tính props. Truyền dữ liệu vào một component bao gồm 2 bước:

  • Bước 1: Khai báo các thuộc tính có thể tham chiếu trong template với tùy chọn props.
  • Bước 2: Truyền dữ liệu cho thuộc tính khai báo với v-bind khi sử dụng component
    Ví dụ:
<!DOCTYPE html>
<html>
<head>
    <title>Truyền dữ liệu vào Component Vue.js với props</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
    <template id="hello-template">
        <h1>{{ message }} {{ userName }}</h1>
    </template>
    <div class="container" id="app">
        <hello-component v-bind:user-name="userName"></hello-component>
    </div>
    <script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
    <script type="text/javascript">
        Vue.component('hello-component',{
            data: function () {
                return {
                    message: 'Xin chào'
                }
            },
            props: ['userName'],
            template: '#hello-template'
        })
        new Vue({
            el: '#app',
            data: {
                userName: 'VietnamLab Center'
            }
        });
    </script>
</body>
</html>

Code điều chỉnh này chỉ khác một chút so với ví dụ đầu tiên:
Thay đổi thứ nhất: Khai báo thuộc tính tham chiếu đến từ bên ngoài trong tùy chọn props

props: ['userName']

Thay đổi thứ hai: Truyền dữ liệu vào thuộc tính user-name khi sử dụng hello-component

<hello-component v-bind:user-name="userName"></hello-component>

Vậy tại sao là v-bind:user-name mà không phải v-bind:userName? Do các thuộc tính trong HTML là không phân biệt hoa thường (case insensitive), như vậy trình duyệt sẽ thông dịch các ký tự viết hoa cũng như các ký tự viết thường. Do đó, khi bạn sử dụng các template với các thành phần DOM, các prop có tên kiểu camelCase cần được chuyển sang tương đương với dạng kebab-cased.

3.Các dạng dữ liệu có thể truyền vào một component

Trong ngôn ngữ HTML, khi muốn truyền một giá trị cho thuộc tính của thẻ HTML nào đó ta chỉ việc dùng cú pháp:

<html-tag property="value">

Với framework Vue.js chúng ta có thể sử dụng cả cách thông thường như trên hoặc sử dụng v-bind, hai cách có một số điểm khác nhau.

<html-tag v-bind:property-a="propertyB">
Chú ý: v-bind:property có thể viết ngắn gọn thành :property
<html-tag :property-a="propertyB">

v-bind được sử dụng để gán một biểu thức Javascript vào một thuộc tính của thẻ HTML. Do vậy, khi bạn sử dụng v-bind:my-number=”78″ thì 78 được hiểu như là một biểu thức Javascript hơn là một chuỗi. Với cách truyền giá trị kiểu HTML thì giá trị truyền vào sẽ là một chuỗi thông thường.

3.1 Truyền một số vào component

<blog-post v-bind:likes="42"></blog-post>
<blog-post v-bind:likes="post.likes"></blog-post>

3.2 Truyền dữ liệu boolean vào component

<blog-post favorited></blog-post>
<base-input v-bind:favorited="false">
<base-input v-bind:favorited="post.currentUserFavorited">

3.4 Truyền mảng vào component

<blog-post v-bind:comments="{ id: 1, title: 'My Journey with Vue' }"></blog-post>
<blog-post v-bind:post="post"></blog-post>

3.4 Truyển một thuộc tính của đối tượng vào component

post: {
    id: 1,
    title: 'My Journey with Vue'
  }
<blog-post 
  v-bind:id="post.id"
  v-bind:title="post.title">
</blog-post>

4.Component bên trong component khác

Cái hay nhất của component là có thể sử dụng lại trong một component khác, như vậy các ứng dụng chỉ đơn giản là các component được lắp ghép với nhau. Ví dụ:

<!DOCTYPE html>
<html>
<head>
<title>Component trong component</title>
<link rel="stylesheet" 	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
<template id="hello-template">
    <div class="row">
        <div class="col-md-12">
            <h1>{{ message }} {{ userName }}</h1>
        </div>
    </div>
</template>
<template id="form-template">
    <div class="row">
        <div class="col-md-3">
            <label for="name">Tên bạn là gì?</label>
        </div>
        <div class="col-md-9">
            <input class="form-control" v-model="userName" type="text">
        </div>
    </div>
</template>
<template id="greeting-template">
    <div>
        <form-component :user-name="userName"></form-component>
        <hello-component :user-name="userName"></hello-component>
    </div>
</template>
<div class="container" id="app">
    <greeting-component></hello-component>
</div>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<script type="text/javascript">
    Vue.component('hello-component',{
        data: function () {
            return {
                message: 'Xin chào'
            }
        },
        props: ['userName'],
        template: '#hello-template'
    })
    Vue.component('form-component', {
        template: '#form-template',
        props: ['userName']
    });
    Vue.component('greeting-component', {
        template: '#greeting-template',
        data: function () {
            return {
                userName: 'Allaravel'
            }
        }
    });
    new Vue({
        el: '#app'
    });
</script>
</body>
</html>

Trong ví dụ này chúng ta có 3 component: hello-component để hiển thị lời chào, form-component để nhập vào tên người dùng và greeting-component sử dụng hai component trước đó tạo thành một component mới. Chúng ta cũng gặp lại cú pháp truyền dữ liệu từ ngoài vào trong component, nó cũng được áp dụng khi truyền dữ liệu từ component cha vào component con.

Mọi thứ hiển thị bình thường nhưng khi bạn thử thay đổi ô nhập liệu tên người dùng, bạn sẽ nhận được một lỗi từ Vue.js:

enter image description here


Lỗi này là tránh thay đổi giá trị được truyền vào một cách trực tiếp, thay vào đó sử dụng một thuộc tính được khai báo trong data hoặc computed dựa trên giá trị truyền vào. Giá trị bị thay đổi ở đây là userName. Thay đổi lại code trên theo gợi ý core Vue.js đưa ra:

<template id="form-template">
<div class="row">
    <div class="col-md-3">
        <label for="name">Tên bạn là gì?</label>
    </div>
    <div class="col-md-9">
        <input class="form-control" v-model="name" type="text">
    </div>
</div>
</template>
Vue.component('form-component', {
    data: function (){
        return {
            name: this.userName
        }
    },
    template: '#form-template',
    props: ['userName']
});

Trong form-component chúng ta không sử dụng trực tiếp thuộc tính userName nữa mà khai báo một thuộc tính khác là name trong data và khởi tạo nó bằng giá trị của userName được truyền vào. Như vậy, khi bạn thay đổi tên người dùng trong ô nhập liệu, nó chỉ tác động đến thuộc tính name cục bộ. Với lời chào chúng ta muốn sẽ loại bỏ ký tự trống ở hai đầu chuỗi tên người dùng và viết hoa tên người dùng, tạo ra một thuộc tính computed để làm việc này

<template id="hello-template">
<div class="row">
    <div class="col-md-12">
        <h1>{{ message }} {{ uperCaseUser }}</h1>
    </div>
</div>
</template>
Vue.component('hello-component',{
    data: function (){
        return {
            message: 'Xin chào'
        }
    },
    computed: {
        uperCaseUser: function (){
            return this.userName.trim().toUpperCase()
        }
    },
    props: ['userName'],
    template: '#hello-template'
})

Tuy nhiên, code sửa đổi vẫn chưa đáp ứng được yêu cầu, chúng ta muốn khi thay đổi tên người dùng thì tên đó phải được cập nhật vào lời chào.

enter image description here

Tức là phần code trên mới chỉ đáp ứng được luồng dữ liệu truyền dữ liệu từ component cha vào component con (chiều 1), còn chiều ngược (chiều 2) lại hiện chưa thực hiện được do khi truyền từ cha vào con chỉ theo một chiều (one-way data binding).

5.Kết luận

Các khái niệm trong component đã dần được giới thiệu đến các bạn kèm với những ví dụ cụ thể bên trên, mình tóm lược lại sau khi phân tích:

  • Component là một code block chứa HTML, Javascript, CSS nó giống như "function" trong ngôn ngữ lập trình thông thường và nó cũng có thể có tham số để truyền dữ liệu vào.

Tất nhiên đánh giá trên chỉ là ý kiến cá nhân của mình. Còn có rất nhiều ví dụ để có thể đem ra so sánh nữa nên tùy vào mục đích của dự án của bạn mà sử dụng hợp lý. Cảm ơn các bạn đã theo dõi!!!!