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基础的动画教程,直观易懂
Jan 10 Javascript
jQuery插件-jRating评分插件源码分析及使用方法
Dec 28 Javascript
jQuery 选择器项目实例分析及实现代码
Dec 28 Javascript
json字符串之间的相互转换示例代码
Aug 21 Javascript
jQuery函数map()和each()介绍及异同点分析
Nov 08 Javascript
jQuery 更改checkbox的状态,无效的解决方法
Jul 22 Javascript
React实现双向绑定示例代码
Sep 19 Javascript
解析JavaScript模仿块级作用域
Dec 29 Javascript
js数组与字符串常用方法总结
Jan 13 Javascript
在Vue中使用Compass的方法
Mar 02 Javascript
浅谈Webpack核心模块tapable解析
Sep 11 Javascript
vue中后端做Excel导出功能返回数据流前端的处理操作
Sep 08 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电台频率大全 - 18 湖南省
2020/03/11 无线电
php 获取mysql数据库信息代码
2009/03/12 PHP
php+Memcached实现简单留言板功能示例
2017/02/15 PHP
原生Js实现按的数据源均分时间点幻灯片效果(已封装)
2010/12/28 Javascript
jQuery 源码分析笔记(2) 变量列表
2011/05/28 Javascript
jquery插件unobtrusive实现片段式加载
2015/06/15 Javascript
jQuery内容过滤选择器用法示例
2016/09/09 Javascript
vue.js表格分页示例
2016/10/18 Javascript
Vue2组件tree实现无限级树形菜单
2017/03/29 Javascript
bootstrap的工具提示实例代码
2017/05/17 Javascript
Vue学习之路之登录注册实例代码
2017/07/06 Javascript
vue axios数据请求get、post方法及实例详解
2018/09/11 Javascript
浅谈JavaScript面向对象--继承
2019/03/20 Javascript
js实现多张图片每隔一秒切换一张图片
2019/07/29 Javascript
Python中List.index()方法的使用教程
2015/05/20 Python
Python函数式编程指南(四):生成器详解
2015/06/24 Python
Python第三方库的安装方法总结
2016/06/06 Python
详解利用OpenCV提取图像中的矩形区域(PPT屏幕等)
2019/07/01 Python
如何在python中写hive脚本
2019/11/08 Python
Python argparse模块应用实例解析
2019/11/15 Python
Python爬取腾讯视频评论的思路详解
2019/12/19 Python
Python-jenkins模块获取jobs的执行状态操作
2020/05/12 Python
基于python实现matlab filter函数过程详解
2020/06/08 Python
对Python 字典元素进行删除的方法
2020/07/31 Python
哈萨克斯坦移动和数字技术在线商店:SatelOnline.kz
2020/09/04 全球购物
药品业务员岗位职责
2014/04/17 职场文书
竞选卫生委员演讲稿
2014/04/28 职场文书
会计学自荐信
2014/06/03 职场文书
经济类毕业生求职信
2014/06/26 职场文书
诚实守信道德模范事迹材料
2014/08/15 职场文书
家具商场的活动方案
2014/08/16 职场文书
行政专员岗位职责说明书
2014/09/01 职场文书
2014年实习班主任工作总结
2014/11/08 职场文书
《法国号》教学反思
2016/02/22 职场文书
idea编译器vue缩进报错问题场景分析
2021/07/04 Vue.js
图神经网络GNN算法
2022/05/11 Python