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 UI 1.72 之datepicker
Dec 29 Javascript
JavaScript中去掉数组中的重复值的实现方法
Aug 03 Javascript
jQuery中:not选择器用法实例
Dec 30 Javascript
浅谈JavaScript中变量和函数声明的提升
Aug 09 Javascript
React学习笔记之条件渲染(一)
Jul 02 Javascript
浅谈JavaScript作用域和闭包
Sep 18 Javascript
node通过npm写一个cli命令行工具
Oct 12 Javascript
javascript实现最长公共子序列实例代码
Feb 05 Javascript
React学习笔记之高阶组件应用
Jun 02 Javascript
RequireJS用法简单示例
Aug 20 Javascript
详解vue-cli 2.0配置文件(小结)
Jan 14 Javascript
JS封装cavans多种滤镜组件
Feb 15 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
DC动漫人物排行
2020/03/03 欧美动漫
php开发文档 会员收费1期
2012/08/14 PHP
ThinkPHP控制器里javascript代码不能执行的解决方法
2014/11/22 PHP
Smarty环境配置与使用入门教程
2016/05/11 PHP
PHP中类属性与类静态变量的访问方法示例
2016/07/13 PHP
在laravel框架中使用model层的方法
2019/10/08 PHP
Extjs中ComboBoxTree实现的下拉框树效果(自写)
2013/05/28 Javascript
jQuery控制TR显示隐藏的几种方法
2014/06/18 Javascript
js实现二代身份证号码验证详解
2014/11/20 Javascript
jQuery选择器源码解读(一):Sizzle方法
2015/03/31 Javascript
Javascript递归打印Document层次关系实例分析
2015/05/15 Javascript
Vue项目webpack打包部署到服务器的实例详解
2017/07/17 Javascript
Vue.js路由实现选项卡简单实例
2019/07/24 Javascript
JavaScript数组去重实现方法小结
2020/01/17 Javascript
python中的迭代和可迭代对象代码示例
2017/12/27 Python
python分治法求二维数组局部峰值方法
2018/04/03 Python
Pycharm无法使用已经安装Selenium的解决方法
2018/10/13 Python
Python基础之函数的定义与使用示例
2019/03/23 Python
Python字符串的修改方法实例
2019/12/19 Python
Python实现队列的方法示例小结【数组,链表】
2020/02/22 Python
CSS3 Notes: -webkit-box-reflect实现倒影的实例
2016/12/08 HTML / CSS
html5本地存储之localstorage 、本地数据库、sessionStorage简单使用示例
2014/05/08 HTML / CSS
HTML5 CSS3实现一个精美VCD包装盒个性幻灯片案例
2014/06/16 HTML / CSS
Html5页面获取微信公众号的openid的方法
2020/05/12 HTML / CSS
萌新的HTML5 入门指南
2020/11/06 HTML / CSS
Manuka Doctor英国官网:真正的麦卢卡蜂蜜和护肤品
2018/10/26 全球购物
美国电子产品购物网站:BuyDig.com
2020/06/17 全球购物
Java程序员面试90题
2013/10/19 面试题
廉政教育心得体会
2014/01/01 职场文书
电台实习生求职信
2014/02/25 职场文书
庆祝国庆节演讲稿2014
2014/09/19 职场文书
银行贷款委托书范本
2014/10/11 职场文书
走进毛泽东观后感
2015/06/04 职场文书
投资入股协议书
2016/03/22 职场文书
Python爬取某拍短视频
2021/06/11 Python
Python 统计序列中元素的出现频度
2022/04/26 Python