5、VUE中的组件
组件
组件就是可复用的单元模块。
组件的data部分是一个函数,返回值是一个对象,在这个对象中传递要传递的值。
1 | Vue.component('组件名称', {组件选项}) |
- 组件名称遵循自定义组件命名规范:全小写、连字符(虽然驼峰式一般也没问题)
- 组件选项与
new Vue
选项配置基本一致(也有一些细节的不同)
全局组件通过Vue
对象进行创建,局部组件通过在组件的内部属性components
创建。
1 | new Vue({ |
data
在非 new Vue
的组件中,data
必须为函数,函数返回值必须是一个对象,作为组件的最终 data
1 | components: { |
props
组件中内部私有数据存储中组件 data
中,通过外部传入的数据,则通过 props
选项接收
- 如果传入的
props
值为一个表达式,则必须使用v-bind
- 组件中的
data
和props
数据都可以通过组件实例进行直接访问 data
中的key
与props
中的key
不能冲突
组件间的通信
父组件向子组件传值
父组件向子组件传值使用props
即可。
子组件通过props
属性接收外部传来的值(即父组件传来的值)
1 | Vue.component('menu-item', { |
传值通过标签属性传值:
1 | <!-- 不使用v-bind绑定,则传递字符串 --> |
子组件向父组件传值
子组件无法直接向父组件传值(修改父组件的值),因为props
是单向数据流。因此父组件向子组件传值需要通过事件触发同值父组件,父组件定义事件来修改值。
定义子组件
1
2
3
4
5
6
7
8
9
10
11Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
<button @click='$emit("enlarge-text",5)'>扩大父组件中字体大小</button>
</div>
`
});为子组件的按钮监听一个点击事件,当点击时触发自定义事件并传入5
调用组件
1
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
为组件绑定自定义事件事件,当事件发生时触发父级修改值的函数,并将事件对象传入
在父级中定义修改值的函数
handle
1
2
3
4
5
6
7
8
9
10
11
12
13
14var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple', 'orange', 'banana'],
fontSize: 10
},
methods: {
handle: function (val) {
// 扩大字体大小
this.fontSize += val;
}
}
});
组件间传值
开辟一个新的VUE实例
1
var hub = new Vue()
定义子组件A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var sona = {
template: `<div>
<p>组件A --- {{ msg }}</p>
<button @click='change'>按钮</button></div > `,
data: function () {
return {
msg: '这是组件A的消息',
}
},
methods: {
change: function () {
hub.$emit('aevent', '来自A修改的消息')
}
},
}为子组件A添加绑定一个事件,当点击按钮时,触发自定义方法change。在change方法中通过触发事件
aevent
并将值传递过去来修改接下来子组件B的信息定义子组件B
1
2
3
4
5
6
7
8
9
10
11
12
13var sonb = {
template: `<div>组件B---{{msg}}</div>`,
data: function () {
return {
msg: '这是组件B的消息',
}
},
mounted: function () {
hub.$on('aevent', (val) => {
this.msg = val
})
},
}在子组件B中通过
mounted
监听aevent
事件的触发。从而修改子组件B的值。
props 验证
组件的 props
就是组件的参数,为了确保传入的数据在可控的合理范围内,我们需要对传入的 props
的值类型进行必要的验证
1 | Vue.component('my-component', { |
非prop 特性
一个非 prop
特性是指传向一个组件,但是该组件并没有相应 prop
定义的特性,这些 props
会被自动添加到组件的根元素上
组件的双向数据绑定
v-model
为组件定义v-model
属性实现双向数据绑定,但并不推荐这种方法。因为这种方式进行的双向数据绑定并不明确到底绑定的是什么数据。
定义组件
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
28Vue.component('xk-radio', {
props: ['value', 'checkedValue'],
model: {
// 要想实现双绑定的属性,比如 checkedValue
prop: 'checkedValue',
// 触发 prop 修改的事件
event: 'click'
},
template: `
<div
:class="{
'kkb-radio': true,
'checked': value === checkedValue
}"
@click="check"
>
{{value}}
</div>
`,
methods: {
check() {
// 触发一个事件,通过事件去调用父级绑定的函数
this.$emit('click', this.value);
// 这里click与 model设置的event是一致
// 会自动的把 this.value 更新到 model 中 props 指定的 属性上
}
}
});接下来调用组件
1
2<xk-radio value="javascript" v-model="val"></xk-radio>
<xk-radio value="css" v-model="val"></xk-radio>
.sync
.sync
修饰符同样可以做到双向数据绑定,相比于v-model
更能直观看到到底绑定的是什么数据。
定义子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Vue.component('xk-radio', {
props: ['value', 'checkedValue'],
data() {
return {
}
},
template: `
<div
:class="{
'xk-radio': true,
'checked': value === checkedValue
}"
@click="check"
>
{{value}}
</div>
`,
methods: {
check() {
this.$emit('update:checkedValue', this.value);
}
}
});为组件元素添加点击事件,点击时触发
check()
方法。在这方法中在通过$emit
触发事件实现更新。这里事件名称要使用
update
加上prop
名称 的格式。第二个参数即要更新的值。组件调用
1
<xk-radio value="css" :checked-value.sync="val"></xk-radio>
插槽
插槽即组件中传递的值,会自动填充到组件中定义的<slot></slot>
位置。其中组件的<slot></slot>
位置也可以传入值,其代表默认内容,也就是没有传值时的内容。
1 | <body> |
具名插槽
具名插槽即为插槽定义name
属性,当填充时通过name
属性确定填充的位置。
组件定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
})组件调用
组件调用填充插槽必须通过
template
标签。在template
标签上定义属性表示将整个template
里的内容填充到该名称的插槽内。1
2
3
4
5
6
7
8
9
10
11
12
13
14<base-layout>
<template>
<p v-slot="header">标题</p> <!-- 只有这个标签会填充到名称为header插槽内-->
<p>哈哈哈</p> <!-- 会填充到默认插槽内-->
</template>
<template>
<p>主要内容</p>
</template>
<template v-slot="footer">
<!-- 此标签下的内容会全部填充到名称为footer插槽内-->
<p>尾部</p>
<p>尾部哈哈哈哈哈</p> <!-- 会填充到默认插槽内-->
</template>
</base-layout>
作用域插槽
对比前面两种插槽,也可以叫它带数据的插槽。作用域插槽要求在slot
上面绑定数据。
定义组件
1
2
3
4
5
6
7
8
9
10Vue.component('fruit-list', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
});为插槽绑定了以名为
info
值为item
的数据。调用时获取到数据
1
2
3
4
5
6
7<fruit-list :list='list'>
<template v-slot='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
<span v-else>{{slotProps.info.name}}</span>
<span>{{slotProps}}</span>
</template>
</fruit-list>此时可以在
template
标签上通过v-slot
命令定义一个变量名,这个变量名的值为定义组件时传过来的值。
其他
非 prop 特性
一个非 prop
特性是指传向一个组件,但是该组件并没有相应 prop
定义的特性,这些 props
会被自动添加到组件的根元素上
替换/合并已有的特性
默认情况下,非prop
特性的属性会覆盖组件根元素上同名的内容,但是针对 style
和 class
有特殊的处理,它们会合并(同名样式还是会覆盖)
禁用特性继承
如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false
,我们可以通过组件的 this.$attrs
来获取这些属性
注意
inheritAttrs: false
选项不会影响style
和class
的绑定