5、VUE中的组件
组件
组件就是可复用的单元模块。
组件的data部分是一个函数,返回值是一个对象,在这个对象中传递要传递的值。
Vue.component('组件名称', {组件选项})- 组件名称遵循自定义组件命名规范:全小写、连字符(虽然驼峰式一般也没问题)
- 组件选项与
new Vue选项配置基本一致(也有一些细节的不同)
全局组件通过Vue对象进行创建,局部组件通过在组件的内部属性components创建。
new Vue({
...,
components: {
'组件名称': {组件选项}
}
})data
在非 new Vue 的组件中,data 必须为函数,函数返回值必须是一个对象,作为组件的最终 data
components: {
'xk-circle': {
data() {
return {pi: 3.14}
},
}
}props
组件中内部私有数据存储中组件 data 中,通过外部传入的数据,则通过 props 选项接收
- 如果传入的
props值为一个表达式,则必须使用v-bind - 组件中的
data和props数据都可以通过组件实例进行直接访问 data中的key与props中的key不能冲突
组件间的通信
父组件向子组件传值
父组件向子组件传值使用props即可。
子组件通过props属性接收外部传来的值(即父组件传来的值)
Vue.component('menu-item', {
props: ['title'],
data: function () {
return {
msg: '子组件'
}
},
template: '<div>{{msg}}---{{title}}</div>'
})
var vm = new Vue({
el: '#app',
data: {
pmsg: "父组件",
ptitle: '来自父组件的值'
}
});传值通过标签属性传值:
<!-- 不使用v-bind绑定,则传递字符串 -->
<menu-item title='来自父组件的值123'></menu-item>
<!-- 使用v-bind绑定,则传递表达式 -->
<menu-item :title='ptitle'></menu-item>
<!-- 不传递任何值 -->
<menu-item></menu-item>子组件向父组件传值
子组件无法直接向父组件传值(修改父组件的值),因为props是单向数据流。因此父组件向子组件传值需要通过事件触发同值父组件,父组件定义事件来修改值。
定义子组件
Vue.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
调用组件
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>为组件绑定自定义事件事件,当事件发生时触发父级修改值的函数,并将事件对象传入
在父级中定义修改值的函数
handlevar vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', parr: ['apple', 'orange', 'banana'], fontSize: 10 }, methods: { handle: function (val) { // 扩大字体大小 this.fontSize += val; } } });
组件间传值
开辟一个新的VUE实例
var hub = new Vue()定义子组件A
var 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
var 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 的值类型进行必要的验证
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
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 ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})非prop 特性
一个非 prop 特性是指传向一个组件,但是该组件并没有相应 prop 定义的特性,这些 props 会被自动添加到组件的根元素上
组件的双向数据绑定
v-model
为组件定义v-model属性实现双向数据绑定,但并不推荐这种方法。因为这种方式进行的双向数据绑定并不明确到底绑定的是什么数据。
定义组件
Vue.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 指定的 属性上 } } });接下来调用组件
<xk-radio value="javascript" v-model="val"></xk-radio> <xk-radio value="css" v-model="val"></xk-radio>
.sync
.sync修饰符同样可以做到双向数据绑定,相比于v-model更能直观看到到底绑定的是什么数据。
定义子组件
Vue.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名称 的格式。第二个参数即要更新的值。组件调用
<xk-radio value="css" :checked-value.sync="val"></xk-radio>
插槽
插槽即组件中传递的值,会自动填充到组件中定义的<slot></slot>位置。其中组件的<slot></slot>位置也可以传入值,其代表默认内容,也就是没有传值时的内容。
<body>
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
Vue.component('alert-box', {
template: `
<div>
<strong>Error:<strong>
<slot>默认内容</slot>
</div>
`
})
var vm = new Vue({
el: '#app',
});
</script>
</body>具名插槽
具名插槽即为插槽定义name属性,当填充时通过name属性确定填充的位置。
组件定义
Vue.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里的内容填充到该名称的插槽内。<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上面绑定数据。
定义组件
Vue.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的数据。调用时获取到数据
<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的绑定













