在某些情况下,Vue 的插槽会导致组件强制更新

前几天在给公司优化代码性能的时候发现 vue 插槽会导致组件强制更新,从而导致不必要的渲染,降低页面性能。

以下是我做的示例重现问题(只展示主要代码,省略样式等不必要的代码)。

首先我创建一个简单的组件 title-slot.vue,其中使用到了 <slot>,并添加的 updated 来监听组件是否更新,如果更新就会弹出对话框:

<template>
  <h2>
    <slot>this is second title</slot>
  </h2>
</template>
<script>
export default {
  name: "TitleSlot",
  updated() {
    alert('title slot component updated!')
  }
}
</script>

然后我在 App.vue 中使用它,但不设定插槽内容:

<template>
  <div id="app">
    <h1>{{ count }}</h1>
    <button @click="count += 1">click add count</button>
    <title-slot></title-slot>
  </div>
</template>

<script>
import TitleSlot from "./components/title-slot.vue"

export default {
  name: 'App',
  components: {
    TitleSlot
  },
  data() {
    return {
      count: 0
    }
  }
}
</script>

不难看出 App.vue 中包含一个点击按钮更新 count 的功能,此时我们点击按钮更新并不会弹出对话框。
不使用 slot

但当在 App.vue 中设定 title-slot 组件的插槽内容后,再点击按钮更新 count,此时就会弹出对话框,也就是说 title-slot 组件更新了!

<template>
  <div id="app">
    <h1>{{ count }}</h1>
    <button @click="count += 1">click add count</button>
    <title-slot>this is second title(by slot)</title-slot>
  </div>
</template>

使用 slot

为什么会这样?经过搜索后发现了一片文章 请你说说 Vue 中 slot 和 slot-scope 的原理(2.6.11 深度解析) ,有对这部分内容进行讲解,对应内容是这样的:

其实,是因为执行 _t 函数时,全局的组件渲染上下文是 子组件,那么依赖收集自然也就是收集到 子组件的依赖了。所以在 msgInParent 更新后,其实是直接去触发子组件的重新渲染的,对比 2.5 的版本,这是一个优化。

虽然我不能完全理解其中的原理,但是可以感受到,我测试出来的这个行为在 vue 看来是正常的

真的是这样嘛?这样我可觉得太奇怪了,这样不就会造成很多不必要的渲染。

然后我又经过了尝试,发现了一个突破口: 在 App.vue 中用 v-slot 来使用插槽,这样再更新 count 的时候,title-slot 就不再更新了:

<template>
  <div id="app">
    <h1>{{ count }}</h1>
    <button @click="count += 1">click add count</button>
    <title-slot>
      <template #default>
        this is second title(by slot)
      </template>
    </title-slot>
  </div>
</template>

使用 v-slot

这究竟是为什么?不会真的要我在每个使用的地方都这样修改吧!

这里想问下大家有没有和我遇到一样的问题,大家都是怎么解决的呢,或者大家有看到介绍类似的文章,可以推荐一下吗,小弟不胜感激!

阅读 3.3k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