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 EasyUI的添加,修改,删除,查询等基本操作介绍
Oct 11 Javascript
js树插件zTree获取所有选中节点数据的方法
Jan 28 Javascript
很全面的JavaScript常用功能汇总集合
Jan 22 Javascript
JS实现鼠标框选效果完整实例
Jun 20 Javascript
使用Javascript监控前端相关数据的代码
Oct 27 Javascript
vue中的计算属性的使用和vue实例的方法示例
Dec 04 Javascript
谈谈为什么你的 JavaScript 代码如此冗长
Jan 30 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
Apr 02 Javascript
Vue项目打包部署到iis服务器的配置方法
Oct 14 Javascript
微信小程序canvas分享海报功能
Oct 31 Javascript
vue实现淘宝购物车功能
Apr 20 Javascript
理解JavaScript中的Proxy 与 Reflection API
Sep 21 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
第九节 绑定 [9]
2006/10/09 PHP
php5 mysql分页实例代码
2008/04/10 PHP
php实现微信公众号主动推送消息
2015/12/31 PHP
php数据访问之查询关键字
2016/05/09 PHP
PhpStorm terminal无法输入命令的解决方法
2016/10/09 PHP
php使用函数pathinfo()、parse_url()和basename()解析URL
2016/11/25 PHP
js中判断Object、Array、Function等引用类型对象是否相等
2012/08/29 Javascript
如何将一个String和多个String值进行比较思路分析
2013/04/22 Javascript
window.location 对象所包含的属性
2014/10/10 Javascript
使用JavaScript和C#中获得referer
2014/11/14 Javascript
jquery事件preventDefault()方法用法实例
2015/01/16 Javascript
jquery插件pagination实现无刷新ajax分页
2015/09/30 Javascript
JS实现的样式切换功能tableCSS实例
2016/12/30 Javascript
node puppeteer(headless chrome)实现网站登录
2018/05/09 Javascript
Vue插件从封装到发布的完整步骤记录
2019/02/28 Javascript
vue中的 $slot 获取插槽的节点实例
2019/11/12 Javascript
Python切片用法实例教程
2014/09/08 Python
简单介绍利用TK在Python下进行GUI编程的教程
2015/04/13 Python
Python卸载模块的方法汇总
2016/06/07 Python
Python基于QRCode实现生成二维码的方法【下载,安装,调用等】
2017/07/11 Python
Python机器学习之决策树算法实例详解
2017/12/06 Python
使用python实现knn算法
2017/12/20 Python
Python 查看文件的读写权限方法
2018/01/23 Python
初探TensorFLow从文件读取图片的四种方式
2018/02/06 Python
pytorch构建网络模型的4种方法
2018/04/13 Python
Python爬虫小技巧之伪造随机的User-Agent
2018/09/13 Python
使用pandas实现csv/excel sheet互相转换的方法
2018/12/10 Python
Python PyCharm如何进行断点调试
2019/07/05 Python
Python 使用多属性来进行排序
2019/09/01 Python
python3中numpy函数tile的用法详解
2019/12/04 Python
PyTorch中topk函数的用法详解
2020/01/02 Python
5分钟快速掌握Python定时任务框架的实现
2021/01/26 Python
html5的新玩法——语音搜索
2013/01/03 HTML / CSS
皮姆斯勒语言学习:Pimsleur Language Programs
2018/06/30 全球购物
乡镇法制宣传日活动总结
2015/05/05 职场文书
使用Python开发冰球小游戏
2022/04/30 Python