props / $emit
父组件可以通过 props
的方式给子组件传递数据,而子组件可以通过 $emit
的方式向父组件通信。
父传子 - props
props 有两种常见的写法:
- 字符串数组:数组中的字符串就是 attribute 的名称;
- 对象类型:对象类型可以在指定 attribute 名称的同时,还可以指定它传递的数据类型、是否是必须的以及默认值等等;
字符串数组
1 2 3 4 5 6 7 8 9 10 11
| <div id="app"> <cpn :cgame="games" :cmsg="msg"></cpn> </div> <template id="cpn"> <div> <ul> <li v-for="item in cgame">{{item}}</li> </ul> <div>{{cmsg}}</div> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| var app = new Vue({ el: '#app', data: { msg: '父传子', games: ['tlbb', 'yxlm', 'cyhx'] }, components: { cpn: { template: '#cpn', props: ['cgame', 'cmsg'] } } })
|
除此之外,如果父组件接收是一个对象,还有如下简便写法:
父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <hello-world v-bind="messageObj"></hello-world> </div> </template>
<script> import HelloWorld from "./components/HelloWorld.vue"; export default { components: { HelloWorld }, data() { return { messageObj: { title: "标题", content: "内容", }, }; }, }; </script>
|
子组件:
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <div> <h2>{{title}}</h2> <h3>{{content}}</h3> </div> </template>
<script> export default { props: ["title", "content"], }; </script>
|
该写法前提是参数要一一对应,这种就类似于解构写法。
对象类型
当使用对象类型时,可以对传入的数据有更多的限制:
- 指定传入的数据类型;
- 指定数据是否必传;
- 指定数据的默认值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> export default { props: { title: { type: String, required: true, default: "默认值", }, content: { type: String, }, }, }; </script>
|
注意:type
的类型可以有如下几个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
对象类型有如下几种写法:
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 39 40 41 42 43
| <script> export default { props: { // 单个类型 propA: Number, // 多个类型 propB: [String, Number], // 必填 propC: { type: String, required: true, }, // 有默认值 propD: { type: String, default: "默认值", }, // 有默认值的对象 propE: { type: Object, default() { return { message: "默认值对象写法", }; }, }, // 自定义验证函数 propF: { validator(value) { // 值必须匹配字符串中的一个 return ["1", "2", "3"].includes(value); }, }, // 有默认值的函数 propG: { type: Function, default() { return "函数默认值"; }, }, }, }; </script>
|
非prop的attribute
当我们传递给组件某个属性时,但该属性并没有定义对应的 props 或者 emits 时,就称之为 非prop的attribute。
常见的包括 class、id、style 等等。
当组件有单个根节点的时候,非 prop 的 attribute 将自动添加到根节点的 attribute 上。
当我们不希望组件的根元素继承 attribute,我们可以在组件中设置 inheritAttrs:false
,我们可以通过 $attrs
来访问所有的非props的attribute。
1 2 3
| <template> <div :class="$attrs.class"></div> </template>
|
当有多个非props的attribute的时,也可以这样:
1 2 3
| <template> <div v-bind="$attrs"></div> </template>
|
当有多个根节点的 attribute 如果没有显式的绑定,那么会报警告,这时需要我们去手动的指定要绑定到哪一个属性上去:
1 2 3 4 5
| <template> <div :class="$attrs.class">1</div> <div>2</div> <div>3</div> </template>
|
子传父 - $emit
自定义事件:$emit("自定义事件名",自定义事件传递的参数)
。
1 2 3 4 5 6 7 8 9
| <div id="app"> <cpn @fclick="dclick"></cpn> {{msg}} </div> <template id="cpn"> <div> <div v-for="item in main" @click="itemClick(item)">{{item}}</div> </div> </template>
|
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
| var app = new Vue({ el: '#app', data: { msg: 'hehe' }, methods: { dclick(item) { console.log(item); this.msg = item } }, components: { cpn: { template: '#cpn', data() { return { main: [ { id: 0, name: 'zww' }, { id: 1, name: 'lq' } ] } }, methods: { itemClick(item) { this.$emit('fclick', item) } }, } } })
|
parent / children
访问子实例 - $children
1 2 3 4 5 6 7
| <div id="app"> <cpn></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>子组件</div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var app = new Vue({ el: '#app', methods: { btnClick() { this.$children[0].showSon() } }, components: { cpn: { template: '#cpn', data() { return { msg: '子组件' } }, methods: { showSon() { console.log("hh"); } }, } } })
|
访问父实例 - $parent
1 2 3 4 5 6 7 8 9
| <div id="app"> <cpn></cpn> </div> <template id="cpn"> <div> <span>子组件</span> <button @click="btnclick">按钮</button> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var app = new Vue({ el: '#app', data: { msg: '父组件内容' }, components: { cpn: { template: '#cpn', methods: { btnclick() { console.log(this.$parent.msg); } }, } } })
|
ref / refs
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
1 2 3 4 5 6 7
| <div id="app"> <cpn ref="a"></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>子组件</div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var app = new Vue({ el: '#app', methods: { btnClick() { this.$refs.a.showSon() } }, components: { cpn: { template: '#cpn', data() { return { msg: '子组件' } }, methods: { showSon() { console.log("hh"); } }, } } })
|
$root
vm.$root
可以获取当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
1 2 3 4 5 6 7 8
| export default { data() { return { msg: "hello" }; }, };
|
1 2 3 4 5 6
| export default { created() { console.log(this.$root.$children[0].msg); }, };
|
因此,子组件可以通过 $root
属性访问父组件实例的属性和方法。
EventBus
EventBus 又称为事件总线。在 Vue 中可以使用 EventBus 来作为沟通的桥梁,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件。
可以通过创建 event-bus.js
文件,也可也在 main.js
文件中初始化 EventBus。
1 2 3 4 5 6 7
| import Vue from 'vue'; let eventBus = new Vue(); export default eventBus;
Vue.prototype.$eventBus = new Vue()
|
现在假设有 A、B 两个组件。
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
| <template> <div> <input type="text" v-model="title" > <button @click="add">add</button> </div> </template> <script> import eventBus from "./event-bus"; export default { name: "A", data() { return { title: "", }; }, methods: { add() { eventBus.$emit("addTitle", this.title); }, }, }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import eventBus from "./event-bus"; export default { name: "B", mounted() { eventBus.$on("addTitle", this.handleAddTitle); }, methods: { handleAddTitle(title) { console.log(title); }, }, beforeDestroy() { eventBus.$off("addTitle", this.handleAddTitle); }, }; </script>
|
这样 A、B 两个组件之间就可以通信了。
1 2 3 4 5 6 7 8 9 10
| eventBus.$emit()
eventBus.$on()
eventBus.$off('事件名', callback) eventBus.$off('事件名') eventBus.$off()
|
provide / inject
它们需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
基本使用:
1 2 3 4 5 6 7 8
| export default { provide() { return { name: "LqZww", }; }, };
|
1 2 3 4 5 6 7 8
| <template> <div>{{name}}</div> </template>
export default { inject: ["name"], };
|
provide 和 inject 主要在开发高阶插件 / 组件库时使用。并不推荐用于普通应用程序代码中。
attrs / listeners
vm.$attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
vm.$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
比如,现在有3个嵌套组件,A -> B,B -> C。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div> <B :name="name" :age="age" @add="add"></B> </div> </template> <script> import B from "./B"; export default { components: { B }, data() { return { name: "zww", age: 18, }; }, methods: { add() { console.log("add"); }, }, }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <div> <C v-bind="$attrs" v-on="$listeners"></C> </div> </template> <script> import C from "./C"; export default { components: { C }, }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> {{name}} - {{age}} <button @click="myadd">add</button> </div> </template> <script> export default { props: ["name", "age"], methods: { myadd() { this.$emit("add"); console.log(this.name); }, }, }; </script>
|
Vuex
Vuex官网