背景
项目中有个需求,需要tab形式展示不确定个数的内容,要求可配置,于是我设计了个组合组件: tab.vue, tabContent.vue 父子配合完成tab切换功能
问题
然后父子组件需要通信了,tab.vue 切换tab的时候需要通知tabContent.vue, 假如有两个tab A, B ;点击tab header A的时候, 显示tabContent A ,tabContent B隐藏 ;点击tab header B的时候, 显示B ,A隐藏, 怎么无感通知呢?我采用了组合组件通用的手段: provide, inject
tab.vue :
<div>
<ul>
<li
v-for="tabName in tabNames"
:key="tabName"
@click="currentTab = tabName"
>
{{tabName }}
</li>
</ul>
<div> <slot /> </div>
</div>
props: {
tabNames: {
type: Array,
required: true
},
},
data() {
return {
currentTab: this.tabNames[0]
}
},
provide: {
tab: this // 传tab实例下去给tabContent
}
tabContent.vue :
<div v-show="tab.currentTab === tabName">
<slot />
</div>
inject: ['tab'], // 拿到tab
props: {
tabName: {
type: String,
required: true
}
},
设计出来,看起来没啥问题
<tab :tabNames="['A', 'B']">
<tabContent tabName="A">A</tabContent>
<tabContent tabName="B">B</tabContent>
</tab>
但是跑起来就是失效 ,A B都没有显示。在tabContent created钩子打印排查发现是this指向undefined了
// tabContent.vue
created() {console.log(this.tab)},
方案
知道问题, 那就好解决了,把this绑定到tab实例就好,但是
provide: {
tab: this // 传tab实例下去给tabContent
}
怎么绑定呢?查看文档,会有另外一种用函数的方式
provide() {
return {
tab: this // 传tab实例下去给tabContent
}
}
结果
解决了问题
但是为什么会这样子呢?多年的经验告诉我应该是底层函数会通过call apply的方式绑定了this到实例上
看看源码:
init.png
initProvide
有人说不推荐在业务中使用provide inject。为什么? 因为这东西在祖先组件注入会污染所有后代,同样假如有多层祖先组件,后代就不确定是哪个祖先传下来的了,不好维护。
所以基于以上考虑,只适合在一些强耦合的组合组件中使用。 如
- el-select, el-option
- el-tab, el-tab-pane
- el-table, el-table-colunm
网友评论