需求
最近在做公司项目时遇到一个需求,按组件化思想说就是祖先容器中,有不确定层级深度的带有可激活性的子组件,子组件激活性唯一。类似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>
在掘金时间不短了,看得多,第一次写,难免有错误,希望指正,感谢。🙏