Svelte 的响应式赋值到底怎么触发更新的?
我刚从 Vue 转过来学 Svelte,有点搞不懂它的响应式机制。在 Vue 里我习惯用 this.count++ 或者 Object.assign 来触发更新,但在 Svelte 里好像直接写 count++ 就行?可我试了下数组操作,发现有时候视图没更新,是不是必须用重新赋值才行?比如下面这种 Vue 写法:
<template>
<div>{{ list.length }}</div>
</template>
<script>
export default {
data() {
return { list: [1, 2] }
},
mounted() {
this.list.push(3) // Vue 能响应
}
}
</script>
那在 Svelte 里,如果我写 list.push(3) 是不是就没反应?必须写成 list = [...list, 3] 才行?这背后的原理到底是啥?
Vue 2 用 Object.defineProperty 劫持属性的 getter/setter,Vue 3 用 Proxy,所以对数组的 push、splice 这些方法能自动拦截到。但 Svelte 不一样,它是在编译阶段分析你的赋值语句,然后自动 inject 更新代码。
简单说:Svelte 的响应式靠的是赋值操作。你写 count++ 本质上就是 count = count + 1,这触发更新没问题。但 list.push(3) 只是调用了一个方法改了数组内容,编译器没看到赋值语句,它就不生成更新代码。
所以你的理解是对的,在 Svelte 里必须这么写:
背后原理是这样的:Svelte 编译 script 块的时候,会记录所有赋值语句的左侧变量。比如它看到 count = xxx,就会在赋值后安排一次 "check if changed, if so notify"。但 push 方法调用不产生赋值,所以它不知道该通知谁。
性能上其实 Svelte 这种设计更利索,Vue 那种拦截方法的方式多少有点额外开销,而且有些边界情况容易踩坑。Svelte 要求你明确用赋值来"声明"状态变化,某种程度上是更可控的。
习惯了就好了,写 Svelte 的时候脑子里要想着"凡改动必赋值",别像 Vue 那样想着"我改了就完事儿"。