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设置按钮的disabled属性的实现代码
Nov 28 Javascript
Js 获取Gridview选中行的内容操作步骤
Feb 05 Javascript
JavaScript怎么判断图片是否加载完成以便获取其尺寸
May 08 Javascript
原生javascript实现图片滚动、延时加载功能
Jan 12 Javascript
JavaScript中数据结构与算法(二):队列
Jun 19 Javascript
Javascript中replace()小结
Sep 30 Javascript
JS设置CSS样式的方式汇总
Jan 21 Javascript
Vue 短信验证码组件开发详解
Feb 14 Javascript
angular2中router路由跳转navigate的使用与刷新页面问题详解
May 07 Javascript
JavaScript实现的超简单计算器功能示例
Dec 23 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
Oct 10 jQuery
js实现简易ATM功能
Oct 27 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的论坛(2)
2006/10/09 PHP
一个php导出oracle库的php代码
2009/04/20 PHP
PHP--用万网的接口实现域名查询功能
2012/12/13 PHP
php可应用于面包屑导航的递归寻找家谱树实现方法
2015/02/02 PHP
JavaScript 给汉字排序实例代码
2008/06/28 Javascript
window.onload 加载完毕的问题及解决方案(上)
2009/07/09 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
jquery实现的带缩略图的焦点图片切换(自动播放/响应鼠标动作)
2013/01/23 Javascript
兼容IE和Firefox火狐的上下、左右循环无间断滚动JS代码
2013/04/19 Javascript
Javascript中的回调函数和匿名函数的回调示例介绍
2014/05/12 Javascript
checkbox勾选判断代码分析
2014/06/11 Javascript
常用DOM整理
2015/06/16 Javascript
浅谈angularJS中的事件
2016/07/12 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
2016/08/15 Javascript
AngularJs Forms详解及简单示例
2016/09/01 Javascript
微信小程序 购物车简单实例
2016/10/24 Javascript
Angular学习笔记之angular的$filter服务浅析
2016/11/12 Javascript
将Sublime Text 3 添加到右键中的简单方法
2017/12/12 Javascript
详解angular脏检查原理及伪代码实现
2018/06/08 Javascript
JavaScript基于数组实现的栈与队列操作示例
2018/12/22 Javascript
js实现弹窗效果
2020/08/09 Javascript
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
2020/09/08 Javascript
[01:10:58]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
py2exe 编译ico图标的代码
2013/03/08 Python
python数据预处理 :数据共线性处理详解
2020/02/24 Python
Pycharm最常用的快捷键及使用技巧
2020/03/05 Python
HTML5移动开发图片压缩上传功能
2016/11/09 HTML / CSS
localStorage 设置过期时间的方法实现
2018/12/21 HTML / CSS
网站性能延迟加载图像的五种技巧(小结)
2020/08/13 HTML / CSS
爱尔兰领先的在线体育用品零售商:theGAAstore
2018/04/16 全球购物
2014年安全生产责任书
2014/07/22 职场文书
社区平安建设汇报材料
2014/08/14 职场文书
特岗教师个人总结
2015/02/10 职场文书
工作简报怎么写
2015/07/21 职场文书
考教师资格证不要错过的4个最佳时机
2019/07/17 职场文书
vue二维数组循环嵌套方式 循环数组、循环嵌套数组
2022/04/24 Vue.js