文章目录
- 前言
- 一、问题展示
- 二、源码分析
- 三、解决方案
前言
如果el-tabs是子组件,父组件传值value / v-model为空字符,这个时候在watch中监听value / v-model就会发现监听的数据会被调用为‘0’。一定是作为子组件引用,且在watch进行监听,且vuex没有缓存。就刚进入页面的时候会出现空白页面,点击tab之后就不会再有问题了,因为value / v-model数据更新了。
当然如果你的tabPanes中的code原本就为‘0’,那就不会出现这个问题了,因为value / v-model匹配的到页面。
一、问题展示
base-tabs组件
<template>
<el-tabs class="base-pane" v-model="activeName">
<el-tab-pane v-for="pane in tabPanes" :key="pane.code" :label="pane[label]" :name="pane[name]">
<slot :name="pane[name]" :pane="pane"></slot>
</el-tab-pane>
</el-tabs>
</template>
<script>
export default {
name: 'basePane',
props: {
value: {
type: String,
default: '',
},
tabPanes: {
type: Array,
default: [],
},
// 标签显示属性
label: {
type: String,
default: 'label',
},
// 标签绑定id属性、插槽绑定id
name: {
type: String,
default: 'code',
},
// 是否要缓存,页面路由
tabId: String,
},
data() {
return {
activeName: this.value,
};
},
mounted() {
// 这里是如果store有缓存,将缓存中的数据进行赋值
if (this.tabId && this.$store.state.setHistoryTab[this.tabId]) {
this.activeName = this.$store.state.setHistoryTab[this.tabId];
}
},
// watch只有在数据变化了之后才会调用
watch: {
value: {
deep: true,
handler(val) {
console.log('value', val);
this.activeName = val;
},
},
activeName(val) {
console.log('activeName', val);
this.$emit('input', val);
if (this.tabId) {
this.$store.commit('setHistoryTab', {
key: this.tabId,
value: val,
});
}
},
},
};
</script>
引用
<base-pane v-model="activeName" :tabPanes="tabPanes" :tabId="$route.fullPath">
</base-pane>
activeName:'', // 这里一定是空字符串
getTabsData() {
this.$post('xxx/xxx/xxx')
.then((res) => {
this.tabPanes = res.rspData;
// 这里是如果store没有缓存,才将数组中的第一个进行赋值
if (this.tabPanes.length && !this.$store.state.setHistoryTab[this.$route.fullPath]) {
this.activeName = this.tabPanes[0].code;
}
})
.catch();
},
打印结果
子组件这里watch中的activeName竟然是0,这也就导致了,vuex中缓存的数据是0,activeName 找不到对应的code,页面就不会显示了
二、源码分析
在elementui文件夹中的tabs.vue文件中。这里将data和watch放出来就是为了显示currentName是value / v-model的数据,setCurrentName的方法就是对currentName 进行数据修改。
data() {
return {
currentName: this.value || this.activeName,
panes: []
};
},
watch: {
activeName(value) {
this.setCurrentName(value);
},
value(value) {
this.setCurrentName(value);
},
currentName(value) {
if (this.$refs.nav) {
this.$nextTick(() => {
this.$refs.nav.$nextTick(_ => {
this.$refs.nav.scrollToActiveTab();
});
});
}
}
},
created() {
// 在if中'',0都会被认为是false。这就会导致currentName会被设置为’0’。base-tabs子组件就会调用watch
if (!this.currentName) {
this.setCurrentName('0');
}
this.$on('tab-nav-update', this.calcPaneInstances.bind(null, true));
},
测试结果
三、解决方案
父组件、子组件两种方式各选一个就可以了。这样可以的原因是因为base-tabs组件里的watch一开始就不会执行,vuex中就不会存储数据,也就会直接执行父组件的【将数组中的第一个进行赋值】的操作。而之前不可以的原因是因为,el-tabs的源码中将空字符串变成了’0’,base-tabs组件里的watch一开始就会执行,vuex中就会存储数据,也就不会直接执行父组件的【将数组中的第一个进行赋值】的操作,而tabPans中的code匹配不到‘0’,页面就不会显示了,但是你点击别的之后页面又会出现了,因为那个时候的activeName就会是正常的code,也就不会再有问题了
base-tabs组件
<script>
export default {
data() {
return {
activeName: this.value || '1',
};
},
};
</script>
父组件引用
activeName:'1',