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 相关文章推荐
javawscript 三级菜单的实现原理
Jul 01 Javascript
Array.prototype.slice 使用扩展
Jun 09 Javascript
jQuery简单实现两级下拉菜单效果代码
Sep 15 Javascript
java中String类型变量的赋值问题介绍
Mar 23 Javascript
javascript的replace方法结合正则使用实例总结
Jun 16 Javascript
javascript实现根据函数名称字符串动态执行函数的方法示例
Dec 28 Javascript
js简易版购物车功能
Jun 17 Javascript
基于jquery trigger函数无法触发a标签的两种解决方法
Jan 06 jQuery
JS运动特效之链式运动分析
Jan 24 Javascript
JS抛物线动画实例制作
Feb 24 Javascript
使用mpvue搭建一个初始小程序及项目配置方法
Dec 03 Javascript
Node.js文本文件BOM头的去除方法
Nov 22 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 Token(令牌)设计
2008/03/15 PHP
php采集速度探究总结(原创)
2008/04/18 PHP
浅谈PHP中foreach/in_array的使用
2015/11/02 PHP
thinkphp ajaxfileupload实现异步上传图片的示例
2017/08/28 PHP
php自定义排序uasort函数示例【二维数组按指定键值排序】
2019/06/19 PHP
TNC vs BOOM BO3 第二场2.13
2021/03/10 DOTA
在Windows上安装Node.js模块的方法
2011/09/25 Javascript
js 幻灯片的实现
2011/12/06 Javascript
jQuery中Dom的基本操作小结
2014/01/23 Javascript
JavaScript闭包详解
2015/02/02 Javascript
jQuery遍历DOM节点操作之filter()方法详解
2016/04/14 Javascript
JavaScript_ECMA5数组新特性详解
2016/06/12 Javascript
jquery通过name属性取值的简单实现方法
2016/06/20 Javascript
Select2.js下拉框使用小结
2016/10/24 Javascript
javascript 中关于array的常用方法详解
2017/05/05 Javascript
jQuery实现QQ空间汉字转拼音功能示例
2017/07/10 jQuery
vue自定义移动端touch事件之点击、滑动、长按事件
2018/07/10 Javascript
Vue vm.$attrs使用场景详解
2020/03/08 Javascript
[00:58]2016年国际邀请赛勇士令状宣传片
2016/06/01 DOTA
python通过socket查询whois的方法
2015/07/18 Python
python实现文本去重且不打乱原本顺序
2016/01/26 Python
Python实现找出数组中第2大数字的方法示例
2018/03/26 Python
Python切片工具pillow用法示例
2018/03/30 Python
python实现时间o(1)的最小栈的实例代码
2018/07/23 Python
jupyter 添加不同内核的操作
2021/02/06 Python
wedgwood加拿大官网:1759年成立的英国国宝级陶瓷餐具品牌
2018/07/17 全球购物
英国领先的大码时装品牌之一:Elvi
2018/08/26 全球购物
Ajax的优点和缺点
2014/11/21 面试题
个人评价范文分享
2014/01/11 职场文书
工作态度检讨书
2014/02/11 职场文书
机械设计及其自动化求职推荐信
2014/02/17 职场文书
医生党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
2015年教师师德师风承诺书
2015/04/28 职场文书
入党群众意见范文
2015/06/02 职场文书
python基于OpenCV模板匹配识别图片中的数字
2021/03/31 Python
MySQL sql模式设置引起的问题
2022/05/15 MySQL