Vue.js的组件
注册组件
全局注册
要注册一个全局组件,你可以使用 Vue.component(tagName, options)。例如:
Vue.component('my-component', { // 选项})
组件在注册之后,便可以在父实例的模块中以自定义元素 < my-component >< /my-component > 的形式使用。要确保在初始化根实例之前注册了组件:
// 注册组件Vue.component('my-component', { template: 'hi girl
'})// 创建根实例new Vue({ el: '#box'})
结果
hi,girl
局部注册
不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用:
var Child = { template: 'A custom component!'}new Vue({ // ... components: { 'my-component': Child }})
当我们感觉template里面写的东西太多了,我们还可以利用vue给我们提供的一个方法 x-template:
var Child = { template: '#a'}
DOM 模板解析说明
当使用 DOM 作为模板时 (例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容。尤其像这些元素 < ul >,< ol >,< table >,< select > 限制了能被它包裹的元素,而一些像 < option > 这样的元素只能出现在某些其它元素内部。
在自定义组件中使用这些受限制的元素时会导致一些问题,例如:...
自定义组件 <my-row> 被认为是无效的内容,因此在渲染的时候会导致错误。变通的方案是使用特殊的 is 属性:
data 必须是函数
通过 Vue 构造器传入的各种选项大多数都可以在组件里用。data 是一个例外,它必须是函数。实际上,如果你这么做:
Vue.component('my-component', { template: '{ { message }}', data: { message: 'hello' }})
那么 Vue 会停止,并在控制台发出警告,告诉你在组件中 data 必须是一个函数。理解这种规则的存在意义很有帮助,让我们假设用如下方式来绕开 Vue 的警告:
var data = { counter: 0 }Vue.component('simple-counter', { template: '', // 技术上 data 的确是一个函数了,因此 Vue 不会警告, // 但是我们返回给每个组件的实例却引用了同一个 data 对象 data: function () { return data }})new Vue({ el: '#example-2'})
0 0 0
由于这三个组件共享了同一个 data,因此增加一个 counter 会影响所有组件!这不对。我们可以通过为每个组件返回全新的 data 对象来解决这个问题:
data: function () { return { counter: 0 }}
现在每个 counter 都有它自己内部的状态了:
0 0 0
构成组件
组件意味着协同工作,通常父子组件会是这样的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件。然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。
在 Vue 中,父子组件的关系可以总结为 props down, events up。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。使用 props 传递数据
如果我们定义了组件本身的属性时,我们需要 props 来注册属性,这样我们才能利用属性
Vue.component('mycom',{ template:"", props:['a'], metods:{ sss:function(){ alert(this.a) } } })
结果
ddd
父元素向子元素传递数据
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的 props 选项。
Vue.component('mycom',{ template:"", props:['a'], methods:{ sss:function(){ alert(this.a) } }})new Vue({ el:'#box', data:{ ddd:'hi' }})
结果
hi
子元素向父元素传递数据
Vue.component('mycom',{ template:"", props:['a'], methods:{ sss:function(){ this.$emit('fff') } }})new Vue({ el:'#box', data:{ ddd:'hi' }, methods:{ gg:function(){ alert(111) } }})
结果
111
我们也可以在组件触发的函数上传入参数
Vue.component('mycom',{ template:"", props:['a'], methods:{ sss:function(){ this.$emit('fff','a','b') } }})new Vue({ el:'#box', data:{ ddd:'hi' }, methods:{ gg:function(x,y){ alert(x+y) } }})
结果
ab
非父子关系传入数据
有时候两个组件也需要通信 (非父子关系)。在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线:
var bus = new Vue()
// 触发组件 A 中的事件bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件bus.$on('id-selected', function (id) { // ...})
camelCase vs. kebab-case
HTML 特性是不区分大小写的。所以,当使用的不是字符串模板,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:
Vue.component('child', { // camelCase in JavaScript props: ['myMessage'], template: '{ { myMessage }}'})
如果你使用字符串模板,则没有这些限制。
Prop 验证
我们可以为组件的 props 指定验证规格。如果传入的数据不符合规格,Vue 会发出警告。当组件给其他人使用时,这很有用。
要指定验证规格,需要用对象的形式,而不能用字符串数组:Vue.component('example', { 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 value > 10 } } }})
type 可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
- 也可以是一个自定义构造器函数,使用 instanceof 检测。
当 prop 验证失败,Vue 会抛出警告 (如果使用的是开发版本)。注意 props 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。
slot 内容分发
在使用组件时,我们常常要像这样组合它们:
注意两点:
< app > 组件不知道它会收到什么内容。这是由使用 < app > 的父组件决定的。< app > 组件很可能有它自己的模板。为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular)。Vue.js 实现了一个内容分发 API,参照了当前 Web 组件规范草案,使用特殊的 < slot > 元素作为原始内容的插槽。单个 Slot
除非子组件模板包含至少一个 < slot > 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。
最初在 < slot > 标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。假定 my-component 组件有下面模板:我是子组件的标题
只有在没有要分发的内容时才会显示。
父组件模板:
我是父组件的标题
这是一些初始内容
这是更多的初始内容
渲染结果:
我是父组件的标题
我是子组件的标题
这是一些初始内容
这是更多的初始内容
具名 Slot
<slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃。例如,假定我们有一个 app-layout 组件,它的模板为:父组件模板:
这里可能是一个页面标题
主要内容的一个段落。
另一个主要段落。
这里有一些联系信息
渲染结果为:
这里可能是一个页面标题
主要内容的一个段落。
另一个主要段落。
在组合组件时,内容分发 API 是非常有用的机制。