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 相关文章推荐
JavaScript 编程引入命名空间的方法
Jun 29 Javascript
jquery validator 插件增加日期比较方法
Feb 21 Javascript
前端开发部分总结[兼容性、DOM操作、跨域等](持续更新)
Mar 04 Javascript
关于JAVASCRIPT urldecode URL解码的问题
Jan 08 Javascript
整理的比较全的event对像在ie与firefox浏览器中的区别
Nov 25 Javascript
jquery的attr方法禁用表单元素禁用输入内容
Jun 23 Javascript
使用jQuery制作遮罩层弹出效果的极简实例分享
May 12 Javascript
angularjs 源码解析之injector
Aug 22 Javascript
在一个页面实现两个zTree联动的方法
Dec 20 Javascript
微信小程序中this.data与this.setData的区别详解
Sep 17 Javascript
微信小程序HTTP接口请求封装的实现
Feb 21 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
Nov 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
全国FM电台频率大全 - 2 天津市
2020/03/11 无线电
特转载一高手总结PHP学习资源和链接.
2006/12/05 PHP
php+mysql写的简单留言本实例代码
2008/07/25 PHP
PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法
2017/11/10 PHP
PHP+redis实现的限制抢购防止商品超发功能详解
2019/09/19 PHP
通过Unicode转义序列来加密,按你说的可以算是混淆吧
2007/05/06 Javascript
JavaScript与C# Windows应用程序交互方法
2007/06/29 Javascript
JavaScript 事件属性绑定带参数的函数
2009/03/13 Javascript
jQuery下通过$.browser来判断浏览器.
2011/04/05 Javascript
jquery实现预览提交的表单代码分享
2014/05/21 Javascript
javascript实现ecshop搜索框键盘上下键切换控制
2015/03/18 Javascript
asp知识整理笔记3(问答模式)
2015/09/27 Javascript
jquery mobile 移动web(5)
2015/12/20 Javascript
纯JS代码实现一键分享功能
2016/04/20 Javascript
swiper实现导航滚动效果
2020/12/13 Javascript
[03:19]2016国际邀请赛中国区预选赛第四日TOP10镜头集锦
2016/07/01 DOTA
python实现每次处理一个字符的三种方法
2014/10/09 Python
python如何压缩新文件到已有ZIP文件
2018/03/14 Python
pytorch构建网络模型的4种方法
2018/04/13 Python
详解python播放音频的三种方法
2019/09/23 Python
wxpython绘制音频效果
2019/11/18 Python
Python datetime 格式化 明天,昨天实例
2020/03/02 Python
Django通过json格式收集主机信息
2020/05/29 Python
Python爬取豆瓣数据实现过程解析
2020/10/27 Python
Python控制鼠标键盘代码实例
2020/12/08 Python
Django 实现图片上传和下载功能
2020/12/31 Python
文明风采获奖感言
2014/02/18 职场文书
预防传染病方案
2014/06/14 职场文书
2015年党员承诺书
2015/01/21 职场文书
酒店工程部的岗位职责汇总大全
2019/10/23 职场文书
Django开发RESTful API实现增删改查(入门级)
2021/05/10 Python
分布式锁为什么要选择Zookeeper而不是Redis?看完这篇你就明白了
2021/05/21 Redis
MySQL CHAR和VARCHAR该如何选择
2021/05/31 MySQL
你知道Java Spring的两种事务吗
2022/03/16 Java/Android
MySQL 执行数据库更新update操作的时候数据库卡死了
2022/05/02 MySQL
使用HBuilder制作一个简单的HTML5网页
2022/07/07 HTML / CSS