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 解决Ajax请求的页面 浏览器后退前进功能,页面刷新功能实效问题
Dec 11 Javascript
javascript实现tabs选项卡切换效果(扩展版)
Mar 19 Javascript
jQuery validate验证插件使用详解
May 11 Javascript
深入解析JavaScript中的arguments对象
Jun 12 Javascript
JS中的hasOwnProperty()和isPrototypeOf()属性实例详解
Aug 11 Javascript
js 转json格式的字符串为对象或数组(前后台)的方法
Nov 02 Javascript
Jquery Easyui菜单组件Menu使用详解(15)
Dec 18 Javascript
从零开始学习Node.js系列教程之基于connect和express框架的多页面实现数学运算示例
Apr 13 Javascript
使用Vue.js和Element-UI做一个简单登录页面的实例
Feb 23 Javascript
vue实现拖拽的简单案例 不超出可视区域
Jul 25 Javascript
JavaScript实现省市联动效果
Nov 22 Javascript
基于ant design日期控件使用_仅月份的操作
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
Php Ctemplate引擎开发相关内容
2012/03/03 PHP
Web程序工作原理详解
2014/12/25 PHP
javascript dom代码应用 简单的相册[firefox only]
2010/06/12 Javascript
字符串的replace方法应用浅析
2011/12/06 Javascript
js DOM 元素ID就是全局变量
2012/09/20 Javascript
IE6浏览器中window.location.href无效的解决方法
2014/11/20 Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
2015/12/14 Javascript
jQuery判断浏览器并动态调整select宽度的方法
2016/03/02 Javascript
jQuery3.0中的buildFragment私有函数详解
2016/08/16 Javascript
微信小程序获取用户openId的实现方法
2017/05/23 Javascript
JavaScript实现连连看连线算法
2019/01/05 Javascript
vue中各种通信传值方式总结
2019/02/14 Javascript
Python实现的简单文件传输服务器和客户端
2015/04/08 Python
Python的Tornado框架异步编程入门实例
2015/04/24 Python
一个基于flask的web应用诞生 组织结构调整(7)
2017/04/11 Python
浅谈pandas筛选出表中满足另一个表所有条件的数据方法
2019/02/08 Python
python实现PID算法及测试的例子
2019/08/08 Python
Django实现whoosh搜索引擎使用jieba分词
2020/04/08 Python
python开发一款翻译工具
2020/10/10 Python
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
大都会艺术博物馆商店:The Met Store
2018/06/22 全球购物
Bailey帽子官方商店:Bailey Hats
2018/09/25 全球购物
Napapijri西班牙在线商店:夹克、外套、运动衫等
2020/11/05 全球购物
咖啡店的创业计划书,让你hold不住
2014/01/03 职场文书
中学生自我评价范文
2014/02/08 职场文书
人事专员岗位职责范本
2014/03/04 职场文书
校园公益广告语
2014/03/13 职场文书
寄语学生的话
2014/04/10 职场文书
法人单位授权委托书范文
2014/10/06 职场文书
2014年高中班主任工作总结
2014/11/08 职场文书
餐饮服务食品安全承诺书
2015/04/29 职场文书
关于军训的感想
2015/08/07 职场文书
2016大学生就业指导课心得体会
2016/01/15 职场文书
原生CSS实现文字无限轮播的通用方法
2021/03/30 HTML / CSS
python中print格式化输出的问题
2021/04/16 Python
如何利用opencv判断两张图片是否相同详解
2021/07/07 Python