记一次Vue跨层级通信

1,562 阅读2分钟

需求

最近在做公司项目时遇到一个需求,按组件化思想说就是祖先容器中,有不确定层级深度的带有可激活性的子组件,子组件激活性唯一。类似Tab和TabContent的关系,按常理做法是直接放到Vuex中解决,参考了ElementUI和iview的跨组件通信方法(向你们致敬),其采用递归按名称查找的方法,类似dom。本需求不十分适用,因为不清楚到底嵌套了多少层级,害怕会影响性能,通过查看Vue API找到解决方法,如下

provide/inject

vue官方文档的描述

提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

思路

首先通过 provide/inject 解决跨多个层级的问题,其次传入一个可监听对象。容器组件缓存所有可激活组件的实例,并且设置激活/未激活状态,实现类似Tab和TabContent

Vue.observable

Vue 2.6新增的API 用来创建可监听的对象,以往的做法是通过创建新的Vue实例并把bus或者event这种事件代理到实例上,使用起来调试和可读性不如新的Vue.observable。利用Vue.observable可以实现一个小型的简易Vuex,正好符合当前的需求。

实现

store.js

import Vue from 'vue'
class Store {
    constructor (activated = '', cacheInstance = [] ) {
        this.store = Vue.observable({ activated, cacheInstance })
    }
    getActivated () {
        return this.store.activated
    }
    setActivated (value) {
        this.store.activated = value
    }
    getInstance () {
        ...
    }
    setInstance (ins) {
        ...
    }
}
export default Store

父容器组件(祖先)

实例化Store 并提供给provide

Wrapper.vue

<template>
    <div>
        <header></header>
        <section>
            <slot></slot>
        </section>
        <footer></footer>
    </div>
</template>
<script>
import Store from './store.js'
export default {
    name:'Wrapper',
    data () {
        const rootStore = new Store()
        return { rootStore }
    },
    provide () {
        return {rootStore: this.rootStore}
    }
}
</script>

子组件

WrapperItem.vue

子组件接收inject中的可监听对象,注意子组件在调用时,必须传入name作为标示,类似Tab和TabContent的关系

<template>
    <div @click='onItemClick'></div>
</template>
<script>
export default {
    name:'WrapperItem',
    props:{
        name:{
            type: String,
            required: true
        }
    },
    inject:['rootStore'],
    data() {
        return {
            activated: false
        }
    },
    watch:{
        // 监听激活状态,保证激活唯一性
        'rootStore.store.activated':{
            handler(value) {
                this.activated = this.name === value
            },
            immediate: true
        }
    },
    methods: {
        // 更新激活状态
        onItemClick () {
            this.rootStore.setActivated (this.name)
        }
    }
    
}
</script>

在掘金时间不短了,看得多,第一次写,难免有错误,希望指正,感谢。🙏