Vue学习笔记(二)

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is特性扩展。

组件使用

定义

1
2
3
var MyComponent = Vue.extend({
// 选项...
});

注意:Vue构造器的多数选项也可以用在 Vue.extend() 中,不过有两个特例:datael。如果只是将数据直接放入创建函数中,则所有组件实例都会共享同样的数据,有时候这并不是我们希望的(实际上Vue在注册组件时直接给data对象会抛出一个警告,最终会拒绝执行data解析工作),所以可以传入一个函数作为data选项——函数返回一个真正的数据对象。

注册

全局注册

1
2
3
4
Vue.component(tag, constructor);
//like this:
Vue.component("my-component", MyComponent);

tag 表示自定义的组件名,建议使用 小写,用短杠分隔 的规则。component 是创建的组件。注册之后就可以在其它模板中使用此组件。

局部注册

局部注册的目的在于:让组件只能用在其它组件内,而不是全局都可以使用。,在实例选项 components 中注册:

1
2
3
4
5
6
7
8
9
var Child = Vue.extend({ /* ... */ });
var Parent = Vue.extend({
template: '...',
components: {
// <my-component> 只能用在父组件模板内
'my-component': Child
}
});

注册语法糖

Vue.component() 注册函数的第二个选项可以直接传入选项对象,Vue会自动调用 Vue.extend() 函数创建组件,使用方式可简化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在一个步骤中扩展与注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 局部注册也可以这么做
var Parent = Vue.extend({
components: {
'my-component': {
template: '<div>A custom component!</div>'
}
}
})

使用已注册的组件

使用 使用自定义组件(指调用Vue构造函数初始化根实例)之前必须已经创建并且注册组件。

1
2
3
<div id="app">
<my-component></my-component>
</div>

Vue进行模板解析的时候会遵循以下html常见的限制:

1
2
3
4
5
- a 不能包含其它的交互元素(如按钮,链接)
- ul 和 ol 只能直接包含 li
- select 只能包含 option 和 optgroup
- table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup
- tr 只能直接包含 th 和 td

对于自定义元素可以使用 is 特性:

1
2
3
4
5
<div id="example">
<ul>
<li is="my-component">此处内容不会显示</li>
</ul>
</div>

组件属性

组件实例的作用域是孤立的,props 用于将数据传给子组件。

props 是选项对象的一个字段,在组件构造时声明,在模板上用属性的方式传入:

1
2
3
4
5
6
7
Vue.component('child', {
// 声明 props
props: ['msg'],
// prop 可以用在模板内
// 可以用 `this.msg` 设置
template: '<span>{{ msg }}</span>'
})

1
<child msg="hello!"></child>

注意:在 props 中用camelCase声明的字段要转换成kebab-case的方式才会生效。

动态PROPS

在子组件中可以使用 v-bind 绑定动态Props到父组件的数据,当父组件的数据变化时,子组件也会跟着改变。

1
<child v-bind:my-message="parentMsg"></child>

PROPS绑定类型

prop 默认是单向绑定的,即:父组件的属性变化将传递给子组件,但是子组件的变化不会影响父组件。Vue提供 .sync.once 绑定修饰符用于显示地强制双向或单次绑定:

1
2
3
4
5
6
7
8
<!-- 默认为单向绑定 -->
<child :msg="parentMsg"></child>
<!-- 双向绑定 -->
<child :msg.sync="parentMsg"></child>
<!-- 单次绑定 -->
<child :msg.once="parentMsg"></child>

注意:如果prop是一个对象或者数组,Vue是按引用传递数据的,所以无论是否指明双向绑定,实际上都是“双向绑定”的。

PROPS验证

指定prop验证的意义在于:约束使用组件的其它人,带验证时 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型 (1.0.21+)
propM: [String, Number],
// 必需且是字符串
propB: {
type: String,
required: true
},
// 数字,有默认值
propC: {
type: Number,
default: 100
},
// 对象/数组的默认值应当由一个函数返回
propD: {
type: Object,
default: function () {
return { msg: 'hello' }
}
},
// 指定这个 prop 为双向绑定
// 如果绑定类型不对将抛出一条警告
propE: {
twoWay: true
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
},
// 转换函数(1.0.12 新增)
// 在设置值之前转换值
propG: {
coerce: function (val) {
return val + '' // 将值转换为字符串
}
},
propH: {
coerce: function (val) {
return JSON.parse(val) // 将 JSON 字符串转换为对象
}
}
}
});

