前几天在给公司优化代码性能的时候发现 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
的功能,此时我们点击按钮更新并不会弹出对话框。
但当在 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>
为什么会这样?经过搜索后发现了一片文章 请你说说 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>
这究竟是为什么?不会真的要我在每个使用的地方都这样修改吧!
这里想问下大家有没有和我遇到一样的问题,大家都是怎么解决的呢,或者大家有看到介绍类似的文章,可以推荐一下吗,小弟不胜感激!