深入理解vue组件
# 深入理解vue组件
参考教程:
[TOC]
# 一、vue组件中的细节点
# 1.1 来自html5规范的bug
<div id="root">
<table>
<tbody>
<row></row>
</tbody>
</table>
</div>
<script>
Vue.component('row', {
template: '<tr><td>this is a row</td></tr>'
})
var vm = new Vue({
el: '#root'
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- bug:html5规定,在table里,td必须写在tr里,所以解析row的时候没法写在table里。
- 除bug:借助
is
属性
<tbody>
<tr is="row"></tr>
</tbody>
1
2
3
2
3
- 类似的case
<ul>
<li is="example1"></li>
</ul>
<select>
<option is="example2"></option>
</select>
1
2
3
4
5
6
7
2
3
4
5
6
7
# 1.2 非根组件的data
应写成函数形式
Vue.component('row', {
data: {
content: "this is a content"
},
template: '<tr><td>this is a row</td></tr>'
})
1
2
3
4
5
6
2
3
4
5
6
- [Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
- 避免子组件公用一套数据而造成的相互影响
// ES6写法
data (){
return {
content: "this is a content"
}
}
1
2
3
4
5
6
2
3
4
5
6
# 二、父子组件的数据传递
# 2.1 单向数据流
- 父组件可以向子组件传参,但子组件不可以改变父组件的数据,避免其他子组件受到影响。(这么做不会抛出错误,但会有警告)。
# 2.2 父传子props
// 父组件
<template>
<child :msg='msg'></child>
</template>
<script>
import Child from './Child'
export default {
components: {
Child
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
// 子组件
export default {
name: 'Child',
props: ['msg']
}
1
2
3
4
5
2
3
4
5
# 2.3 子传父$emit
- 父传子的数据若要实时更新,则应该这么做
watch: {
page: {
// 表示创建组件时 handler 回调会立即执行
// 省去在 created 函数中再次调用了
immediate: true,
handler (val) {
this.currentPage3 = val
}
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 使用修饰符简化写法
.sync
- 实现父子组件双向绑定数据,子组件可以改父组件的值
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件 触发事件必须要用双引号
this.$emit('update:myMessage',params);
1
2
3
4
2
3
4
# 三、组件参数校验与非props特性
# 3.1 组件参数校验
content: {
//接收的类型,可以接受两种数据类型 content: [String, Number]
type: String,
//参数是否必须接收
required: false,
//如果没有参数则显示默认值
default: 'default value',
//校验器
validator: function(value){
return (value.length > 5)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 3.2 非props特性
- 非props就是没有写props, 就无法接受参数,会警告。
- content属性会显示在dom上,props特性则不会。
# 四、非父子组件通信
# 4.1 vuex
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
# 4.2 Bus/总线/发布订阅模式/观察者模式
<div id="root">
<child content="xiao"></child>
<child content="lin"></child>
</div>
<script>
// 之后调用new Vue()或者创建组件的时候,每一个组件上都会有Bus这个属性
Vue.prototype.bus = new Vue()
Vue.component('child', {
data () {
return {
selfContent: this.content
}
},
props: {
content: String
},
template: '<div @click="handleClick">{{selfContent}}</div>',
methods: {
handleClick () {
this.bus.$emit('change', this.selfContent)
}
},
//将一个组件的子组件的数据传递给另一个组件的子组件
//el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
mounted () {
var _this = this
//在父组件中使用$on监听bus上触发出来事件
//会执行两遍(两个child都触发了一次事件监听)
this.bus.$on('change', function(msg) {
_this.selfContent = msg
})
}
})
var vm = new Vue({
el: '#root'
})
</script>
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
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
- personal分析(test通过)
- 点击
<child content="xiao"></child>
- 发生
handleClick
,触发change
事件,将selfContent
的值xiao
传给了msg
<child content="xiao"></child>
挂载完后执行mounted
并监听到change
,将xiao
替换了selfContent
的值xiao
<child content="lin"></child>
挂载完后执行mounted
并监听到change
,将lin
替换了selfContent
的值lin
- 点击
# 五、插槽(slot废弃,改为v-slot)
# 六、动态组件
注意事项:绑定事件应该是@value-updated="onUpdate",而不是@valueUpdated="onUpdate($event)"。
<div id="root">
<component :is="type"></component>
<!-- 等价于 -->
<!-- <child-one v-if="type==='child-one'"></child-one> -->
<!-- <child-two v-if="type==='child-two'"></child-two> -->
<button @click="handleBtnClick">change</button>
</div>
<script>
Vue.component('child-one', {
template: `<div v-once>child-one</div>`
})
Vue.component('child-two', {
template: `<div v-once>child-two</div>`
})
var vm = new Vue({
el: '#root',
data: {
type: 'child-one'
},
methods: {
handleBtnClick: function() {
this.type = (this.type === 'child-one' ? 'child-two' : 'child-one')
}
}
})
</script>
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
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
# 七、异步组件
# 八、创建更高层次的组件
详细教程:https://segmentfault.com/a/1190000020637062
Vue 学习笔记:$attrs 和 $listeners 的用法 (opens new window)
v-bind="$props": 可以将父组件的所有props下发给它的子组件,子组件需要在其props:{} 中定义要接受的props。
v-bind="$attrs": 将调用组件时的组件标签上绑定的非props的特性(class和style除外)向下传递。在子组件中应当添加inheritAttrs: false(避免父作用域的不被认作props的特性绑定应用在子组件的根元素上)。
vm.$listeners
: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
# 8.1 $attrs
- 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件—— 在创建高级别的组件时非常有用。
<template>
<div>
<el-input v-model="input" v-bind="$attrs"></el-input>
</div>
</template>
<script>
export default {
inheritAttrs: false, // 必不可少的代码,避免el-input的属性被添加到根元素div上
props: {
value: {
type: String,
default: '',
},
},
computed: {
input: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
}
}
}
}
</script>
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
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
- $attrs + inheritAttrs 可以使该组件可以直接使用el-input上的所有属性。
# 8.2 $listeners
- 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
<template>
<div>
<el-input v-model="input" v-bind="$attrs" v-on="$listeners"></el-input>
</div>
</template>
1
2
3
4
5
2
3
4
5
- $listeners 可以使该组件使用el-input绑定的事件。(注意:可以使用的是Input Event,非input Methods)
# 九、编写组件
# 9.1 v-model
props: {
value: {
// [key1,key2]
// v-model
type: Array,
default: () => [],
},
}
this.$emit("input", []); // 改变v-model的值
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10