百科狗-知识改变命运!
--

响应性语法糖 - vue 进阶主题

乐乐1年前 (2023-11-21)阅读数 18#技术干货
文章标签是一个

响应性语法糖

实验性功能
响应性语法糖目前是一个实验性功能,默认是禁用的,需要显式选择使用。在最终确定前仍可能发生变化,你可以查看GitHub 上的提案与讨论来关注和跟进最新进展。组合式 API 特有
响应性语法糖是组合式 API 特有的功能,并且需要一个构建步骤。

ref vs.响应式变量

自从引入组合式 API 的概念以来,一个主要的未能解决的问题就是ref和响应式对象的使用方式。到处使用.value无疑是很繁琐的,并且在没有类型系统的帮助时很容易漏掉。

Vue 的响应性语法糖是一个编译时的转换过程,使我们可以像这样书写代码:

 {{ count }} 

这里的这个$ref()方法是一个编译时的宏命令:它不是一个真实的、在运行时会调用的方法。而是用作 Vue 编译器的标记,表明最终的count变量需要是一个响应式变量。

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

上面的代码将被编译成下面这样的运行时声明:

export default {
  props: {
    msg: { type: String, required: true },
    count: { type: Number, default: 1 },
    foo: String
  },
  setup(props) {
    watchEffect(() => {
      console.log(props.msg, props.count, props.foo)
    })
  }
}


保持在函数间的响应性

虽然响应式变量使我们可以不再受.value的困扰,但它也使得我们在函数间传递响应式变量时可能造成“响应性丢失”的问题。这可能在以下两种场景中出现:

以参数形式传入函数

假设有一个期望接收一个 ref 对象为参数的函数:

function trackChange(x: Ref) {
  watch(x, (x) => {
    console.log('x 改变了!')
  })
}

let count = $ref(0)
trackChange(count) // 无效

上面的例子不会正常工作,因为代码被编译成了这样:

let count = ref(0)
trackChange(count.value)

这里的count.value是以一个 number 类型值的形式传入,然而trackChange期望接收的是一个真正的 ref。要解决这个问题,可以在将count作为参数传入之前,用$$()包装:

let count = $ref(0)
- trackChange(count)
+ trackChange($$(count))

上面的代码将被编译成:

import { ref } from 'vue'

let count = ref(0)
trackChange(count)

我们可以看到,$$()的效果就像是一个转义标识:$$()中的响应式变量不会追加上.value


作为函数返回值

如果将响应式变量直接放在返回值表达式中回丢失掉响应性:

function useMouse() {
  let x = $ref(0)
  let y = $ref(0)

  // 监听 mousemove 事件
  // 不起效!
  return {
    x,
    y
  }
}

上面的语句将被翻译为:

return {
  x: x.value,
  y: y.value
}

为了保持响应性,我们需要返回的是真正的 ref,而不是返回时 ref 内的值。

我们还是可以使用$$()来结局。在这个例子中,$$()可以直接用在要返回的对象上,$$()调用时任何对响应式变量的引用都会保留为对相应 ref 的引用:

function useMouse() {
  let x = $ref(0)
  let y = $ref(0)

  // 监听 mousemove 事件
  // 修改后起效
  return $$({
    x,
    y
  })
}


在解构$$()使用

响应性语法糖 - vue 进阶主题

$$()可以用在对 props 的结构,因为它们也是响应式的变量。编译器会高效地通过toRef来做转换:

const { count } = defineProps()

passAsRef($$(count))

编译结果为:

setup(props) {
  const __props_count = toRef(props, 'count')
  passAsRef(__props_count)
}


TypeScript 集成

Vue 为这些宏函数都提供了类型声明(全局可用)并且类型都会符合你的使用预期。它与标准的 TypeScript 语义没有不兼容之处,因此它的语法可以与所有现有的工具兼容。

这也意味着这些宏函数在任何 JS / TS 文件中都是合法的,不是仅能在 Vue SFC 中使用。

因为这些宏函数都是全局可用的,它们的类型需要被显式地引用(例如,在env.d.ts文件中):

/// 

若你是从vue/macros中显式引入宏函数时,则不需要像这样全局声明了。


显式启用

响应性语法糖目前默认是关闭状态,需要你显式选择启用。此外,接下来的所有配置都需要vue@^3.2.25

Vite

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


vue-cli

  • 目前仅对 SFC 起效
  • 需要vue-loader@^17.0.0
// 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
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          reactivityTransform: true
        }
      }
    ]
  }
}

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)