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 相关文章推荐
javascript 伪数组实现方法
Oct 11 Javascript
js类式继承与原型式继承详解
Apr 07 Javascript
基于JavaScript实现Tab选项卡切换效果
Nov 24 Javascript
Bootstrap整体框架之JavaScript插件架构
Dec 15 Javascript
JS正则表达式验证账号、手机号、电话和邮箱是否合法
Mar 08 Javascript
使用openSpeDiv方法实现Ecshop登录弹窗框效果
Mar 13 Javascript
9种使用Chrome Firefox 自带调试工具调试javascript技巧
Dec 22 Javascript
vue和webpack项目构建过程常用的npm命令详解
Jun 15 Javascript
vue+elementUI实现表格关键字筛选高亮
Oct 26 Javascript
js实现带搜索功能的下拉框
Jan 11 Javascript
Vue实现菜单切换功能
Nov 08 Javascript
如何在现代JavaScript中编写异步任务
Jan 31 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
MySql中正则表达式的使用方法描述
2008/07/30 PHP
PHP 获取远程文件大小的3种解决方法
2013/07/11 PHP
PHP中获取时间的下一周下个月的方法
2014/03/18 PHP
PHP根据传入参数合并多个JS和CSS文件的简单实现
2014/06/13 PHP
PHP遍历文件夹与文件类及处理类用法实例
2014/09/23 PHP
php简单防盗链实现方法
2015/07/29 PHP
php set_include_path函数设置 include_path 配置选项
2016/10/30 PHP
详解thinkphp中的volist标签
2018/01/15 PHP
用js的for循环获取radio选中的值
2013/10/21 Javascript
jquery操作复选框(checkbox)的12个小技巧总结
2014/02/04 Javascript
js实现网页抽奖实例
2015/08/05 Javascript
jquery实现的Accordion折叠面板效果代码
2015/09/02 Javascript
js显示当前日期时间和星期几
2015/10/22 Javascript
js仿微博实现统计字符和本地存储功能
2015/12/22 Javascript
JS实现点击循环切换显示内容的方法
2017/10/19 Javascript
js常用方法、检查是否有特殊字符串、倒序截取字符串操作完整示例
2020/01/26 Javascript
js实现动态时钟
2020/03/12 Javascript
js实现从右往左匀速显示图片(无缝轮播)
2020/06/29 Javascript
[02:11]2016国际邀请赛中国区预选赛全程回顾
2016/07/01 DOTA
python下MySQLdb用法实例分析
2015/06/08 Python
Jacobi迭代算法的Python实现详解
2019/06/29 Python
简单易懂Pytorch实战实例VGG深度网络
2019/08/27 Python
Python获取一个用户名的组ID过程解析
2019/09/03 Python
Python3如何在Windows和Linux上打包
2020/02/25 Python
对python中各个response的使用说明
2020/03/28 Python
python opencv 实现读取、显示、写入图像的方法
2020/06/08 Python
详解win10下pytorch-gpu安装以及CUDA详细安装过程
2021/01/28 Python
纯css3实现思维导图样式示例
2018/11/01 HTML / CSS
HTML5 在canvas中绘制文本附效果图
2014/06/23 HTML / CSS
英国最受信任的在线眼镜商之一:Fashion Eyewear
2019/10/31 全球购物
《狼和小羊》教学反思
2014/04/20 职场文书
机关党总支领导班子整改方案
2014/09/20 职场文书
公司授权委托书格式样本
2014/10/01 职场文书
筑梦中国心得体会
2016/01/18 职场文书
导游词之黄帝陵景区
2019/09/16 职场文书
浅谈pytorch中stack和cat的及to_tensor的坑
2021/05/20 Python