vue组件中watch props根据v-if动态判断并挂载DOM的问题


Posted in Javascript onMay 12, 2019

问题复现:父组件中通过名为 source 的 prop 向子组件 Chart 传入数据

<Chart :source="chartData"></Chart>
import Chart from '../components/Chart'

export default {
 name: 'Home',
 components: { Chart },
 data () {
  return {
   chartData: []
  }
 },
 mounted () {
  setTimeout(() => {
   this.chartData = [
    [89.3, 58212, 'Matcha Latte'],
    [57.1, 78254, 'Milk Tea'],
    [74.4, 41032, 'Cheese Cocoa'],
    [50.1, 12755, 'Cheese Brownie'],
    [89.7, 20145, 'Matcha Cocoa'],
    [68.1, 79146, 'Tea'],
    [19.6, 91852, 'Orange Juice'],
    [10.6, 101852, 'Lemon Juice'],
    [32.7, 20112, 'Walnut Brownie']
   ]
  }, 2000)
 }
}

子组件接收 source 数据当存在且至少有一条数据的时候,创建 id 为 main 的 div,用以初始化 echarts 实例

<div v-if="source && source.length" id="main" ref="main" style="width: 600px;height: 400px;"></div>
<div vi-else>none</div>

Chart 组件通过接收数据 watch prop 的变化动态的调用 echarts 的 setOptions 方法,最终渲染数据。

export default {
 // ...
 watch: {
  source (newVal, oldVal) {
   this.setOpts()
  }
 },
 props: ['source'],
 methods: {
  setOpts () {
   let myChart = this.$echarts.init(this.$refs.main)
   myChart.setOption({
    dataset: {
     // ...
     source: this.source
    },
    // ...
   })
  }
 }
}

如果直接这么写必定报错:

Error in callback for watcher "source": "TypeError: Cannot read property 'getAttribute' of undefined"

在代码中增加一行代码:

watch: {
  source (newVal, oldVal) {
   console.log(newVal, this.$refs.main) // [Array ...] undefined
   this.setOpts()
  }
 },

启示 source 数据虽然有了,但 div 还并未挂载,因此 echarts 无法完成初始化

那么想当然的我们就会去在 mounted 生命周期函数中调用 setOpts 方法:

mounted () {
  console.log(this.source, this.$refs.main) // [] undefined
  this.setOpts()
 },

这样也是错的,因为模板语法中使用了 v-if,那么当 source 并未满足条件的时候,div 当然也不会挂载。因此 div 仍然无法访问到。

Error in mounted hook: "TypeError: Cannot read property 'getAttribute' of undefined"

解决办法是要么去掉 v-if 要么换另一种写法

有时我们需要在没有数据的情况下增加一个占位标签用来展示一些额外的提醒信息,如“暂未获取到数据”等。那么去掉 v-if 肯定不行。

既然如此我们保留 v-if 但写法有所改变:

修改 Chart 组件:

<template>
 <div>
  <div id="main" ref="main" style="width: 600px;height: 400px;"></div>
 </div>
</template>

我们只需要一个 source 数据源,当 mounted 的时候调用 setOpts 方法,当 watch 数据变化的时候再次调用以更新数据

export default {
 name: 'Chart',
 props: ['source'],
 mounted () {
  this.setOpts()
 },
 watch: {
  source () {
   this.setOpts()
  }
 },
 methods: {
  setOpts () {
   let myChart = this.$echarts.init(this.$refs.main)
   myChart.setOption({
    dataset: {
     dimensions: ['score', 'amount', 'product'],
     source: this.source
    },
    xAxis: { type: 'category' },
    yAxis: {},
    series: [
     {
      type: 'bar',
      encode: {
       x: 'product',
       y: 'amount'
      }
     }
    ]
   })
  }
 }
}

v-if 的判断我们把他移出去了我们判断 chartData 是否获取到,一旦获取到数据,马上加载 Chart 组件,这样就可以避开在组件内部调用 v-if 带来的问题:

<template>
 <div>
  <Chart :source="chartData" v-if="flag"></Chart>
  <div v-else>none</div>
 </div>
</template>
import Chart from '../components/Chart'

export default {
 name: 'Home',
 components: { Chart },
 data () {
  return {
   chartData: [],
   flag: false
  }
 },
 methods: {
  getData () {
   setTimeout(() => {
    this.chartData = [
     [89.3, 58212, 'Matcha Latte'],
     [57.1, 78254, 'Milk Tea'],
     [74.4, 41032, 'Cheese Cocoa'],
     [50.1, 12755, 'Cheese Brownie'],
     [89.7, 20145, 'Matcha Cocoa'],
     [68.1, 79146, 'Tea'],
     [19.6, 91852, 'Orange Juice'],
     [10.6, 101852, 'Lemon Juice'],
     [32.7, 20112, 'Walnut Brownie']
    ]
    this.flag = true
   }, 2000)
  }
 },
 mounted () {
  this.getData()
 }
}

另外还可将 Chart 组件和站位标签一同封装成一个 ChartWrapper。

这样就不会因在组件内部调用 watch 监听 props 的变化动态 v-if 判断并挂载数据到 DOM 上出现的这种问题了。

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

Javascript 相关文章推荐
jquery 设置元素相对于另一个元素的top值(实例代码)
Nov 06 Javascript
js取消单选按钮选中并判断对象是否为空
Nov 14 Javascript
jquery预加载图片的方法
May 27 Javascript
jquery实现兼容IE8的异步上传文件
Jun 15 Javascript
基于JavaScript实现TAB标签效果
Jan 12 Javascript
简单讲解AngularJS的Routing路由的定义与使用
Mar 05 Javascript
js实现页面a向页面b传参的方法
May 29 Javascript
AngularJS 指令详细介绍
Jul 27 Javascript
Bootstrap实现渐变顶部固定自适应导航栏
Aug 27 Javascript
微信小程序getPhoneNumber获取用户手机号
Sep 29 Javascript
JS实现留言板功能[楼层效果展示]
Dec 27 Javascript
Layui动态生成select下拉选择框不显示的解决方法
Sep 24 Javascript
用js简单提供增删改查接口
May 12 #Javascript
electron-vue利用webpack打包实现多页面的入口文件问题
May 12 #Javascript
vue中axios实现数据交互与跨域问题
May 12 #Javascript
jquery3和layui冲突导致使用layui.layer.full弹出全屏iframe窗口时高度152px问题
May 12 #jQuery
JS块级作用域和私有变量实例分析
May 11 #Javascript
微信小程序封装的HTTP请求示例【附升级版】
May 11 #Javascript
微信小程序自定义toast组件的方法详解【含动画】
May 11 #Javascript
You might like
优化PHP代码的53条建议
2008/03/27 PHP
php str_pad 函数使用详解
2009/01/13 PHP
使用zend studio for eclipse不能激活代码提示功能的解决办法
2009/10/11 PHP
php实现的Cookies操作类实例
2014/09/24 PHP
PHP生成随机数的方法实例分析
2015/01/22 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
Array对象方法参考
2006/10/03 Javascript
jQuery示例收集
2010/11/05 Javascript
jQuery实现移动 和 渐变特效的点击事件
2015/02/26 Javascript
JQuery实现样式设置、追加、移除与切换的方法
2015/06/11 Javascript
Centos7 中 Node.js安装简单方法
2016/11/02 Javascript
jquery插件开发之选项卡制作详解
2017/08/30 jQuery
js 显示日期时间的实例(时间过一秒加1)
2017/10/25 Javascript
Angular4实现图片上传预览路径不安全的问题解决
2017/12/25 Javascript
解决vue 路由变化页面数据不刷新的问题
2018/03/13 Javascript
es6数据变更同步到视图层的方法
2019/03/04 Javascript
解决使用layui的时候form表单中的select等不能渲染的问题
2019/09/18 Javascript
[01:08:57]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第二场
2014/05/24 DOTA
利用python代码写的12306订票代码
2015/12/20 Python
Python利用ElementTree模块处理XML的方法详解
2017/08/31 Python
python交互式图形编程实例(一)
2017/11/17 Python
Python实现按特定格式对文件进行读写的方法示例
2017/11/30 Python
python pandas中对Series数据进行轴向连接的实例
2018/06/08 Python
解决python彩色螺旋线绘制引发的问题
2019/11/23 Python
python pygame实现滚动横版射击游戏城市之战
2019/11/25 Python
Python3搭建http服务器的实现代码
2020/02/11 Python
详解Python IO编程
2020/07/24 Python
Pycharm导入anaconda环境的教程图解
2020/07/31 Python
Css3实现无缝滚动防抖
2020/09/14 HTML / CSS
常用的HTML5列表标签
2017/06/20 HTML / CSS
css animation配合SVG制作能量流动效果
2021/03/24 HTML / CSS
个人四风问题整改措施思想汇报
2014/10/04 职场文书
2015年酒店客房部工作总结
2015/04/25 职场文书
2016年度继续教育学习心得体会
2016/01/19 职场文书
2016年推广普通话宣传周活动总结
2016/04/06 职场文书
Java处理延时任务的常用几种解决方案
2022/06/01 Java/Android