Vue3 响应式语法糖

2023-11-05 23:42:13 浏览数 (1)

ref 与 响应式变量($ref)

响应式的变量可以像普通变量那样被访问和重新赋值,但这些操作在编译后都会变为带 .value 的 ref。

响应式对象存在解构丢失响应性的问题,而 ref 需要到处使用 .value 则感觉很繁琐.

Vue 响应式语法糖 提供了一个 $ref() 方法是一个编译时的宏命令。

它不是一个真实的、在运行时会调用的方法。而是用作 Vue 编译器的标记,表明最终的 变量需要是一个响应式变量

ref

ref 得通过ref.value 访问, 有时会我们会忘记使用 .value 而丢失响应性。

代码语言:txt复制
const str = ref('测试')


onMounted(() => {
  console.log(str.value)  //得通过ref.value 访问
})

$ref

通过 使用宏命令 $ 编译后都会变为带 .value 的 ref 。

代码语言:txt复制
const str = $ref('测试')


onMounted(() => {
  console.log(str)  
})

每一个会返回 ref 的响应式 API 都有一个相对应的、以 $ 为前缀的宏函数。包括以下这些 API:

  • ref -> $ref
  • computed -> $computed
  • shallowRef -> $shallowRef
  • customRef -> $customRef
  • toRef -> $toRef

当启用响应性语法糖时,这些宏函数都是全局可用的、无需手动导入。但如果你想让它更明显,你也可以选择从 vue/macros 中引入它们:

代码语言:txt复制
import { $ref } from 'vue/macros'

let count = $ref(0)

用 $() 将现存的 ref 转换为响应式对象

在某些场景中我们可能已经有了会返回 ref 的函数。然而,Vue 编译器并不能够提前知道该函数会返回一个 ref。那么此时可以使用 $() 宏来将现存的 ref 转换为响应式变量。

代码语言:txt复制
function myCreateRef() {
  return ref(0)
}

let count = $(myCreateRef())

Props 响应式

两个痛点

失去响应性

和 ref 一样,都得通过 .value ,为了保持响应性,通过 props.x 来访问 prop ,得到的变量将不是响应式的、也不会更新。

代码语言:txt复制
const props = defineProps({
    name: {
      type: String,
      default: () => '默认值'  
    },
    data:{
        type: Object,
        required: true
    }
})



//访问props


props.name
props.data

定义默认值笨拙

当使用基于类型的 props 的声明时,无法很方便地声明这些 prop 的默认值。为此我们提供了 withDefaults() 这个 API,但使用起来仍然很笨拙。

代码语言:txt复制
interface MyProps {
    phone: string | number,
    name ?: string,
    age : number | string

}
const props =  withDefaults(defineProps<MyProps>(),{
    name:'海军',
    phone: '123123123123'
})




props.name
props.phone

解决痛点

当 defineProps 与解构一起使用时,我们可以通过应用编译时转换来解决这些问题,类似于我们之前看到的 $()

代码语言:txt复制
interface MyProps {
    phone: string | number,
    name ?: string,
    age : number | string

}


const {
    name = '海军',
    phone = 1234567,
    age = 28,
} = defineProps<MyProps>()



watchEffect(() => {
    // 会在 props 变化时打印
    console.log(name, phone, age)
    //海军 1234567 22
})

函数间传递时的响应性

参数形式传入函数 保持响应式

当 一个函数期望接收一个 ref 对象为参数时,我们可以这样写

$$() 的效果就像是一个转义标识:$$() 中的响应式变量不会追加上 .value。

代码语言:txt复制
const contents = $ref('测试')

const changeContent = (ctx: Ref<String>) => {
    console.log(ctx.value)
}



watch($$(contents),(val)=> {
    
    changeContent($$(contents))
})

作为函数返回值

如果将响应式变量直接放在返回值表达式中会丢失掉响应性:$$() 可以直接用在要返回的对象上,$$() 调用时任何对响应式变量的引用都会保留为对相应 ref 的引用, 这样保留了响应性。

有点像React Hooks 。

代码语言:txt复制
const useWindowData = () => {
    const href = ref((window as Window).location.href)
    return $$({
        href
    })
}


const {href} = useWindowData()

开启响应性语法糖

默认是关闭的状态,需要你显式选择启用。此外,以下列出的所有配置都需要 vue@^3.2.25。

Vite

  • 需要 @vitejs/plugin-vue@>=2.0.0
  • 应用于 SFC 和 js(x)/ts(x) 文件。在执行转换之前,会对文件进行快速的使用检查,因此不使用宏的文件应该不会有性能损失。
  • 注意 reactivityTransform 现在是一个插件的顶层选项,而不再是位于 script.refSugar 之中了,因为它不仅仅只对 SFC 起效。
代码语言:txt复制
// vite.config.js
export default {
  plugins: [
    vue({
      reactivityTransform: true
    })
  ]
}

Vue-CLi

  • 目前仅对 SFC 起效
  • 需要 vue-loader@>=17.0.0
代码语言:txt复制
// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap((options) => {
        return {
          ...options,
          reactivityTransform: true
        }
      })
  }
}

Webpack Vue-loader

  • 目前仅对 SFC 起效
  • 需要 vue-loader@>=17.0.0
代码语言:txt复制
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader',
        options: {
          reactivityTransform: true
        }
      }
    ]
  }
}

0 人点赞