type 可以是下面的原生构造器之一:

  • String
  • Number
  • Boolean
  • Function
  • Obiect
  • Array

也可以是自定义的构造器:使用 instanceof 检测。

组件通信

父链

父链指的是因组件之间的嵌套关系产生的关系链,Vue定义了三个关于父链的变量:

  • this.$parent:指向父组件的实例;
  • this.$root:指向根对象;
  • this.$children:父组件的一个数组,包含它所有的子元素;

自定义事件

Vue的自定义事件接口一共提供四个方法:

  • $on():监听事件;
  • $emit():在组件实例上触发事件;
  • $dispatch():派发事件,沿着父链冒泡;
  • $brjoadcast():广播事件,事件向下传导分为所有后代;

使用方式:

1
2
3
4
5
6
7
8
9
10
11
<!-- 子组件模板 -->
<template id="child-template">
<input v-model="msg">
<button v-on:click="notify">Dispatch Event</button>
</template>
<!-- 父组件模板 -->
<div id="events-example">
<p>Messages: {{ messages | json }}</p>
<child v-on:child-msg="handleIt"></child>
</div>

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
// 注册子组件
// 将当前消息派发出去
Vue.component('child', {
template: '#child-template',
data: function () {
return { msg: 'hello' }
},
methods: {
notify: function () {
if (this.msg.trim()) {
this.$emit('child-msg', this.msg)
this.msg = ''
}
}
}
})
// 初始化父组件
// 将收到消息时将事件推入一个数组
var parent = new Vue({
el: '#events-example',
data: {
messages: []
},
// 在创建实例时 `events` 选项简单地调用 `$on`
methods: {
'handleIt': function (msg) {
// 事件回调内的 `this` 自动绑定到注册它的实例上
this.messages.push(msg)
}
}
})

注意:

  • 不同于DOM事件,Vue事件在冒泡过程中第一次触发回调之后自动停止冒泡,除非回调明确返回true
  • Vue的事件时同步机制,执行dispatch后会等待一个返回再接着执行;

子组件索引

Vue中可以使用v-ref为子组件指定一个索引ID,用于直接访问子组件。Vue实例的$refs包含父组件中所有制定了v-ref属相的子组件,当执行v-refv-for一起使用时$refs是一个数组,否则是一个对象。

组件内容分发

Vue使用slot特性进行内容分发

在父组件放入已被slot标记的内容,这些内容的顺序可以随意。之后这些内容被分发到子组件的特殊元素slot中,根据name属性在子组件中重新组合。

需要注意的是,内容应该被放在父组件模板的子组件中:

1
2
3
4
5
6
7
8
9
<!--父组件模板-->
<div id="parent-template">
<child>
<!--此处是待分发的内容-->
<p slot="one">one</p>
<p slot="two">two</p>
<p>default</p>
</child>
</div>

父组件模板中的内容被分发到子组件中重新组合,请看子组件的模板:

1
2
3
4
5
6
7
<!--子组件的模板-->
<div>
<h1>内容被分发且重新组合</h1>
<slot>默认分发处</slot>
<slot name="two"></slot>
<slot name="one"></slot>
</div>

初始化父组件实例之前要祖册子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 注册子组件
Vue.component("child", {
template: '<div>'+
'<h1>内容被分发且重新组合</h1>'+
'<slot>默认分发处</slot>'+
'<slot name="two"></slot>'+
'<slot name="one"></slot>'+
'</div>'
});
// 初始化父组件
new Vue({
el: "#parent-template"
});

异步组件

Vue允许讲组件定义为一个工厂函数,在组件需要渲染是触发工厂函数动态地解析组件,并且将结果缓存起来:

1
2
3
4
5
6
7
8
Vue.component("async-component", function(resolve, reject){
// async operation
setTimeout(function() {
resolve({
template: '<div>something async</div>'
});
},1000);
});

配合WEBPACK实现代码分割

webpack可以讲代码分割成块,在需要次块时再使用ajax的方式下载:

1
2
3
4
5
6
Vue.component('async-webpack-example', function(resolve) {
// 这个特殊的 require 语法告诉 webpack
// 自动将编译后的代码分割成不同的块,
// 这些块将通过 ajax 请求自动下载。
require(['./my-async-component'], resolve)
});

谢谢你请我吃糖果