详解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 相关文章推荐
javascript 去字符串空格终极版(支持utf8)
Nov 14 Javascript
禁止js文件缓存的代码
Apr 09 Javascript
基于jquery的无限级联下拉框js插件
Oct 29 Javascript
fixedBox固定div漂浮代码支持ie6以上大部分主流浏览器
Jun 26 Javascript
页面内容排序插件jSort使用方法
Oct 10 Javascript
JavaScript中eval()函数用法详解
Dec 14 Javascript
探寻JavaScript中this指针指向
Apr 23 Javascript
深入理解JavaScript中的尾调用(Tail Call)
Feb 07 Javascript
vue数据双向绑定原理解析(get &amp; set)
Mar 08 Javascript
解决Vue页面固定滚动位置的处理办法
Jul 13 Javascript
vue中component组件的props使用详解
Sep 04 Javascript
详解javascript中的变量提升和函数提升
May 24 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安全性需要注意的几点事项
2014/07/17 PHP
php+mysql删除指定编号员工信息的方法
2015/01/14 PHP
php实现的微信红包算法分析(非官方)
2015/09/25 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
Javascript实现的鼠标经过时播放声音
2010/05/18 Javascript
基于jquery的模态div层弹出效果
2010/08/21 Javascript
JavaScript传递变量: 值传递?引用传递?
2011/02/22 Javascript
js获取location.href的参数实例代码
2013/08/02 Javascript
jquery text(),val(),html()方法区别总结
2013/11/04 Javascript
jQuery实现仿淘宝带有指示条的图片转动切换效果完整实例
2015/03/04 Javascript
javascript判断变量是否有值的方法
2015/04/20 Javascript
JavaScript中的Object对象学习教程
2016/05/20 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
2016/06/07 Javascript
关于Vue背景图打包之后访问路径错误问题的解决
2017/11/03 Javascript
vue实现todolist功能、todolist组件拆分及todolist的删除功能
2019/04/11 Javascript
JavaScript HTML DOM元素 节点操作汇总
2019/07/29 Javascript
layer.open 子页面弹出层向父页面传输数据的例子
2019/09/26 Javascript
Vue文本模糊匹配功能如何实现
2020/07/30 Javascript
Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作
2020/08/31 Javascript
[05:04]完美世界携手游戏风云打造 卡尔工作室地图界面篇
2013/04/23 DOTA
python实现socket端口重定向示例
2014/02/10 Python
python实现颜色空间转换程序(Tkinter)
2015/12/31 Python
Python中表达式x += y和x = x+y 的区别详解
2017/06/20 Python
Python 字符串类型列表转换成真正列表类型过程解析
2019/08/26 Python
Python enumerate函数遍历数据对象组合过程解析
2019/12/11 Python
用CSS禁用输入法(CSS3 UI规范)实例解析
2012/12/04 HTML / CSS
波兰购物网站:MALL.PL
2019/05/01 全球购物
10条PHP编程习惯
2014/05/26 面试题
大学生毕业鉴定
2014/01/31 职场文书
三个儿子教学反思
2014/02/03 职场文书
大学生个人自荐信
2014/02/24 职场文书
房产转让协议书(2014版)
2014/09/30 职场文书
2015年检察院个人工作总结
2015/05/20 职场文书
初中英语教师个人工作总结2015
2015/07/21 职场文书
golang中的并发和并行
2021/05/08 Golang
Python使用socket去实现TCP客户端和TCP服务端
2022/04/12 Python