Introduction
One of the major unresolved issues since the introduction of the compositional API concept has been which to use ref
or reactive
. reactive
has the problem of losing responsiveness when deconstructed, while ref
needs to use .value
everywhere, which feels cumbersome, and when there is no help from the type system It’s easy to miss .value
.
For example, the following counter:
<template> <button @click="increment">{<!-- -->{ count }}</button> </template> Copy Code
Use ref
to define the count
variable and the increment
method:
let count = ref(0) function increment() { count.value++ } Copy Code
And using responsive syntactic sugar, we can write code like this:
let count = $ref(0) function increment() { count ++ } Copy Code
-
Vue’s responsive syntactic sugar is a compile-time conversion step, the
$ref()
method is a compile-time macro, it is not a real, runtime method that will be called, but serves as a flag to the Vue compiler that the finalcount
variable needs to be a reactive variable. -
Reactive variables can be accessed and reassigned like ordinary variables, but these operations will become
ref
with.value
after compilation. So the code in the example above will also be compiled to the syntax defined usingref
. -
Every reactive API that returns
ref
has a corresponding macro function prefixed with$
. These include the following APIs:
-
ref -> $ref
-
computed -> $computed
-
shallowRef -> $shallowRef
-
customRef -> $customRef
-
toRef -> $toRef
-
You can use the `$()` macro to convert an existing `ref` into a reactive variable.
const a = ref(0) let count = $(a) count ++ console.log(a.value) // 1 Copy Code
-
The `$$()` macro can be used to keep any reference to a reactive variable as a reference to the corresponding `ref`.
let count = $ref(0) console.log(isRef($$(count))) // true Copy Code
$$()
also works with destructured props
since they are also reactive variables. The compiler will efficiently do the conversion via toRef
:
const { count } = defineProps<{ count: number }>() passAsRef($$(count)) Copy Code
Configuration
Reactive syntactic sugar is a feature specific to the Composition API and must be used through the build step.
-
Required, requires `@vitejs/plugin-vue@>=2.0.0`, will apply to SFC and js\(x\)/ts\(x\) files.
// vite.config.js export default { plugins: [ vue({ reactivityTransform: true }) ] } Copy Code
-
Note that
reactivityTransform
is now a top-level option of the plugin, and not inscript.refSugar
anymore, since it doesn’t apply only to SFC.
If it is built with vue-cli
, vue-loader@>=17.0.0
is required, currently only works for SFC.
// vue.config.js module.exports = { chainWebpack: (config) => { config.module .rule('vue') .use('vue-loader') .tap((options) => { return { ...options, reactivityTransform: true } }) } } Copy Code
If it is built with webpack
+ vue-loader
, vue-loader@>=17.0.0
is required, currently only works for SFC.
// webpack.config.js module.exports = { module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { reactivityTransform: true } } ] } } Copy Code
-
Optional, add the following code in the `tsconfig.json` file, otherwise it will report an error `TS2304: Cannot find name '$ref'.`, although it does not affect the use, but it will affect the development experience:
"compilerOptions":{ "types": ["vue/ref-macros"] } Copy Code
-
Optional, add the following code in the `eslintrc.cjs` file, otherwise it will prompt `ESLint: '$ref' is not defined.(no-undef)`:
module.exports = { globals: { $ref: "readonly", $computed: "readonly", $shallowRef: "readonly", $customRef: "readonly", $toRef: "readonly", } }; Copy Code
-
When responsive syntax sugar is enabled, these macro functions are available globally without manual import. You can also explicitly introduce `vue/macros` in the vue file, so you don't need to configure `tsconfig.json` and `eslintrc` in the second and third steps.
import { $ref } from 'vue/macros' let count = $ref(0) Copy Code
Deprecated experimental features
-
Reactive syntax sugar used to be an experimental feature and has been deprecated, please read the deprecation reason [1].
-
It will be removed from Vue core in a future minor version update. For continued use, please pass the Vue Macros[2] plugin.
Reason for Deprecation
You Yuxi personally gave the reason for abandonment 2 weeks ago (February 21, 2023, 10:05 AM GMT+8), the translation is as follows:
As many of you already know, we officially abandoned this RFC with the consensus of the team.
Reason
The original goal of Reactivity Transform was to improve the developer experience by providing a cleaner syntax when dealing with reactive state. We released it as an experimental product to gather feedback from real-world usage. Despite these proposed benefits, we identified the following issues:
-
Losing the
.value
makes it harder to tell what is being tracked and which line triggered the react effect. This problem is less noticeable in small SFCs, but in large code bases the mental overhead becomes more noticeable, especially if the syntax is also used outside the SFC . -
Because of (1), some users choose to use Reactivity Transform only inside SFC, which creates inconsistencies and context switching costs between different mental models. So the dilemma is that using it only inside the SFC causes inconsistencies, but using it outside the SFC hurts maintainability.
-
Since there will still be external functions expecting raw references, conversions between reactive variables and raw references are unavoidable. This ended up adding more learning curve and extra mental load, which we noticed was more confusing for beginners than the normal Composition API.
Most importantly, the potential risk of fragmentation. Although this is an explicit opt-in, some users have expressed strong opposition to the proposal due to concerns that they will have to work with different codebases where some have opted in to it and others No. This is a legitimate concern, since Reactivity Transform requires a different mental model, which distorts JavaScript semantics (variable assignments can trigger reactive effects).
All things considered, we feel that using it as a stable feature would cause more problems than benefits, and thus isn’t a good tradeoff.
Migration plan
-
This feature is already supported as an external package via Vue Macros[3].
-
3.3: This feature will be marked as deprecated. It will continue to work, but you should migrate to Vue Macros in the meantime.
-
3.4: This functionality will be removed from core and will no longer work unless Vue Macros are used.
Leave a message
-
Although Reactivity Transform will be removed from the official package, I think it’s a good try.
-
Well written. I like detailed RFCs and objective evaluations based on user feedback. The final conclusion makes sense. Don’t let the perfect be the enemy of the good.
-
While I enjoy the convenience of this feature, I did find this potential fragmentation problem in practice. It may be reluctant to remove this feature in a future release, but engineers should take it seriously.
-
Do you remove all functionality or just the part where
ref.value
is converted? What about reactiveprops
destructuring, will it stay? -
I’ve been using it for a medium sized e-commerce site without any issues. I understand the rationale behind removing it, but in practice I’ve found it to be a really big improvement. So my question is: what now?
-
Is it recommended that those who hate
.value
avoidref()
if possible for now and usereactive()
as before? -
.value
is the necessary complexity. Just like any other reactive libraryxxx.set()
does. -
It should be easy to create a package that transforms all Reactivity Transform code, right? I also like to do things the recommended way.
-
…
About this article
Author: zkj
https://juejin.cn/post/7206604884057325605
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge