组件协调与通信
获取子组件对象
在模板中使用 ref 属性进行标识,可以通过这个标识获取对应的 DOM 或 vc对象。
<User ref="userVC"></User>
<divr ref="divDOM"></div>获取子组件、模板元素,使用特定的api即可:
子组件 | DOM元素 = this.$refs.ref属性值
// this 是父组件
// 如:
let subVC = this.$refs.userVC
let div = this.$refs.divDOM
// 获取子组件后,仍可以继续调用,获得孙子组件 | 孙子DOM
let subsubDIV = subVC.$refs.name混入 mixins
混入 mixins:一个配置项,用以实现配置级别的复用。
如:不同组件的methods配置项中有着相同的方法(代码完全一样,可以直接cp无需更改的),就可以才有mixins配置项实现复用。
混入的实现步骤:
提取共有配置:通常定义一个 mixin.js(和main.js同级),用以存放所有的共有配置
// mixin.js // 单独导出配置对象,和vue的配置一样 export const mix1 = { methods: { printInfo(){ console.log(this.name, ',' , this.age) } } } export const mix2 = { methods: { a(){ console.log('mixin.js a.....') } } } export const mix3 = { mounted() { console.log('mixin.js mounted...') } }引入并使用
import {mix1} from '../mixin.js' import {mix2} from '../mixin.js' import {mix3} from '../mixin.js' /* mix1 = { methods: { printInfo(){ console.log(this.name, ',' , this.age) } } } ... */ export default { name : 'User', mounted() { console.log('User mounted...') }, data() { return { msg : '用户信息', name : '张三2', age : 20 } }, // 配置混入的配置对象,会自动合并配置项 mixins : [mix1,mix2, mix3], methods: { a(){ console.log('user a....') } } }- 最终配置 = 用户配置 + 混入配置。
- 除生命周期钩子外,当混入配置与用户配置冲突时,保留用户配置。这也是“混入”的含义,即不破坏。
- 生命周期钩子的混入会采用叠加的方式:先执行混入的钩子,在执行用户的钩子。
混入支持局部混入(mixins配置项,混入当前vc),也支持全局混入(混入所有vc):
import {mix1} from './mixin.js'
import {mix2} from './mixin.js'
import {mix3} from './mixin.js'
// 全局混入,必须在创建vm之前
Vue.mixin(mix1)
Vue.mixin(mix2)
Vue.mixin(mix3)
new Vue({
el : '#app',
render : h => h(App)
})插件
插件,是vue提供的用来实现功能增强的一种机制。
定义插件对象并导出。通常定义在 plugis.js 文件中
// 每一个插件都是一个对象 export const p1 = { // 每一个插件对象中必须有一个install方法 // 这个install方法会被自动调用 // install方法上的参数:包括两部分 // 第一部分:Vue构造函数 // 第二部分:可以接收用户在使用这个插件时传过来的数据,参数个数无限制。 install(Vue, a, b, c, d){ console.log('这个插件正在显示一个可爱的封面....') // 通过Vue的原型对象扩展的属性,通过vm和vc都可以访问。借此实现api的拓展,达到功能增强 Vue.prototype.counter = 1000 } }使用插件
// 导入插件 import {p1} from './plugins.js' // 插件的使用通常放在创建Vue实例之前 // 插上插件。(删除就是拔下插件) // p1:插件对象 // 1,2,3,4:使用者传参 Vue.use(p1, 1,2,3,4) // 创建Vue实例 new Vue({ ... }
局部样式
默认情况下,在 vue 组件中定义的样式最终会汇总到一块,如果样式名一致,会导致冲突,冲突发生后,以后来加载的组件样式为准。
局部样式:样式只作用于当前vc。
<style scoped>
/*加个scoped即可开启局部样式*/
.s {
background-color:bisque
}
</style>- 根组件App一般不建议设置为局部样式
vue 组件的 style 样式支持多种样式语言,例如:css、less、sass 等,也可以进行指定:
<style scoped lang="less">
/*加个scoped即可开启局部样式*/
.s {
background-color:bisque,
.s1 {
color:red
}
}
</style>- 使用 less 前需安装 less-loader:
npm i less-loader。
组件通信
组件化开发极大提高了代码复用,组件间的数据传递进一步方便了组件功能的复用。
组件间的通信方式总结:
- props:可以完成父向子传数据;父向子传一个函数,则可以完成子向父传数据
- 组件自定义事件:可以完成子向父传数据。
- 全局事件总线:任意两个组件间通信
- 消息订阅与发步:任意两个组件间通信
props 配置
组件以类HTML标签的形式使用,而props则以HTML标签属性赋值的形式完成数据传递。
<component prop1="value1" prop2="value2" ...></component>- 一般用于父组件给子组件“传参”
- 接收的prop可以是函数,即可以调用父组件的函数作为回调(子向父传数据)
在子组件中需要提供props配置项以接收prop,有三种接收方式:
简单接收:只提供属性名(参数名)即可
props : ['name','age','sex']接收时添加类型限制
props : { name : String, age : Number, sex : String }接收时添加类型限制、必要性限制、默认值
props : { name : { type : Number, // 类型限制 required : true // 必要性设置 }, age : { type : Number, default : 10 // 默认值 }, sex : { type : String, default : '男' } }
如:
<User name=”jack” age=”20” sex=”男”></User>注意事项:
- 不要乱接收,接收的一定是其它组件提供的。
- props 接收到的数据不能修改。
- 修改之后,虽然页面会刷新,会报错
- 可以找个中间变量来解决
组件自定义事件
Vue 除了支持内置事件,也支持给组件添加自定义事件。有两种方式;
- 使用指令绑定事件
- 使用代码绑定事件
自定义事件像是组件提供给外部的一个数据接收窗口,窗口名即事件名,自定义事件触发时窗口向外传递数据(
$emit(...)),接不接受就是外部(父组件)的事了。父组件如果需要这个数据,则给子组件注册相应的事件,绑定handler即可。
指令绑定
v-on:事件名="..." 指令同样适用于自定义事件,底层实现是一样的(注册监听器、触发事件)。
<Car @event1=”doSome”></Car>给组件car绑定(注册)event1事件,发送时执行dosome方法:
// 父组件
methods : {
// doSome(name, age, gender){}
// 或者可以这样
doSome(name, ...parameters){} // ...parameters 表示采用一个数组接收参数
}子组件要给出事件的触发逻辑:
methods : {
triggerEvent1(){
// 触发事件并且给事件传数据
// 参数:事件名+参数
this.$emit('event1', this.name, this.age, this.gender)
}
}- 通过内置事件的触发,执行这段触发逻辑,即可传递事件参数给父组件,执行父组件的方法。
- 子内置事件触发 --> 执行自定义事件触发逻辑 --> 触发自定义事件,传递参数 --> 父组件接收参数 --> 父组件执行事件处理函数 --> 实现子向父传递数据。
自定义事件同样支持修饰符:
<Car @event1.once="doSome"></Car> 表示只触发一次。
<Car @click.native="doSome"></Car> 使原生事件生效。代码绑定
通过 ref 获取子组件:
<Car ref=”car”></Car>获取子组件并绑定事件:
let vc = this.$refs.car;
// 参数:事件名,handler
vc.$on('event1', this.doSome)
vc.$once('event1', this.doSome) //表示只触发一次
// 这种方式更加灵活。例如:希望 AJAX 请求响应回来数据之后再给组件绑定事件。绑定handler时需要注意:
vc.$on('event1', function(){
//这里的 this 是子组件实例,即vc自己
})
vc.$on('event1', ()=>{
// 这里的 this 是父组件实例,即vc的父组件,如App
})解绑事件
vm 和 vc 销毁的时候,所有组件以及子组件当中的事件会全部解绑。
也可以手动解绑:
vc.$off('event1') // 这种方式只能解绑一个事件。
vc.$off(['event1', 'event2']) // 这种方式解绑多个事件。
vc.$off() // 解绑所有事件。
// 哪个vc绑定了(执行了$on),就哪个vc执行 $off全局事件总线
自定义事件可以实现两个组件之间通信:
A组件 -> B组件
- A组件提供事件触发的逻辑,同时给事件处理传递参数*(事件触发方)*。
- B组件负责事件的处理,并接受传递的参数*(事件处理方)*。
可见,事件方式传递数据有三个角色:
- 事件:提供数据传递的窗口
- 事件触发方:负责触发事件,发送数据
- 事件处理方:负责绑定事件,处理事件(处理时可以接收数据)

在父子组件中,自定义事件常见于子组件向父组件传值。本质上,仍是事件触发方给事件处理方传值。
全局事件总线就是扁平化的事件触发方,这个促发方提供了多个自定义事件的触发逻辑,并且可以在vm、vc上访问到,即任意的vm、vc都可以作为事件处理方来接收数据。
- 全局事件总线就是一个共享的vc对象。可以绑定多个事件,通过绑定handler来实现数据的接收。
- 全局事件总线把事件间的数据传递跳脱出父子组件的范畴,兄弟之间、祖孙之间都可以通过这种方式传递数据。

全局事件总线的实现:
- 通常把全局事件总线记作
$bus。
- 手动创建vc并绑定至原型对象
// main.js
// 获取 VueComponent 构造函数
const VueComponentConstructor = Vue.extend({})
// 创建 vc
const vc = new VueComponentConstructor()
// 让所有的 vc 都能够使用这个 vc
Vue.prototype.$bus = vc
new Vue({
...
})- 利用vm,将vm绑定至原型对象(建议)
// main.js
new Vue({
el : '#app',
render : h => h(App),
// 利用生命周期钩子
beforeCreate(){
Vue.prototype.$bus = this
}
})全局事件总线的使用:
// 数据发送方:触发事件
methods : {
triggerEvent(){
this.$bus.$emit('eventx', 传数据)
}
}
// 数据接收方:绑定事件,提供handler
mounted(){
this.$bus.$on('eventx', handler)
}
// 养成好习惯:组件实例被销毁前,将绑定在$bus 上的事件解绑。
beforeDestroy(){
this.$bus.off('eventx')
}消息订阅与发布
和自定义事件(全局事件总线)类似,消息的订阅与发布也可以时间两组件间的通信,通常需要借助第三方库,如 pubsub-js。

使用:
安装
pubsub-js:npm i pubsub-js。引入
pubsub对象:import pubsub from 'pubsub-js'。通过
pubsub对象完成订阅与发布:// A组件:发布消息——发送数据 pubsub.publish('messageName', 'zhangsan', 20) // B组件:订阅消息——接收数据 mounted(){ this.pubsubId = pubsub.subscribe('messageName', (messageName, data) => { // 两个参数:第一个是消息的名字。第二个参数是消息发布时传过来的数据。 // 要使用箭头函数。这样才能保证 this 是当前vc。 }) } beforeDestroy(){ // 取消订阅,停止数据的接收 pubsub.unsubscribe(this.pubsubId) }
