Open Twlig opened 2 years ago
组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。只是没el 这样根实例特有的选项。
new Vue
data
computed
watch
methods
el
Vue.component('my-component-name', { /* ... */ })
<div id="app"> <my-component-name></my-component-name> </div>
Vue.component('MyComponentName', { /* ... */ })
<my-component-name></my-component-name> <MyComponentName></MyComponentName>
注意:直接在 DOM (即非字符串的模板)如:在单个组件的<template></template>中 或者 index.html中直接CDN引入vue.js的<div id="app"></div>中,使用时只有 kebab-case 是有效的。
<template></template>
<div id="app"></div>
⭐建议:定义时两种方式都可以,引用使用kebab-case。不过为了统一还是建议定义和引用都用kebab-case。
组件注册有全局注册和局部注册两种。
全局注册的组件注册之后可以用在任何新建的Vue根实例的模板中。因为component相当于直接挂载在Vue属性上。所有Vue实例都可以共享该属性。
//全局注册 Vue.component('my-component-name', { // ... 选项 ... }) new Vue({ el: '#app' })
<!--使用--> <div id="app"> <my-component-name></my-component-name> </div>
全局注册所有的组件意味着即便已经不再使用一个组件了,它仍然会被包含在最终的构建结果中。这造成了用户下载的 JavaScript 的无谓增加。
因此可以局部注册:
定义组件和注册组件在一个文件内
var ComponentA = { /* ... */ }
所谓局部注册就是在Vue实例上注册,只有该实例可以使用,其他Vue实例需要自己注册才能使用。
new Vue({ el: '#app', components: { 'component-a': ComponentA } })
定义组件和注册组件在不同文件
假设在ComponentA.vue文件中定义了一个组件。
export default { name: 'ComponentA', /* ... */ }
在另一个组件ComponentB.vue中注册A组件。
import ComponentA from './ComponentA' //1. 导入A组件 export default { components: { ComponentA //2. 注册A组件 }, // ... }
在Vue基础指令中曾经说过,data有两种写法:
对象写法
data: { count: 0 }
函数写法
data: function () { return { count: 0 } }
⭐注意:但是在组件中只能用函数写法。因为组件是要被复用的,也就是说组件可能有很多个实例。当采用对象写法,则每一个组件实例都会指向相同的data对象。而函数写法新建组件实例后,调用data会执行一遍data函数,返回新的对象。每个实例都能有独立的数据。
<h3>{{ title }}</h3> <div v-html="content"></div>
在模板中采用上面的写法,Vue会报错,显示every component must have a single root element (每个组件必须只有一个根元素)。可以修改成下面的写法:
<div class="blog-post"> <h3>{{ title }}</h3> <div v-html="content"></div> </div>
⭐原因:因为一个组件的一般只有一个Vue实例,Vue实例要挂载DOM元素才能实现响应式更新。而如果组件内部有多个最外层的并列DOM元素。那挂载在哪一个上面都无法覆盖整个DOM节点。因此要采用一个单根元素去包裹全部DOM节点,实现整体响应式更新DOM。
假设有一个blog-post子组件:
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的, 使用时需要用kebab-case props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' })
父组件如下:
new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
<!--HTML中属性用kebab-case形式--> <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post-title="post.title" ></blog-post>
通过在子组件中定义需要的props属性,父组件可以通过v-bind动态属性把父组件的值传入子组件。这样每一篇博客的title就在子组件上显示了。
假设在子组件上存在一个按钮,通过点击子组件上的按钮,能放大字体。
父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件。
v-on
<blog-post ... v-on:enlarge-text="postFontSize += 0.1" ></blog-post>
子组件通过$emit触发enlarge-text事件。
$emit('enlarge-text')
<button v-on:click="$emit('enlarge-text')"> Enlarge text </button>
父组件监听到enlarge-text事件发生,进入(postFontSize += 0.1,也可以写成一个函数)事件处理程序。更新 postFontSize 的值,字体就变大了。
postFontSize
上面写法中,字体每次放大的数值是父组件决定的。但是如果要让子组件自己决定呢?我们该如何向父组件传递子组件的信息。
$emit
<button v-on:click="$emit('enlarge-text', 0.1)"> Enlarge text </button>
$event
<blog-post ... v-on:enlarge-text="postFontSize += $event" ></blog-post>
<blog-post ... v-on:enlarge-text="onEnlargeText" ></blog-post>
这个值会做为第一个参数传递过去:
methods: { onEnlargeText: function (enlargeAmount) { this.postFontSize += enlargeAmount } }
https://blog.csdn.net/weixin_47111901/article/details/116033575
组件是可复用的 Vue 实例,所以它们与
new Vue
接收相同的选项,例如data
、computed
、watch
、methods
以及生命周期钩子等。只是没el
这样根实例特有的选项。组件名
kebab-case(短横线分隔命名)
PascalCase(首字母大写命名)
⭐建议:定义时两种方式都可以,引用使用kebab-case。不过为了统一还是建议定义和引用都用kebab-case。
组件注册
组件注册有全局注册和局部注册两种。
全局注册
全局注册的组件注册之后可以用在任何新建的Vue根实例的模板中。因为component相当于直接挂载在Vue属性上。所有Vue实例都可以共享该属性。
局部注册
全局注册所有的组件意味着即便已经不再使用一个组件了,它仍然会被包含在最终的构建结果中。这造成了用户下载的 JavaScript 的无谓增加。
因此可以局部注册:
同文件局部注册
定义组件和注册组件在一个文件内
定义组件
局部注册组件
所谓局部注册就是在Vue实例上注册,只有该实例可以使用,其他Vue实例需要自己注册才能使用。
不同文件局部注册
定义组件和注册组件在不同文件
定义组件
假设在ComponentA.vue文件中定义了一个组件。
局部注册组件
在另一个组件ComponentB.vue中注册A组件。
组件注意事项
data必须是一个函数
在Vue基础指令中曾经说过,data有两种写法:
对象写法
函数写法
⭐注意:但是在组件中只能用函数写法。因为组件是要被复用的,也就是说组件可能有很多个实例。当采用对象写法,则每一个组件实例都会指向相同的data对象。而函数写法新建组件实例后,调用data会执行一遍data函数,返回新的对象。每个实例都能有独立的数据。
单根元素
在模板中采用上面的写法,Vue会报错,显示every component must have a single root element (每个组件必须只有一个根元素)。可以修改成下面的写法:
⭐原因:因为一个组件的一般只有一个Vue实例,Vue实例要挂载DOM元素才能实现响应式更新。而如果组件内部有多个最外层的并列DOM元素。那挂载在哪一个上面都无法覆盖整个DOM节点。因此要采用一个单根元素去包裹全部DOM节点,实现整体响应式更新DOM。
父子组件间的通信
1.通过 Prop 父组件向子组件传递数据
假设有一个blog-post子组件:
父组件如下:
通过在子组件中定义需要的props属性,父组件可以通过v-bind动态属性把父组件的值传入子组件。这样每一篇博客的title就在子组件上显示了。
2. 通过 $emit 子组件向父组件传递数据
假设在子组件上存在一个按钮,通过点击子组件上的按钮,能放大字体。
父组件监听enlarge-text事件
父级组件可以像处理 native DOM 事件一样通过
v-on
监听子组件实例的任意事件。子组件通过点击触发enlarge-text事件
子组件通过$emit触发enlarge-text事件。
$emit('enlarge-text')
事件处理程序父组件监听到enlarge-text事件发生
父组件监听到enlarge-text事件发生,进入(postFontSize += 0.1,也可以写成一个函数)事件处理程序。更新
postFontSize
的值,字体就变大了。使用事件抛出一个值
上面写法中,字体每次放大的数值是父组件决定的。但是如果要让子组件自己决定呢?我们该如何向父组件传递子组件的信息。
$emit
的第二个参数来提供这个值:$event
访问到被抛出的这个值:这个值会做为第一个参数传递过去: