【Vue】组件
组件注册方式
- 组件名为驼峰式(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 {}; // 组件的数据需要通过一个函数来返回一个对象,避免组件复用时数据共享
// 一个足迹可能会创建多个实例,如果data是一个纯粹的对象,则所有的实例会共享引用同一个数据对象
},
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 = $event.target.value" />
<!-- $event是事件对象 -->
要在组件中使用 v-model 则需要模拟上述过程即可
- 通过 props 接收一个名为
value
的属性 - 给组件内的 input 绑定
value
属性,属性值为value
- 给组件内的 input 绑定
input
事件处理函数,主动触发父级的input
事件
<hello v-model="myAge"><hello></hello></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 的<template>
中的内容都会被视为默认插槽的内容
// 定义组件时给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>