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 相关文章推荐
Confirmer JQuery确认对话框组件
Jun 09 Javascript
JQUBar 基于JQUERY的柱状图插件
Nov 23 Javascript
微信企业号开发之微信考勤百度地图定位
Sep 11 Javascript
ES6概念 ymbol.for()方法
Dec 25 Javascript
通过BootStrap-select插件 js jQuery控制select属性变化
Jan 03 Javascript
JS处理数据四舍五入(tofixed与round的区别详解)
Oct 26 Javascript
JS实现常见的查找、排序、去重算法示例
May 21 Javascript
JS函数动态传递参数的方法分析【基于arguments对象】
Jun 05 Javascript
深入解析koa之异步回调处理
Jun 17 Javascript
Layui 数据表格批量删除和多条件搜索的实例
Sep 04 Javascript
layui的表单提交以及验证和修改弹框的实例
Sep 09 Javascript
vscode中使用npm安装babel的方法
Aug 02 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 加密解密内部算法
2010/04/22 PHP
PHP函数之error_reporting(E_ALL ^ E_NOTICE)详细说明
2011/07/01 PHP
ThinkPHP中使用ajax接收json数据的方法
2014/12/18 PHP
laravel中短信发送验证码的实现方法
2018/04/25 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
javascript 使td内容不换行不撑开
2012/11/29 Javascript
用js写了一个类似php的print_r输出换行功能
2013/02/18 Javascript
关于jQuery object and DOM element
2013/04/15 Javascript
javascript窗口宽高,鼠标位置,滚动高度(详细解析)
2013/11/18 Javascript
javascript设计模式之解释器模式详解
2014/06/05 Javascript
javascript常见操作汇总
2014/09/03 Javascript
使用Node.js为其他程序编写扩展的基本方法
2015/06/23 Javascript
JavaScript学习小结之使用canvas画“哆啦A梦”时钟
2016/07/24 Javascript
JS提示:Uncaught SyntaxError:Unexpected token ) 错误的解决方法
2016/08/19 Javascript
AngularJS中如何使用echart插件示例详解
2016/10/26 Javascript
JavaScript中的编码和解码函数
2017/02/15 Javascript
JavaScript设计模式之策略模式详解
2017/06/09 Javascript
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
2018/01/05 NodeJs
微信小程序如何调用新闻接口实现列表循环
2019/07/02 Javascript
Python的迭代器和生成器使用实例
2015/01/14 Python
Python利用matplotlib.pyplot绘图时如何设置坐标轴刻度
2018/04/09 Python
解决python报错MemoryError的问题
2018/06/26 Python
pytorch实现onehot编码转为普通label标签
2020/01/02 Python
python 项目目录结构设置
2020/02/14 Python
PyCharm License Activation激活码失效问题的解决方法(图文详解)
2020/03/12 Python
Python爬虫如何应对Cloudflare邮箱加密
2020/06/24 Python
.NET笔试题(20个问题)
2016/02/02 面试题
班长岗位职责
2013/11/10 职场文书
老师自我鉴定范文
2013/12/25 职场文书
公积金单位接收函
2014/01/11 职场文书
幼儿园小班评语
2014/04/18 职场文书
大学生村官工作心得体会
2016/01/23 职场文书
升职感谢领导的话语及升职感谢信
2019/06/24 职场文书
SpringBoot生成License的实现示例
2021/06/16 Java/Android
使用Redis实现点赞取消点赞的详细代码
2022/03/20 Redis
java.util.NoSuchElementException原因及两种解决方法
2022/06/28 Java/Android