Vue3 + Echarts项目日记-Props被动更新、refs、emit和监听"空白处"事件

2,612 阅读2分钟

前言

需求是这样的,我要在当前组件生成一个柱状图来渲染我的数据,点击柱状图上的某一列作为查询条件对表格内的数据更改。

我将Echarts作为子组件放在当前组件中,这就涉及到了父子组件传值,props被动更新,Echarts初始化,Echarts监听“空白处”事件。

干货知识点:

  • props如何取值

  • Echarts在什么位置初始化

  • Echarts通过$ref获取DOM节点

  • Echarts点击事件图表更新

  • 子组件给父组件传值的两种emit方法

代码

<template>
  <div ref="category" :style="{ height: height, width: width }" />
</template>

<script>
import * as echarts from 'echarts'
import { onBeforeMount, onMounted, onBeforeUpdate, toRefs, onUpdated, getCurrentInstance } from 'vue'

export default {
  props: {
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '350px'
    },
    data1: Object, 
    data2: Object,
    chartSource: Object
  },
  setup (props, context) {
    // TODO: write Code
  }
}
</script>

Props如何取值

Vue3新增了setup后,我们可以在setup中访问到取过来的props携带的值。

官网文档中说setup的第一个参数是props,它是响应式的,当传入新的props时,它会被更新。

但是我们不能通过ES6解构props,这样会消除prop的响应性,官方给的方法使用toRefs函数来完成操作

import { toRefs } from 'vue'
setup (props, context) {
	const { data1, data2, chartSource } = toRefs(props)
}

这样我们就能取到props中的所有的值,但是要注意的是我们需要加.value才等于他的值,因为传过来的值是proxy对象

Echarts在什么位置初始化

在什么位置初始化,取决于我们在哪个生命周期能获取到DOM节点和拿到props传过来的值。这就要对生命周期的过程十分了解!

mount的过程中,在解析DOM节点,在此时我们是拿不到我们要的DOM节点的,在mounted后才可以获取到DOM节点,那我们就在mouted初始化Echarts?

不,不,不

此时我们的数据还没有传过来,data1、data2、chartSource的值还是空,我们还不能初始化Echarts

子组件的props的数据是父组件渲染结束后才更新的

我们来好好的理解下这句话的原理

首次渲染时,父组件内data1、data2、chartSource的数据是使用ref初始化的,当数据发生变化时,父组件的渲染函数会重新执行,父组件进行自更新。在更新过程中,渲染器发现父组件的subTree包含组件类型的虚拟节点,也就是子组件,调用patchCompnent函数完成子组件更新。

由父组件自更新引起子组件更新叫子组件的被动更新,当子组件发生被动更新时,会检测子组件是否真的需要更新,也就是数据是否变化,如果需要更新,更新子组件的props、slot等内容。

这个阶段就相当于

father mount -> son beforeUpdate -> son Update -> father beforeUpdate

props本质上是父组件的数据,当props发生变化时,会触发父组件的重新渲染。渲染完成后,父组件的数据更新完成,传给子组件,这是在子组件的beforeUpdate才能拿到更新后的props数据。

如果对上面的过程不是很清楚的,可以看 《Vue.js设计与实现》中12.4-props与组件的被动更新

所以,我们应该在子组件的beforeUpdate中进行Echarts初始化。

Echarts通过$ref获取DOM节点

Echarts初始化的第一步要获取DOM节点,那怎么获得呢?

在官方文档中,访问组件的property中表明:

执行setup时,只能访问

  • props
  • attrs
  • slots
  • emit

换句话说,你将无法访问一下组件选项:

  • data
  • computed
  • methods
  • refs(模版ref)

我们在Vue2中使用this.$refs获取DOM节点,但是在Vue3的setup() 内部的 this 的行为与其它选项中的 this 完全不同,无法在setup中使用this.$refs获取DOM节点

那我们要如何在setup中使用refs呢?

Vue3提供了一个getCurrentInstanceApi方法,它支持访问内部组件的实例。

所以在setup中创建一个getCurrentInstance的实例,使用里面的proxy属性带的$ref方法,来获取DOM节点

const { proxy } = getCurrentInstance()	// 获取实例中的proxy

const myChart = proxy.$refs.category	// 获取category的DOM节点

之后就可以进行Echarts初始化了。Echarts的options会使用到props的数据,在使用时记得加.value

Echarts点击事件图表更新

我们要实现在点击空白处的时候,下面的表格数据发生变化。

效果是这样的,其实就是获取到点击位置对应x轴的值。

Snipaste_2022-04-04_21-35-54.png

Echarts官方文档中的概念篇-事件与行为-监听“空白处”的事件,提供了getZr()方法

这个方法要从初始化后的实例上调用,使用.on绑定事件。

const myChart = proxy.$refs.category

const thisChart = echarts.init(myChart)

// 绑定点击事件
thisChart.getZr().on('click', function(event) {
  // TODO: write Code
})

接下来,我们通过什么方法来获取到点击处的X轴的值

thisChart.getZr().on('click', function(event) {
  // 获取点击位置的X和Y坐标
  let pointInPixel = [event.offsetX, event.offsetY]
  // 判断坐标点是否在指定的坐标系或者系列上
  if (thisChart.containPixel('grid', pointInPixel)) {
    // 转换像素坐标值到逻辑坐标系上的点
    let xIndex = thisChart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)[0]
    // 比对location的全名对应的id值,(这个地方可以忽略,因为我的xindex是地点全名不是locationid值)
    let locationid = chartSource.value[xIndex].locationid
    // emit传值给父组件,看下一部分
  }
})

子组件给父组件传值的两种emit方法

我们通过emit方法实现子组件向父组件传值,emit是用来发送组件的自定义事件。

第一种方法

我们可以使用刚才实例的proxy中的$emit方法来发送自定义事件

proxy.$emit('getLocationid', locationid)

第二种方法

在上面中提到了setup可以访问emit

我们需要使用到setup的第二个参数 context 中有 emit 方法

setup (props, context) {
  context.emit('getLocationid', locationid)
}

这样也能发送自定义事件。

结语

干货很多,感谢您耐心的看完了!

在写这篇文章的时候,学习了setup、props、emit、Echarts监听空白事件,实现这个需求需要的知识点真的挺多的,为了搞明白,为什么props在mounted后读不到数据的问题,特地去查了查底层实现的源码。

欢迎掘友在评论区留言

求大佬们评论您遇到这类问题的解决方法,或者还有什么坑点?

觉得我写的还不错的,可以给我个赞吗?求求了!