基本示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <div id="components-demo"> <button-counter></button-counter> </div> <script> // 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) new Vue({ el: '#components-demo' }) </script>
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>
。
我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
组件的复用 可以将组件进行任意次数的复用:
1 2 3 4 5 <div id="components-demo"> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> </div>
当点击按钮时,每个组件都会各自独立维护它的 count
。因为你每用一次组件,就会有一个它的新实例被创建。
data
必须是一个函数当我们定义这个 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例返回的对象都是各自独立的:
1 2 3 4 5 data: function () { return { count: 0 } }
全局注册与局部注册 用 Vue.component
函数注册组件为全局组件,全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例中,也包括其组件树中的所有子组件的模板中。但全局注册也会有其他的不理想的地方,比如,你使用一个像 webpack 这样的构建系统,全局注册的组件意味着即使你已经不再使用该组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
这种情况下,可以通过一个普通的 Javascript 对象来定义组件,然后在components
选项中定义你想要使用的组件:
1 2 3 4 5 6 7 8 9 10 var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ } new Vue({ el: '#app' components: { 'component-a': ComponentA, 'component-b': ComponentB } })
局部注册的组件在其子组件中不可用,例如,如果希望ComponentA
在ComponentB
中可用,则需要这样写:
1 2 3 4 5 6 7 8 var ComponentA = { /* ... */ } var ComponentB = { components: { 'component-a': ComponentA }, // ... }
或者通过 Babel 和 webpack 使用 ES2015模块,代码如下:
1 2 3 4 5 6 7 8 import ComponentA from './ComponentA.vue' export default { components: { ComponentA }, // ... }
通过 prop 向子组件传递数据 Prop 是指你可以在组件上注册一些自定义属性。当一个值传递给一个 prop 属性的时候,它就变成了那个组件实例的一个属性。
1 2 3 4 Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
一个 prop 被注册之后,你就可以像如下这样把数据作为一个自定义属性传递进来:
1 2 3 <blog-post title="My journey with Vue"></blog-post> <blog-post title="Blogging with Vue"></blog-post> <blog-post title="Why Vue is so fun"></blog-post>
实际应用中,很可能在 Vue 实例的data
里有一个 blog 的数组,如下所示:
1 2 3 4 5 6 7 8 9 10 new Vue({ el: '#components-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
然后,在模板中通过v-bind
动态渲染每一个 blog
1 2 3 4 5 <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
props 类型验证 可以为 props 中的值提供一个带有验证需求的对象,而不仅仅是一个字符串数组,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Vue.component('my-component', { props: { // 基础的类型检查 (`null` 匹配任何类型) propA: Number, // 多个可能的类型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 带有默认值的数字 propD: { type: Number, default: 100 }, // 带有默认值的对象 propE: { type: Object, // 对象或数组且一定会从一个工厂函数返回默认值 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } })
通过事件向父级组件发送事件 通过一个按钮来放大 blog 的字号demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <div id="blog-posts-events-demo"> <div :style="{fontSize: postFontSize+'em'}"> <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="postFontSize += 0.1"> </blog-post> </div> </div> <script> Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <button v-on:click="$emit('enlarge-text')"> Enlarge text </button> <div v-html="post.content"></div> </div> ` }) new Vue({ el: '#blog-posts-events-demo', data: { posts: [ { id: 1, title: 'My journey with Vue', content: '<h2>This is the first content</h2>' }, { id: 2, title: 'Blogging with Vue', content: '<h2>This is the second content</h2>'}, { id: 3, title: 'Why Vue is so fun', content: '<h2>This is the third content</h2>' } ], postFontSize: 1 } }) </script>
使用事件抛出一个值 如果想要控制每次点击要放大的值,可以用$emit
的第二个参数来控制:
1 2 3 <button v-on:click="$emit('enlarge-text', 0.1)"> Enlarge text </button>
然后当在父级组件监听这个事件的时候,我们可以通过$event
访问到被抛出的这个值:
1 2 3 4 <blog-post ... v-on:enlarge-text="postFontSize += $event" ></blog-post>
或者,通过实现处理函数:
1 2 3 4 5 6 7 8 9 10 <blog-post ... v-on:enlarge-text="onEnlargeText" ></blog-post> methods: { onEnlargeText: function (enlargeAmount) { this.postFontSize += enlargeAmount } }
通过插槽分发内容 demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id="slot_demo"> <alert-box> Something bad happened. </alert-box> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` }) new Vue({ el: '#slot_demo' }) </script>