Latest, responsive syntactic sugar in Vue is deprecated

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
  1. 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 final count variable needs to be a reactive variable.

  2. 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 using ref .

  3. 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

  1. 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
  1. 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.

  1. 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 in script.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
  1. 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
  1. 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
  1. 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

  1. Reactive syntax sugar used to be an experimental feature and has been deprecated, please read the deprecation reason [1].

  2. 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:

  1. 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 .

  2. 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.

  3. 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 reactive props 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 avoid ref() if possible for now and use reactive() as before?

  • .value is the necessary complexity. Just like any other reactive library xxx.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