報錯信息: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>