详解vue2父组件传递props异步数据到子组件的问题


Posted in Javascript onJune 29, 2017

测试环境:vue v2.3.3, vue v2.3.1

案例一

父组件parent.vue

// 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

<template>
 <div>
  子组件{{childData}}
 </div>
</template>

<script>
 export default {
  props: ['childData'],
  data: () => ({
  }),
  created () {
   console.log(this.childData) // 空值
  },
  methods: {
  }
 }
</script>

上面按照这里的解析,子组件的html中的{{childData}}的值会随着父组件的值而改变,但是created里面的却不会发生改变(生命周期问题)

案例二

parent.vue

<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

<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]}}的值虽然会随着父组件的值而改变,但是过程中会报错

// 首先传过来的是空,然后在异步刷新值,也开始时候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为空问题

// 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

<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

<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

<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

<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

<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

<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

<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

<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

<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

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

const state = {
 asyncData: ''
}

const actions = {
 asyncAction ({ commit }) {
  setTimeout(() => {
   commit('asyncMutation')
  }, 2000)
 }
}
const getters = {
}

const mutations = {
 asyncMutation (state) {
  state.asyncData = {'items': [1, 2, 3]}
 }
}

export default {
 state,
 actions,
 getters,
 mutations
}

parent.vue

<template>
 <div>
  父组件
  <child></child>
 </div>
</template>

<script>
 import child from './child'
 export default {
  data: () => ({
  }),
  components: {
   child
  },
  created () {
   this.$store.dispatch('asyncAction')
  },
  mounted () {
  }
 }
</script>

child.vue

<template>
 <div>
  子组件
  <p>{{$store.state.index.asyncData.items[0]}}</p>
 </div>
</template>

<script>
 export default {
  data: () => ({
  }),
  created () {
  },
  methods: {
  }
 }
</script>

{{$store.state.index.asyncData.items[0]}}可以取到改变的值,但是过程中还是出现这样的报错,原因同上

[Vue warn]: Error in render function: "TypeError: Cannot read property '0' of undefined"

所以这里的解决方法是:vuex结合computed、mapState或者合computed、mapGetters

parent.vue

<template>
 <div>
  父组件
  <child></child>
 </div>
</template>

<script>
 import child from './child'
 export default {
  data: () => ({
  }),
  components: {
   child
  },
  created () {
   this.$store.dispatch('asyncAction')
  },
  mounted () {
  }
 }
</script>

child.vue

<template>
 <div>
  子组件
  <p>{{item0}}</p>
  <p>{{item}}</p>
 </div>
</template>

<script>
 import { mapState, mapGetters } from 'vuex'
 export default {
  data: () => ({
   test: ''
  }),
  computed: {
   ...mapGetters({
    item: 'getAsyncData'
   }),
   ...mapState({
    item0: state => state.index.asyncData
   })
  },
  created () {
  },
  methods: {
  }
 }
</script>

index.js

const state = {
 asyncData: ''
}

const actions = {
 asyncAction ({ commit }) {
  setTimeout(() => {
   commit('asyncMutation', {'items': [1, 2, 3]})// 作为参数,去调用mutations中的asyncMutation方法来对state改变
  }, 2000)
 }
}
const getters = {
 getAsyncData: state => state.asyncData
}

const mutations = {
 asyncMutation (state, params) {
  state.asyncData = params.items[0] // 此时params={'items': [1, 2, 3]}被传过来赋值给asyncData,来同步更新asyncData的值,这样html就可以拿到asyncData.items[0]这样的值了
 }
}

export default {
 state,
 actions,
 getters,
 mutations
}

注意上面的

....
commit('asyncMutation', {'items': [1, 2, 3]})
...
state.asyncData = params.items[0]

如果写成这样的话

commit('asyncMutation')
state.asyncData = {'items': [1, 2, 3]}

首先asyncAction是个异步的操作,所以asyncData默认值为空,那么还是导致,child.vue这里报0的错

<template>
 <div>
  子组件
  <p>{{item0}}</p>
  <p>{{item}}</p>
 </div>
</template>

不过根据以上的案例,得出来一个问题就是异步更新值的问题,就是说开始的时候有个默认值,这个默认值会被异步数据改变,比如说这个异步数据返回的object,如果你用props的方式去传递这个数据,其实第一次传递的空值,第二次传递的是更新后的值,所以就出现{{childObject.items[0]}}类似这种取不到值的问题,既然说第一次是空值,它会这样处理''.items[0],那么我们是不是可以在html判断这个是不是空(或者在computed来判断是否为默认值),所以把案例二的child.vue

