引言
最近在使用 Vue 3 的过程中,我对 <script setup>
语法糖感到特别惊艳。它大幅简化了组件的编写过程。但有一次,我发现同样的代码在 setup()
函数中和 <script setup>
语法糖中表现出来的效果竟然不同!这到底是为什么?经过一个小探索,总算有所了解。这里分享出来,希望能对同样困惑的朋友有所帮助。
现象描述
同样的一段代码,在 setup()
函数和 <script setup>
语法糖中表现出不同的效果。这一现象确实让我相当困惑。于是,我决定通过一段具体的最小复现代码示例来进行探讨和理解。
先来看以下两个例子:
使用 setup()
函数的效果:
使用 <script setup>
时的效果:
使用 setup()
函数的代码:
使用 setup()
函数的版本可以在这个链接中在线试验:演示地址
<template>
<button @click="change">+1</button>
<div>{{ text }}</div>
{{ flag }}
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
setup() {
const flag = ref(false);
let text = 111;
const change = () => {
text++;
flag.value = !flag.value;
};
return {
flag,
text,
change
};
}
})
</script>
使用 <script setup>
语法糖的代码:
使用 <script setup>
的版本可以在这个链接中在线试验:演示地址
<template>
<button @click="change">+1</button>
<div>{{ text }}</div>
{{ flag }}
</template>
<script setup lang="ts">
import { ref } from 'vue'
const flag = ref(false);
let text = 111;
const change = () => {
text++;
flag.value = !flag.value;
};
</script>
两段代码看上去几乎一模一样,但在运行时却会发现,使用 setup()
函数的版本中页面中 text
的值不会变化,而 <script setup>
版本中页面中 text
的值会变化。为了理解这种现象,我决定查看编译后的产物。
编译后的产物分析
通过上述提供的 演示地址
,可以方便地运行两种不同版本的代码,并且点击右侧工具栏中的 “JS” 按钮可以查看编译后的产物。
在查看了编译后的代码后,我发现了两种写法在处理变量暴露时的不同之处。这些差异导致了页面行为的变化。
setup()
函数编译后的关键部分:
export default {
setup() {
const flag = ref(false);
let text = 111;
const change = () => {
text++;
flag.value = !flag.value;
};
return {
flag,
text, // 这里是值传递
change
};
}
}
在 setup()
函数中,返回的 text
是一个普通的局部变量。通过值传递
的方式在返回对象中暴露出来。因此,当 text
的值变化时,由于它不是响应式的,Vue 无法追踪到这种变化,也就不会触发视图的更新。
<script setup>
编译后的关键部分:
const __sfc__ = /*#__PURE__*/defineComponent({
setup(__props, { expose }) {
expose();
const flag = ref(false);
let text = 111;
const change = () => {
text++;
flag.value = !flag.value;
};
return { flag, get text() { return text }, set text(v) { text = v }, change };
}
});
在 <script setup>
中,编译器会为顶层变量自动生成 getter
和 setter
,即使这些变量本身不是响应式的。这样一来,每次渲染时模板会通过 getter
获取最新值,因此表现出类似响应式的效果。
总结
Vue 3 的 <script setup>
语法糖通过自动生成 getter
和 setter
,使得顶层变量即使不是响应式,也能在模板中表现出类似响应式的效果。
当然,我的理解可能有所不足或存在偏误,欢迎大家批评指正和交流讨论。希望这篇文章能对你有所帮助,共同提升我们的开发体验! 😊