组件
一、组件注册方式
- 组件名为驼峰式(comName)定义时,可以通过 com-name 的形式引用(不区分大小写)
- 直接在页面中引用时只能用 kebab-case 的组件名引用(com-name)
一)全局注册
Vue.component('component-name', {})
二)局部注册
new Vue({
components: {
'component-a': ComponentA, // component-a 为使用时的组件名,ComponentA为要引入的组件的名字
}
})
三)模块化
// 定义
<template></template>
export default {}
// 引入
import ComponentA from './ComponentA.vue'
export default {
components: {
ComponentA
},
}
二、组件内容
一)注册参数
Vue.component('component-name', {
template: '<div></div>', // 组件内容, 一个组件只能有一个根元素
components: {}, // 组件可以引用其他组件
props: ['tit'], // 组件通过props接收父级传值, 在组件中可以直接通过this.tit取到tit的值
data() {
return {}; // 组件的数据需要通过一个函数来返回一个对象,避免组件复用时数据共享
},
methods, // 事件注册,监听器,计算属性,钩子函数与vue实例相同
})
二)监听子组件事件
给组件绑定一个myClick事件,这个事件名是可以自定义的
<div id="wrapper">
<hello @myClick="addSize"></hello>
</div>
父级中定义对应事件处理函数,子组件中通过$emit(),参数传入事件名(不是处理函数名)来触发
new Vue({
el: '#wrapper',
methods: {
addSize() {console.log(1)}
}
})
在组件内部绑定事件,通过$emit()来触发父级绑定到子组件上的对应事件,一参为要触发的事件名,其后的参数会作为父级绑定事件的参数
Vue.component('hello', {
template: `<div @click='$emit("myClick",1,2,3)'>hello</div>` // 通过点击该元素来触发父级的myClick事件
})
三)使用v-model
v-model的作用实际是给元素绑定一个名为
value
的属性,然后监听该元素的input
事件,在触发input
事件时修改绑定的值
<input v-model='myAge'>
<!-- 等价于 -->
<input :value='myAge' @input='myAge = $enent.target.value'>
<!-- $event是事件对象 -->
要在组件中使用v-model则需要模拟上述过程即可
- 通过props接收一个名为
value
的属性 - 给组件内的input绑定
value
属性,属性值为value
- 给组件内的input绑定
input
事件处理函数,主动触发父级的input
事件
<hello v-model='myAge'><hello>
Vue.component('hello', {
template:
`<div>
<input :value='value' @input='$emit('input', $event.target.value)'>
</div>`,
props: ['value']
})
三、Prop类型
一)只定义属性名
Vue.component('hello', {
props: ['value']
})
二)定义类型,如果类型不对会打印警告,但是不影响运行
Vue.component('hello', {
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
})
三)带验证的props对象,验证失败会打印警告,验证是在组件实例创建前进行的
type的可选值
type:[String,Number,Boolean,Array,Object,Date,Function,Symbol]
type也可以是一个自定义的构造函数,检验时通过instanceof
来判断
function Person() {}
Vue.component('hello', {
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
}
},
propG: {
type: Person // propG instanceof Person
}
}
})
四)未定义prop的属性值
如果组件上绑定数据,但是组件内没有对应的prop来接收,这个属性会被绑定到该组件的根元素上
除了class
和style
之外,父级传递给组件的属性都会替换组件内定义的属性
Vue.component('hello', {
template:
`<div>
<div>hello</div>
</div>`,
})
<div id="wrapper">
<hello :noProp="39"></hello>
</div>
<!-- 编译后 -->
<div id="wrapper">
<div noProp='39'>
<div>hello</div>
</div>
</div>
五)禁用prop继承
在组件中设置
inheritAttrs
为false
就能让组件根元素的属性不被传值覆盖
class
和style
不受此属性影响
Vue.component('my-component', {
inheritAttrs: false,
})
四、自定义事件
一)触发事件
事件名不存在自动化和大小写转换,要触发的事件名需要与定义的事件名完全一致
<my-component @myEvent="doSomething"></my-component>
this.$emit('myEvent')
二)修改v-model默认行为
v-model
默认会利用名为value
的prop
和名为input
的事件,可以用model选项修改
Vue.component('base-checkbox', {
model: {
prop: 'checked', // 将value属性修改为checked属性
event: 'change' // 将input事件修改为change事件
},
props: {
checked: Boolean // 任需要定义对应的prop
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
三)监听原生事件
想要直接在组件根元素上监听原生事件需要使用.native修饰符
<base-input v-on:focus.native="onFocus"></base-input>
五、插槽
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
v-slot:header
可简写为#header
一)后备内容
- 组件中如果没有
slot
标签则父级写在组件标签内的元素都会被抛弃 - 在组件的
slot
标签内写入的内容会在父级没有在组件标签内传入内容时展示
Vue.component('hello', {
template: `
<div>
hello <slot>normal Text</slot>
</div>
`
})
<hello></hello>
<!-- 展示为 -->
<div>
hello normal Text
</div>
<hello>is new Text</hello>
<!-- 展示为 -->
<div>
hello is new Text
</div>
二)具名插槽
一个不带name的
slot
出口会带有隐含的名字 ‘default’
任何没有被包裹在带有 v-slot 的 中的内容都会被视为默认插槽的内容
// 定义组件时给slot标签一个name属性,引入时传入对应的属性
Vue.component('hello', {
template:
`<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`
})
<base-layout>
<!-- 传入属性即可 -->
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<!-- 等价于
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
-->
<template #:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
三)作用域插槽
插槽总是在父级组件中使用的,这时的作用域是在父级中,插槽拿不到组件内的数据
这时如果需要调用组件里的数据就要用到作用域插槽,在组件内把属性值赋给插槽
// 定义组件时给slot标签一个name属性,引入时传入对应的属性
Vue.component('hello', {
template:
`<div class="container">
<slot name="header" :uName='innerName' :uAge='innerAge'></slot>
</div>`,
data() {
return {
innerName:'zhangkb',
innerAge: 099
}
}
}
<hello>
<!-- msg就是接收到的组件内传给插槽的数据对象 -->
<template v-slot:header='{msg}'>
<h1>get slot data: {{mag.uName + msg.uAge}}</h1>
</template>
</hello>
<!-- 展示为 -->
<h1>get slot data: zhangkb099</h1>