Vue v-for中子组件script作用域共享且只执行一次?

212 阅读2分钟

前言

近期在 Vue2 开发中发现 v-for 渲染组件时出现 script 执行一次,且多个组件共享一个 script 作用域。

我现在有这样的界面,统计不同班级的图表数据,正常逻辑肯定是把Card抽出来做成组件

image.png

于是得到一个 PieCard.vue 组件,引入 echarts 并实例化饼图

因为 echarts 实例对象不需要在 DOM 中使用,也没必要让 Vue 对其做响应式处理,所以我把实例myChart放到了 script 下的顶级作用域

// PieCard.vue
<template>
  <div class="pie-simple-chart-container" title="">
    // 省略
    <div class="echarts-pie-simple-box" />
  </div>
</template>

<script>
import * as echarts from 'echarts'
// echarst 实例
let myChart = null
export default {
  mounted () {
    this.initEcharts()
  },
  methods: {
    initEcharts () {
      const chartContainer = this.$el.querySelector('.echarts-pie-simple-box')
      myChart = echarts.init(chartContainer)
      myChart.setOption({ ... })
    },
  },
}
</script>

在父组件中使用 v-for 循环渲染 PieCard.vue

// 父组件
<template>
    <div class="card-list-container">
      <PieCard v-for="item in cardList" :key="item.id" class="card" :/>
    </div>
</template>

到目前目前为止似乎合乎常理没有问题。

继续 为了不让出现内存泄露,咱们在 PieCard.vue 组件 beforeDestroy 生命周期中销毁 echarts 实例对象

// PieCard.vue
<script>
import * as echarts from 'echarts'
// echarst 实例
let myChart = null
export default {
  beforeDestroy () {
   myChart.dispose() // 销毁实例
  },
}
</script>

em... 逻辑上似乎没有问题,但是问题就突然跳出来给了我一耳光。

image.png

并跟我说 实例已经被销毁了,不能再次销毁

什么鬼?

我们在 beforeDestroy 打印一下实例到底怎么了

// PieCard.vue
<script>
import * as echarts from 'echarts'
// echarst 实例
let myChart = null
export default {
  beforeDestroy () {
   console.log(myChart)
  },
}
</script>
1688462094849.jpg

仔细一看两个实例对象 id 竟然一样?为什么两个子组件的myChart指向一个实例?

难道 script 作用域是同一个?

抱着好奇心 再看看,咱们在 PieCard.vuescript 顶级作用域再打印一下

// PieCard.vue
<script>
import * as echarts from 'echarts'
// echarst 实例
let myChart = null
console.log('PieCard script 作用域')
</script>

image.png

我两个图表按理说应该打印两次啊,为什么只打一次?

难道是因为 v-for?

于是我用template-explorer 想看看 v-for 编译后是什么样子

image.png

也没看明白,感觉是编译后 引入组件只执行一次,所以 script 也只执行一次,因为 v-for 中是可以访问遍历项的,所以他们应该是共享了一个父级作用域,但是为什么子组件内的 script 作用域为什么是一个,因为没读过源码 不太明白,有大佬懂得的话可以评论区讲一下。

后面试了 v-for 结合 :is 确没有这个问题。

对于我这里这个问题,最终解决方法还是把 echarts 实例对象放到了 data