003.(高级)组件通信
Posted Ruovan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了003.(高级)组件通信相关的知识,希望对你有一定的参考价值。
01. 祖先与后代
-
通过
provide
和inject
可以进行跨级组件通信只能祖先给后代传值
这个注入的值,主要用于读取,不建议更改
确实需要更改,可以从祖先传一个函数,在祖先内进行更改
// 在祖先元素提供 provide(){ return { foo: '123123', app: this // 甚至可以直接返回这个祖先实例 } } // 后代元素 export default { // 在后代元素注入祖先元素提供的值 inject: ['foo'] }
02. 派发与广播
-
通过
$on
和$emit
定义两个自定义事件进行父子组件间的通信$emit
会在当前组件上触发事件,然后在父组件上用v-on
监听这个事件也可以通过
$on
在当前组件自己监听自己 -
通过向上/向下递归遍历,寻找到指定
name
的组件- 每个组件都应设置一个组件名
name
,且组件名应该是唯一的
-
在子组件可以调用
dispatch
方法- 用于向上级查找指定的组件,并触发自定义事件和传递数据
- 上级组件需要用
$on
监听这个事件
-
在父组件可以调用
broadcast
方法- 用于向下级查找指定的组件,并触发自定义事件和传递数据
- 下级组件需要用
$on
监听这个事件
// componentName--组件名,eventName--事件名,params--传递参数 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])); } }); } function 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)); } }, export default { methods: { dispatch(componentName, eventName, params) { dispatch.call(this, componentName, eventName, params); }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); } } };
- 每个组件都应设置一个组件名
03.关于mixin
混入
-
使用
mixin
混入,可以分发Vue组件中的可复用功能- 混入对象可以包含任意组件选项,如生命周期、基础数据、实例方法
- 当组件使用时,混入对象的所有选项都将被【混入】到组件中
// mixin.js文件----- let mixin = { // 写法同组件内的写法一致 data(){return {}}, created(){}, methods:{} } export default mixin // 组件中----- import mixin from './mixin' export default { // 局部混入 mixins: [mixin] } // 全局混入 Vue.mixin({ mixin })
- 同名的钩子函数(
created
、mounted
),将合并为一个数组,且混入对象的钩子将在组件钩子之前调用 - 对象选项(
methods
、components
),将被合并为同一个对象,当组件有冲突时,以组件优先
- 混入不同组件的【混入对象】,他们的方法和参数不共享
-
当我们需要全局去注入一些
methods
、filter
、hooks
时我们就可以使用mixin
来实现 -
注意:一旦使用全局混入,它将影响每一个之后创建的 Vue 实例
-
可以通过混入
mixin
将上面的派发与广播方法混入需要的组件中... export default { methods: { dispatch(componentName, eventName, params) { dispatch.call(this, componentName, eventName, params); }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); } } };
04. 组件构造器Extend
-
Vue.extend
的作用,就是基于 Vue 构造器,创建一个【类】,用于扩展实例- 它的参数跟
new Vue
的基本一样 - 但data要跟组件一样,是个函数
- 再配合
$mount
,就可以渲染组件,并且挂载到任意指定的节点上
extend创建的是一个组件构造器,而不是一个具体的组件实例
可以理解为构造了一个和vue组件内部有一样结构的对象
最终还是要通过
Vue.components
注册才可以使用let Profile = Vue.extend({ template: '', data() { return { } } }) // 方式一:创建 Profile 实例,并挂载到一个元素上 new Profile().$mount('#app') // 方式二: new Profile({ el: "#app" }) // 方式三:文档之外手动挂载 let myProfile = new Profile().$mount(); export default { mounted(){ // 因为这里是通过获取节点的方式添加到某个元素内,所以一定要在钩子函数中挂载,确保当前页面的dom节点加载完成 let app = document.getElementById('app') app.appendChild(myProfile.$el) } } // 注册组件 Vue.component('myProfile', Profile) // 或者在局部组件 export default { components: { 'myProfile': Profile } }
- 它的参数跟
05. 工具函数
-
通过定义工具函数递归、遍历,找到指定组件的
name
,并返回匹配的组件实例工具函数实现的思路,与派发与广播的思路基本一致
// assist.js // context--组件实例,一般传入this(即当前vue组件实例),components--要查找的组件实例的名称 // 由一个组件,向上找到最近的指定组件 function findComponentUpward (context, componentName) { let parent = context.$parent; let name = parent.$options.name; while (parent && (!name || [componentName].indexOf(name) < 0)) { parent = parent.$parent; if (parent) name = parent.$options.name; } return parent; } // 由一个组件,向上找到所有的指定组件 function findComponentsUpward (context, componentName) { let parents = []; const parent = context.$parent; if (parent) { if (parent.$options.name === componentName) parents.push(parent); return parents.concat(findComponentsUpward(parent, componentName)); } else { return []; } } // 由一个组件,向下找到最近的指定组件 function findComponentDownward (context, componentName) { const childrens = context.$children; let children = null; if (childrens.length) { for (const child of childrens) { const name = child.$options.name; if (name === componentName) { children = child; break; } else { children = findComponentDownward(child, componentName); if (children) break; } } } return children; } // 由一个组件,向下找到所有指定的组件 function findComponentsDownward (context, componentName) { return context.$children.reduce((components, child) => { if (child.$options.name === componentName) components.push(child); const foundChilds = findComponentsDownward(child, componentName); return components.concat(foundChilds); }, []); } // 由一个组件,找到指定组件的兄弟组件: except表示是否把本身除外 function findBrothersComponents (context, componentName, exceptMe = true) { let res = context.$parent.$children.filter(item => { return item.$options.name === componentName; }); let index = res.findIndex(item => item._uid === context._uid); if (exceptMe) res.splice(index, 1); return res; } export { findComponentUpward, findComponentsUpward, findComponentDownward, findComponentsDownward, findBrothersComponents };
以上是关于003.(高级)组件通信的主要内容,如果未能解决你的问题,请参考以下文章