先上结论,当然是可以的: https://github.com/vuejs/vue-next/tree/master/packages/ref-transform
React hooks 通过重复调用模拟 algebraic effects ,但只是在获取值的时候不需要 value,改动的时候需要调用函数,而且带来很多其他限制和代价(比如必须要给 useEffect 传递正确的依赖数组,不然回调里的变量引用就会是过期的)。另外 React hooks 跟 react 组件上下文强绑定,hooks 脱离 react 组件就无法使用,更别提脱离 react 框架了。
Svelte 通过分析组件 script AST 进行编译来改写你的源码,比如 a = 1 后面会插入一个 $$invalidate() 函数来通知组件。这样做也有一些代价,比如:
- 只能通过给变量赋值来触发更新。比如你写 arr.push(1) 就不行,必须写 arr = [...arr, 1]
- 嵌套的函数内声明的变量就不能触发更新,也不能传递出去。
- 跟 hooks 一样跟组件上下文强绑定。脱离组件上下文就不能使用赋值响应式,而必须用另一套 store API。
- 强依赖编译。没有编译这一步就不能用。
Vue 3 的响应式系统本身最大的特点是不仅不依赖编译,而且跟组件上下文无关,甚至跟 Vue 框架其它部分也是解耦的。同一套系统你可以用在 Vue 组件里,组件外,其他框架里,甚至用在后端。
在无编译的前提下,JS 是不可能做到靠赋值触发更新的。或者说靠赋值触发更新本身就是披着 js 外衣但做着 js 做不到的行为的黑魔法。有些人会觉得违背 js 标准行为不好,但有些人觉得比起带来的开发体验收益,这么一点点黑魔法是可以接受的。
开头提到的 ref transform 就是在以 Vue 的响应式系统做基础,配合编译的前提,做到能够对用 $ 开头的 API 声明的变量以赋值触发更新。比起 Svelte 还顺道解决了一些问题:
- 因为底下依然是 Vue 的响应式系统,所以对对象的操作也能触发更新,比如 arr.push(1) 就能正常工作。
- 能够在嵌套函数内使用,并且用配套的 $$ 宏传递出去。
- 跟组件上下文解耦,在 ts/js 里也能使用。组件内外依然使用同一套系统,同一套语法。
总的来说,由于 js 的语言限制,响应式系统是逃不过类似于 atom 的概念的。Svelte 是组件内外两套系统并且强依赖编译,Vue 则是基于同一套不依赖编译的系统,并在可以编译的情况下提供改善体验的语法糖。