1. 程式人生 > >詳解vue2父元件傳遞props非同步資料到子元件的問題

詳解vue2父元件傳遞props非同步資料到子元件的問題

案例一

父元件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 = trueconsole.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] // 1this.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

?
1234567891011121314151617181920212223import 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 = falseimport 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