<template>
 <div>
  <p>{{childObject != '' ? childObject.items[0]: ''}}</p>
 </div>
</template>

<script>
 export default {
  props: ['childObject'],
  data: () => ({
  }),
  created () {
   console.log(this.childObject) // 空值
  },
  methods: {
  }
 }
</script>

这样是可以通过不报错的,就是created是空值,猜想上面vuex去stroe也可以也可以这样做

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS模拟的QQ面板上的多级可展开的菜单
Oct 10 Javascript
jQuery prev ~ siblings选择器使用介绍
Aug 09 Javascript
js触发onchange事件的方法说明
Mar 08 Javascript
超级好用的jQuery圆角插件 Corner速成
Aug 31 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
Dec 17 Javascript
prototype与__proto__区别详细介绍
Jan 09 Javascript
基于jQuery实现简单人工智能聊天室
Feb 10 Javascript
js断点调试心得分享(必看篇)
Dec 08 Javascript
JS实现的邮箱提示补全效果示例
Jan 30 Javascript
Vue实现自定义下拉菜单功能
Jul 16 Javascript
jQuery内容选择器与表单选择器实例分析
Jun 28 jQuery
Vue项目中使用flow做类型检测的方法
Mar 18 Javascript
Vue.js列表渲染绑定jQuery插件的正确姿势
Jun 29 #jQuery
详解vue父子组件间传值(props)
Jun 29 #Javascript
详解如何使用Node.js编写命令工具——以vue-cli为例
Jun 29 #Javascript
基于angular2 的 http服务封装的实例代码
Jun 29 #Javascript
JS通过调用微信API实现微信支付功能的方法示例
Jun 29 #Javascript
详解vue数据渲染出现闪烁问题
Jun 29 #Javascript
将angular.js项目整合到.net mvc中的方法详解
Jun 29 #Javascript
You might like
PHP垃圾回收机制简单说明
2010/07/22 PHP
PHP使用递归生成文章树
2015/04/21 PHP
php实现购物车功能(上)
2020/07/23 PHP
PHP新特性详解之命名空间、性状与生成器
2017/07/18 PHP
javaScript 关闭浏览器 (不弹出提示框)
2010/01/31 Javascript
最新的10款jQuery内容滑块插件分享
2011/09/18 Javascript
自己写了一个展开和收起的多更能型的js效果
2013/03/05 Javascript
js或者jquery判断图片是否加载完成实现代码
2013/03/20 Javascript
JQuery 给元素绑定click事件多次执行的解决方法
2014/09/09 Javascript
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
2016/08/29 Javascript
对js中回调函数的一些看法
2016/08/29 Javascript
Jquery Easyui验证组件ValidateBox使用详解(20)
2016/12/18 Javascript
jQuery中DOM节点的删除方法总结(超全面)
2017/01/22 Javascript
基于JavaScript实现移动端无限加载分页
2017/03/27 Javascript
vue2.0获取自定义属性的值
2017/03/28 Javascript
vue货币过滤器的实现方法
2017/04/01 Javascript
再也不怕 JavaScript 报错了,怎么看怎么处理都在这儿
2020/12/09 Javascript
python3反转字符串的3种方法(小结)
2019/11/07 Python
Django Form and ModelForm的区别与使用
2019/12/06 Python
python爬虫容易学吗
2020/06/02 Python
重构Python代码的六个实例
2020/11/25 Python
css3实现图片遮罩效果鼠标hover以后出现文字
2013/11/05 HTML / CSS
HTML5 实现一个访问本地文件的实例
2012/12/13 HTML / CSS
德国2018年度最佳在线药房:Bodfeld Apotheke
2019/11/04 全球购物
英国比较机场停车场网站:Airport Parking Essentials
2019/12/01 全球购物
平面设计师的工作职责
2013/11/21 职场文书
致标枪运动员广播稿
2014/02/06 职场文书
买房委托公证书
2014/04/08 职场文书
2014最新毕业证代领委托书
2014/09/26 职场文书
校长个人总结
2015/03/03 职场文书
2015年社区环境卫生工作总结
2015/04/21 职场文书
开国大典观后感
2015/06/04 职场文书
公开致歉信
2019/06/24 职场文书
浅谈Python实现opencv之图片色素的数值运算和逻辑运算
2021/06/23 Python
Python函数式编程中itertools模块详解
2021/09/15 Python
MySQL分区以及建索引的方法总结
2022/04/13 MySQL