报错信息:Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-...
出错原因:
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
解决方案:
使用$emit('事件名称',参数)方法,将子组件数据作为参数抛出给组件的事件(事件名称可自定义),在父级组件监听这个事件,并且在父组件写一个方法作为事件处理函数,则子组件抛出的数据将作为方法的第一个参数传入,然后在此方法内改变prop参数值即可.
实际上数据流向:子组件----$emit触发事件抛出数据---->父组件----监听事件改变prop---->子组件
代码:
子组件:
1 <template> 2 <div> 3 <!-- 将v-model 拆解为v-bind:value="value" v-on:input="value => $emit('input', value)" 实现封装组件的v-model双向绑定--> 4 <Tabs type="card" closable @on-tab-remove="handleTabRemove" v-bind:value="value" v-on:input="value => $emit('input', value)" @on-click="handleTabClick"> 5 <TabPane v-for="(tab,ind) in tabs" :index="ind+1" ref="tabPanes" :key="tab.component" :name="tab.component" :label="tab.component" :v-if="tab.display" > 6 <Content :style="mainContent" ref="main-cotent"> 7 <router-view /> 8 </Content> 9 </TabPane> 10 <Button @click="closeAll" size="small" slot="extra">关闭全部</Button> 11 </Tabs> 12 </div> 13 </template> 14 15 <script> 16 export default { 17 name: 'i_tabs', 18 props: { 19 value:{ 20 21 },//父组件v-model透传来的value数据,实现双向绑定 22 tab_prop: { 23 type: Object 24 } 25 26 }, 27 data() { 28 return { 29 tabs: this.tab_prop.tabs, 30 mainContent: { 31 'padding-left': '14px', 32 'padding-right': '14px', 33 background: '#fff', 34 height: '1000px' 35 } 36 }; 37 }, 38 created() { 39 40 }, 41 methods: { 42 handleTabsAdd() { 43 44 }, 45 handleTabRemove(name) { 46 console.log('----关闭标签方法,参数name=', name); 47 48 console.log("----关闭tab前tabs:",this.tabs); 49 var thiz = this; 50 var index; 51 this.tabs.forEach(function(element) { 52 if (element.component == name) { 53 index = thiz.tabs.indexOf(element); 54 element.display = false; 55 } 56 }); 57 //如果关闭的是最左侧标签,则激活右侧标签,否则激活左侧标签 58 if(this.tabs.length > 1){ 59 let name =""; 60 if(index == 0){ 61 name = this.tabs[index+1].component; 62 63 }else{ 64 name = this.tabs[index-1].component; 65 } 66 console.log('----tab组件,关闭后---激活标签==', name); 67 this.$emit('change-active-tab-name', name);//向父组件传递数据:借助vue内建的$emit方法触发change-active-tab-name事件,抛出参数name 68 this.$router.push(name); 69 } 70 this.tabs.splice(index, 1); 71 console.log("----关闭tab后tabs:",this.tabs); 72 73 }, 74 closeAll() {}, 75 handleTabClick(name){ 76 console.log("---handleTabClick方法,标签name==",name) 77 this.$router.push(name); 78 } 79 80 }, 81 mounted() { 82 83 }, 84 watch: { 85 '$route' (newRoute) { 86 console.log("------监控路由变化,参数newRoute==",newRoute) 87 console.log('----tab组件,---激活标签==', this.value); 88 // const { name, query, params, meta } = newRoute 89 } 90 }, 91 components: {} 92 }; 93 </script> 94 95 <style></style>
父组件:
1 <template> 2 <div class="index"> 3 <Layout :style="{ padding: '0 24px 24px', background: '#fff' }"> 4 <i_tabs ref="navTabs" :tab_prop="tab_prop" v-model="activeName" @change-active-tab-name="changeActiveName"></i_tabs> 5 </Layout> 6 </div> 7 </template> 8 9 <script> 10 import i_tabs from '@/components/template/i_tabs/i_tabs.vue'; 11 export default { 12 name: 'index', 13 props: {}, 14 data() { 15 return { 16 user: {}, 17 mainContent: { 18 'padding-left': '14px', 19 'padding-right': '14px', 20 background: '#fff', 21 height: '1000px' 22 }, 23 activeName:"", 24 tab_prop: { 25 tabs: [] 26 } 27 }; 28 }, 29 methods: { 30 changeActiveName(name){//父组件事件处理函数,改变传入子组件的prop属性值 31 this.activeName = name; 32 } 33 } 34 , 35 components: { 36 i_tabs 37 } 38 }; 39 </script> 40 <style></style>