詳解vue2父元件傳遞props非同步資料到子元件的問題
阿新 • • 發佈:2018-12-30
案例一
父元件parent.vue
12345678910111213141516171819202122232425262728 | // asyncData為非同步獲取的資料,想傳遞給子元件使用 <template> <div> 父元件 <child :child-data= "asyncData" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncData: '' }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncData = 'async data' console.log( 'parent finish' ) }, 2000) } } </script> |
子元件child.vue
?123456789101112131415161718 | <template> <div> 子元件{{childData}} </div> </template> <script> export default { props: [ 'childData' ], data: () => ({ }), created () { console.log( this .childData) // 空值 }, methods: { } } </script> |
上面按照這裡的解析,子元件的html中的{{childData}}的值會隨著父元件的值而改變,但是created裡面的卻不會發生改變(生命週期問題)
案例二
parent.vue
?123456789101112131415161718192021222324252627 | <template> <div> 父元件 <child :child-object= "asyncObject" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncObject: '' }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncObject = { 'items' : [1, 2, 3]} console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?12345678910111213141516171819 | <template> <div> 子元件<!--這裡很常見的一個問題,就是{{childObject}}可以獲取且沒有報錯,但是{{childObject.items[0]}}不行,往往有個疑問為什麼前面獲取到值,後面獲取不到呢?--> <p>{{childObject.items[0]}}</p> </div> </template> <script> export default { props: [ 'childObject' ], data: () => ({ }), created () { console.log( this .childObject) // 空值 }, methods: { } } </script> |
created裡面的卻不會發生改變, 子元件的html中的{{{childObject.items[0]}}的值雖然會隨著父元件的值而改變,但是過程中會報錯
?12 | // 首先傳過來的是空,然後在非同步重新整理值,也開始時候childObject.items[0]等同於''.item[0]這樣的操作,所以就會報下面的錯 vue.esm.js?8910:434 [Vue warn]: Error in render function : "TypeError: Cannot read property '0' of undefined" |
針對二的解決方法:
使用v-if可以解決報錯問題,和created為空問題
123456789101112131415161718192021222324252627282930 | // parent.vue <template> <div> 父元件 <child :child-object= "asyncObject" v- if = "flag" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncObject: '' , flag: false }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncObject = { 'items' : [1, 2, 3]} this .flag = true console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?1234567891011121314151617181920 | <template> <div> 子元件 <!--不報錯--> <p>{{childObject.items[0]}}</p> </div> </template> <script> export default { props: [ 'childObject' ], data: () => ({ }), created () { console.log( this .childObject) // Object {items: [1,2,3]} }, methods: { } } </script> |
子元件使用watch來監聽父元件改變的prop,使用methods來代替created
parent.vue
?123456789101112131415161718192021222324252627 | <template> <div> 父元件 <child :child-object= "asyncObject" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncObject: '' }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncObject = { 'items' : [1, 2, 3]} console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?1234567891011121314151617181920212223242526 | <template> <div> 子元件<!--1--> <p>{{test}}</p> </div> </template> <script> export default { props: [ 'childObject' ], data: () => ({ test: '' }), watch: { 'childObject.items' : function (n, o) { this .test = n[0] this .updata() } }, methods: { updata () { // 既然created只會執行一次,但是又想監聽改變的值做其他事情的話,只能搬到這裡咯 console.log( this .test) // 1 } } } </script> |
子元件watch computed data 相結合,有點麻煩
parent.vue
?123456789101112131415161718192021222324252627 | <template> <div> 父元件 <child :child-object= "asyncObject" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncObject: undefined }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncObject = { 'items' : [1, 2, 3]} console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?123456789101112131415161718192021222324252627282930313233343536 | <template> <div> 子元件<!--這裡很常見的一個問題,就是{{childObject}}可以獲取且沒有報錯,但是{{childObject.items[0]}}不行,往往有個疑問為什麼前面獲取到值,後面獲取不到呢?--> <p>{{test}}</p> </div> </template> <script> export default { props: [ 'childObject' ], data: () => ({ test: '' }), watch: { 'childObject.items' : function (n, o) { this ._test = n[0] } }, computed: { _test: { set (value) { this .update() this .test = value }, get () { return this .test } } }, methods: { update () { console.log( this .childObject) // {items: [1,2,3]} } } } </script> |
使用emit,on,bus相結合
parent.vue
?12345678910111213141516171819202122232425 | <template> <div> 父元件 <child></child> </div> </template> <script> import child from './child' export default { data: () => ({ }), components: { child }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { // 觸發子元件,並且傳遞資料過去 this .$bus.emit( 'triggerChild' , { 'items' : [1, 2, 3]}) console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?123456789101112131415161718192021222324252627 | <template> <div> 子元件 <p>{{test}}</p> </div> </template> <script> export default { props: [ 'childObject' ], data: () => ({ test: '' }), created () { // 繫結 this .$bus.on( 'triggerChild' , (parmas) => { this .test = parmas.items[0] // 1 this .updata() }) }, methods: { updata () { console.log( this .test) // 1 } } } </script> |
這裡使用了bus這個庫,parent.vue和child.vue必須公用一個事件匯流排(也就是要引入同一個js,這個js定義了一個類似let bus = new Vue()的東西供這兩個元件連線),才能相互觸發
使用prop default來解決{{childObject.items[0]}}
parent.vue
?123456789101112131415161718192021222324252627 | <template> <div> 父元件 <child :child-object= "asyncObject" ></child> </div> </template> <script> import child from './child' export default { data: () => ({ asyncObject: undefined // 這裡使用null反而報0的錯 }), components: { child }, created () { }, mounted () { // setTimeout模擬非同步資料 setTimeout(() => { this .asyncObject = { 'items' : [1, 2, 3]} console.log( 'parent finish' ) }, 2000) } } </script> |
child.vue
?1234567891011121314151617181920212223242526 | <template> <div> 子元件<!--1--> <p>{{childObject.items[0]}}</p> </div> </template> <script> export default { props: { childObject: { type: Object, default () { return { items: '' } } } }, data: () => ({ }), created () { console.log( this .childObject) // {item: ''} } } </script> |
在說用vuex解決方法的時候,首先看看案例三
案例三
main.js
?1234567891011121314151617181920212223 | import Vue from 'vue' import App from './App' import router from './router' import VueBus from 'vue-bus' import index from './index.js' Vue.use(VueBus) Vue.config.productionTip = false import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ modules: { index } }) /* eslint-disable no-new */ new Vue({ el: '#app' , store, router, template: '<App/>' , components: { App } }) |
index.js