⭐️ 更多前端技术和知识点,搜索订阅号
JS 菌
订阅html
问题复现:父组件中经过名为 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 实例app
<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 方法,最终渲染数据。echarts
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"ui
在代码中增长一行代码:this
watch: {
source (newVal, oldVal) {
console.log(newVal, this.$refs.main) // [Array ...] undefined
this.setOpts()
}
},
复制代码
启示 source 数据虽然有了,但 div 还并未挂载,所以 echarts 没法完成初始化spa
那么想固然的咱们就会去在 mounted 生命周期函数中调用 setOpts 方法:code
mounted () {
console.log(this.source, this.$refs.main) // [] undefined
this.setOpts()
},
复制代码
这样也是错的,由于模板语法中使用了 v-if,那么当 source 并未知足条件的时候,div 固然也不会挂载。所以 div 仍然没法访问到。component
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 上出现的这种问题了。
请关注个人订阅号,不按期推送有关 JS 的技术文章,只谈技术不谈八卦 😊