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 相关文章推荐
document.onreadystatechange事件的用法分析
Oct 17 Javascript
一个JavaScript递归实现反转数组字符串的实例
Oct 14 Javascript
js淡入淡出的图片轮播效果代码分享
Aug 24 Javascript
纯JavaScript基于notie.js插件实现消息提示特效
Jan 18 Javascript
jQuery实现导航高亮的方法【附demo源码下载】
Nov 09 Javascript
详解vue2.0脚手架的webpack 配置文件分析
May 27 Javascript
基于js原生和ajax的get和post方法以及jsonp的原生写法实例
Oct 16 Javascript
node中的密码安全(加密)
Sep 17 Javascript
微信小程序http连接访问解决方案的示例
Nov 05 Javascript
详解React 服务端渲染方案完美的解决方案
Dec 14 Javascript
vue模仿网易云音乐的单页面应用
Apr 24 Javascript
Vue打包后访问静态资源路径问题
Nov 08 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_NETWORK_GETADDRESSES: GETADDRINFO FAILED问题解决办法
2014/05/04 PHP
关于PHP中字符串与多进制转换函数的实例代码
2016/11/03 PHP
Zend Framework上传文件重命名的实现方法
2016/11/25 PHP
Avengerls vs KG BO3 第三场2.18
2021/03/10 DOTA
mouse_on_title.js
2006/08/25 Javascript
Node.js 异步编程之 Callback介绍(一)
2015/03/30 Javascript
JavaScrip调试技巧之断点调试
2015/10/22 Javascript
关于获取DIV内部内容报错的原因分析及解决办法
2016/01/29 Javascript
JavaScript学习笔记之取数组中最大值和最小值
2016/03/23 Javascript
jquery移除了live()、die(),新版事件绑定on()、off()的方法
2016/10/26 Javascript
Angular.JS内置服务$http对数据库的增删改使用教程
2017/05/07 Javascript
Node.js中 __dirname 的使用介绍
2017/06/19 Javascript
vue+echarts实现动态绘制图表及异步加载数据的方法
2018/10/17 Javascript
vue elementui tree 任意级别拖拽功能代码
2020/08/31 Javascript
了不起的11个JavaScript代码重构最佳实践小结
2021/01/11 Javascript
Python Tkinter简单布局实例教程
2014/09/03 Python
python机器学习案例教程——K最近邻算法的实现
2017/12/28 Python
windows下 兼容Python2和Python3的解决方法
2018/12/05 Python
Pandas DataFrame数据的更改、插入新增的列和行的方法
2019/06/25 Python
python 反编译exe文件为py文件的实例代码
2019/06/27 Python
python爬虫模拟浏览器访问-User-Agent过程解析
2019/12/28 Python
Python读取csv文件实例解析
2019/12/30 Python
navabi英国:设计师大码女装
2019/06/25 全球购物
如何定义一个可复用的服务
2014/09/30 面试题
拉丁舞学习者的自我评价
2013/10/27 职场文书
中专毕业生自荐信
2013/11/16 职场文书
小学生感恩老师演讲稿
2014/08/28 职场文书
组工干部演讲稿
2014/09/02 职场文书
写给父母的感谢信
2015/01/22 职场文书
成品仓管员岗位职责
2015/04/01 职场文书
通用员工手册范本
2015/05/14 职场文书
让生命充满爱观后感
2015/06/08 职场文书
竞聘演讲报告:基本写作有哪些?附开头范文
2019/10/16 职场文书
详细分析PHP7与PHP5区别
2021/06/26 PHP
OpenStack虚拟机快照和增量备份实现方法
2022/04/04 Servers
Java后端 Dubbo retries 超时重试机制的解决方案
2022/04/14 Java/Android