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.buildFragment使用方法及思路分析
Jan 07 Javascript
判定是否原生方法的JS代码
Nov 12 Javascript
Windows系统下Node.js的简单入门教程
Jun 23 Javascript
JS+HTML5手机开发之滚动和惯性缓动实现方法分析
Jun 12 Javascript
jQuery实现订单提交页发送短信功能前端处理方法
Jul 04 Javascript
Bootstrap源码学习笔记之bootstrap进度条
Dec 24 Javascript
easy ui datagrid 从编辑框中获取值的方法
Feb 22 Javascript
vue2.0实战之使用vue-cli搭建项目(2)
Mar 27 Javascript
原生js轮播特效
May 18 Javascript
Javascript实现信息滚动效果
May 18 Javascript
详解ECMAScript typeof用法
Jul 25 Javascript
学习 Vue.js 遇到的那些坑
Feb 02 Vue.js
用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的bbs设计(三)
2006/10/09 PHP
php 深入理解strtotime函数的使用详解
2013/05/23 PHP
PHP邮箱验证示例教程
2016/06/01 PHP
PHP数组遍历的几种常见方式总结
2019/02/15 PHP
SWFObject Flash js调用类
2008/07/08 Javascript
JavaScript 学习历程和心得分享
2010/12/12 Javascript
js判断浏览器类型的方法
2013/08/07 Javascript
JS实现带圆弧背景渐变效果的导航菜单代码
2015/10/13 Javascript
整理关于Bootstrap过渡动画的慕课笔记
2017/03/29 Javascript
iscroll动态加载数据完美解决方法
2017/07/18 Javascript
vue 开发一个按钮组件的示例代码
2018/03/27 Javascript
ES6 fetch函数与后台交互实现
2018/11/14 Javascript
[01:03:00]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第一局
2016/02/25 DOTA
[01:05:32]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第一局
2016/03/04 DOTA
Python查看多台服务器进程的脚本分享
2014/06/11 Python
Python 2.7.x 和 3.x 版本的重要区别小结
2014/11/28 Python
基于python 字符编码的理解
2017/09/02 Python
python编程线性回归代码示例
2017/12/07 Python
Python学生成绩管理系统简洁版
2020/04/05 Python
python requests证书问题解决
2019/09/05 Python
Anaconda+Pycharm环境下的PyTorch配置方法
2020/03/13 Python
Django如何继承AbstractUser扩展字段
2020/11/27 Python
基于css3的属性transition制作菜单导航效果
2015/09/01 HTML / CSS
通过HTML5 Canvas API绘制弧线和圆形的教程
2016/03/14 HTML / CSS
日本航空官方网站:JAL
2019/06/19 全球购物
电子商务自荐书范文
2014/01/04 职场文书
舞蹈专业大学生职业规划范文
2014/03/12 职场文书
小学生竞选班长演讲稿
2014/04/24 职场文书
环保建议书400字
2014/05/14 职场文书
小学生学习雷锋倡议书
2014/05/15 职场文书
小学感恩教育活动总结
2014/07/07 职场文书
2014年自愿离婚协议书范本
2014/09/25 职场文书
2014年教师业务工作总结
2014/12/19 职场文书
2015年大学班主任工作总结
2015/04/30 职场文书
shell进度条追踪指令执行时间的场景分析
2022/06/16 Servers
Python测试框架pytest核心库pluggy详解
2022/08/05 Golang