ref/$parent/$children
基于当前上下文访问父组件或全部子组件:
ref:给元素或组件注册引用信息;$parent/$children:访问父 / 子实例。
这两种方法的弊端是,无法在跨级或兄弟间通信,比如下面的结构:
// parent.vue<component-a></component-a><component-b></component-b><component-b></component-b>
<template># 使用 ref 引用<component-a ref="comA"></component-a></template><script>export default {mounted () {const comA = this.$refs.comA;console.log(comA.title); // Vue.jscomA.sayHello(); // 弹窗}}</script>
provide / inject
允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
// parent.vueexport default {provide: {name: 'Aresn'}}// child.vueexport default {inject: ['name'],mounted () {console.log(this.name); // Aresn}}
所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 Aresn。
===》 解决方式:
之前实习遇到的一个需求场景,下拉选择框 选择了项目就更新下面的tab;但是tab是很深很深。不仅仅是一层;
当时就用到了 provide 和 inject;
但是 provide提供的属性改变了 inject不会改变,即不会触发自身的修改
vue 路由参数变化,页面不刷新,provide /inject 完美解决方案
// 祖先<template><div class="os_container"><navbar></navbar># isRouterAlive改变了之后 就会让下面的页面重新渲染# 所以定义一个方法reload 修改isRouterAlive(不是真修改,只是通过这个变量触发组件重新渲染====》 先让他false 然后在挂载之前又更新回来 this.$nextTick问题:怎么把这个方法 传入给 更深的子组件?使得子组件可以调用这个reload;当时调用这个方法的子组件是 放在nav里的一个 下拉框; 所以正好是 隔代关系;<router-view v-if="isRouterAlive"></router-view></div></template><script>import navbar from '@/components/navbar'export default {name:'mainframe',provide(){return {reload:this.reload}},data () {return {isRouterAlive:true,}},components:{navbar,},methods:{reload(){this.isRouterAlive = false//在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOMthis.$nextTick(()=>{this.isRouterAlive = true})}}}</script>// nav-input.vue<script>export default {name: 'newproduct',inject:['reload'],method:{selectChange(){this.reload()}}</script>
原来这就是传说中的 依赖注入,控制反转
进阶,在根入口文件app.vue上挂载基本不会变的数据,比如用户的信息;给全局访问;
<template><div><router-view></router-view></div></template><script>export default {provide () {return {app: this}}data () {return {userInfo: null}},}</script>全局数据多了可以拆分为 多个js 然后 mixin进来
$on 与 $emit
<template><div><button @click="handleEmitEvent">触发自定义事件</button></div></template><script>export default {methods: {handleEmitEvent () {// 在当前组件上触发自定义事件 test,并传值this.$emit('test', 'Hello Vue.js')}},mounted () {// 监听自定义事件 testthis.$on('test', (text) => {window.alert(text);});}}</script>
自行实现dispatch / broadcast
功能:
事件触发与监听
===》 找到组件,让组件调用相关方法,并传入参数
要实现的 dispatch 和 broadcast 方法,将具有以下功能:
- 在子组件调用 dispatch 方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过
$on监听了这个事件; - 相反,在父组件调用 broadcast 方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过
$on监听了这个事件。
如何实现?
实现这对方法的关键点在于准确地找到组件实例。那在寻找组件实例上,我们的“惯用伎俩”就是通过遍历来匹配组件的 name 选项,在独立组件(库)里,每个组件的 name 值应当是唯一的,name 主要用于递归组件
function broadcast(componentName, eventName, params) {this.$children.forEach(child => {const name = child.$options.name;if (name === componentName) {child.$emit.apply(child, [eventName].concat(params));} else {broadcast.apply(child, [componentName, eventName].concat([params]));}});}export default {methods: {// 组件name,自定义事件,要传递的数据dispatch(componentName, eventName, params) {let parent = this.$parent || this.$root;let name = parent.$options.name;// 向上找while (parent && (!name || name !== componentName)) {parent = parent.$parent;if (parent) {name = parent.$options.name;}}if (parent) {parent.$emit.apply(parent, [eventName].concat(params));}},broadcast(componentName, eventName, params) {broadcast.call(this, componentName, eventName, params);}}};
使用
<!-- A.vue --><template><button @click="handleClick">触发事件</button></template><script>import Emitter from '../mixins/emitter.js';export default {name: 'componentA',mixins: [ Emitter ],methods: {handleClick () {this.broadcast('componentB', 'on-message', 'Hello Vue.js');}}}</script>// B.vueexport default {name: 'componentB',created () {this.$on('on-message', this.showMessage);},methods: {showMessage (text) {window.alert(text);}}}
实现的缺陷:
- 需要额外传入组件的 name 作为第一个参数;
- 无冒泡机制;
- 第三个参数传递的数据,只能是一个(较多时可以传入一个对象),而 Vue.js 1.x 可以传入多个参数,当然,你对 emitter.js 稍作修改,也能支持传入多个参数,只是一般场景传入一个对象足以。
实现findComponents
可以说是组件通信的终极方案。findComponents 系列方法最终都是返回组件的实例,进而可以读取或调用该组件的数据和方法。
它适用于以下场景:
- 由一个组件,向上找到最近的指定组件;
- 由一个组件,向上找到所有的指定组件;
- 由一个组件,向下找到最近的指定组件;
- 由一个组件,向下找到所有指定的组件;
- 由一个组件,找到指定组件的兄弟组件。
5 个不同的场景,对应 5 个不同的函数,实现原理也大同小异。
5 个函数的原理,都是通过递归、遍历,找到指定组件的 name 选项匹配的组件实例并返回。

