文章目录
- 前言
- 一、问题展示
- 二、源码分析
- 三、解决方案
前言
如果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属性、插槽绑定idname: {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子组件就会调用watchif (!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',