当前位置:   article > 正文

Vue3.X中 ref属性剖析(一看就懂)_reftransform

reftransform

Vue3中的setup有什么优缺点?

诱因

首先 setup 函数有冗长返回语句的问题已经在

至于我们为什么要用 setup,我想既然同学们要了解 Vue 3,应该对这个大版本要做和已经的事情有一个更全面的认知:

Vue 3 的任务是:补短板 + 提上限

短板何在?

在今天我们看到的大部分较大的国内互联网公司所提供的 Web 产品中,使用的构建框架都是 React,我自己是字节,而我惊奇的发现 VueConf 2021 上这么多位分享的大佬竟是出自我们这个用 React 更多的公司,有同学玩笑戏称“ 字节把懂 Vue 的都抓去写 React 了 ”。

而我之前在腾讯微信支付数据中心实习过一小段时间,那里有一些内部平台系统为了开发快速简单,选择了容易上手的 Vue,但普遍项目量级都还不算特别大。

一个令人好奇的,与此相关的问题出现了:为什么大公司不敢用 Vue ?

据我自己的体验来看,可以分为以下两点,这应该就是 Vue 过去的短板:

  1. 2.x 版本对 TypeScript 的支持是硬伤,而 TypeScript 对大型项目的保障能力是被普遍认可的,看到 Vue 没法支持,在选择技术栈时很容易放弃它。
  2. 一旦项目体量变大,Vue 的代码会变得更难以维护,真正在实践中 Options API 虽然在组件层面上,每个内容的职责都很清晰,这是 data,那是 method,但是从跨组件的角度来看就没那么好了,因此 Vue 2 缺少一种真正更好的抽象逻辑的办法,而非将代码搬到独立的 .js 文件里来缩减 SFC 代码行数。

解决这两个问题的思路也很明确,尤大也在很多视频演讲中提到了:

  1. 函数是对类型最友好的,输入、输出的类型都是确定的,易推导的。若要这样做,显然是用 TypeScript 重写一遍 Vue 更好。
  2. 虽然我们的页面内容被划分成了一个个的组件,但是我们思考的逻辑却不应该僵死地被他们框住,不需要在每个组件中罗列他们本身的职责,而是让组件去适配、去载入我们可多处复用的逻辑。Vue 需要为开发者提供一套新的框架内 API,使得程序员们能创造出更容易复用的 “ 业务 API ”。若要这样做,显然需要一套新的函数式响应式 API,这些东西将成为我们书写业务逻辑的 “原语”

这些更新内容都在 Vue 3 中完成了,通过一些社区中新的插件对 TypeScript 集成,我们还获得了更好的 DX 体验。

总结

所以请题主莫要纠结于 setup 本身,因为把所有逻辑不加抽象、不加简化地都写在它其中,本就是不对的。setup 只是为我上面说的 组件载入逻辑 提供了一个入口,而你不应该把所有东西都摆在门口。

Options API 对一些小的,功能简单纯粹且独特的组件还是能用上的。另外如果你喜欢它,Vue 也没有删掉对其的支持。

Vue3 Ref 语法糖,告别 .value 的写法

Vue3 提了一个 Ref Sugar 的 RFC,即 ref 语法糖,目前还处理实验性的(Experimental)阶段。在 RFC 的动机(Motivation)中,Evan You 介绍到在 Composition API 引入后,一个主要未解决的问题是 refsreactive 对象的使用。而到处使用 .value 可能会很麻烦,如果在没使用类型系统的情况下,也会很容易错过:

let count = ref(1)

function add() {
   
 count.value++
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以,一些用户会更倾向于只使用 reactive,这样就不用处理使用 refs.value 问题。而 ref 语法糖的作用是让我们在使用 ref 创建响应式的变量时,可以直接获取和更改变量本身,而不是使用 .value 来获取和更改对应的值。简单的说,站在使用层面,我们可以告别使用 refs 时的 .value 问题:

let count = $ref(1)

function add() {
   
 count++
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

那么,ref 语法糖目前要怎么在项目中使用?它又是怎么实现的?这是我第一眼看到这个 RFC 建立的疑问,相信这也是很多同学持有的疑问。所以,下面让我们来一一揭晓。

1 Ref 语法糖在项目中的使用

由于 ref 语法糖目前还处于实验性的(Experimental)阶段,所以在 Vue3 中不会默认支持 ref 语法糖。那么,这里我们以使用 Vite + Vue3 项目开发为例,看一下如何开启对 ref 语法糖的支持。

在使用 Vite + Vue3 项目开发时,是由 @vitejs/plugin-vue 插件来实现对 .vue 文件的代码转换(Transform)、热更新(HMR)等。所以,我们需要在 vite.config.js 中给 @vitejs/plugin-vue 插件的选项(Options)传入 refTransform: true

// vite.config.js
import {
    defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
   
  plugins: [vue({
   
    refTransform: true
  })]
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

那么,这样一来 @vitejs/plugin-vue 插件内部会根据传入的选项中 refTransform 的值判断是否需要对 ref 语法糖进行特定的代码转换。由于,这里我们设置的是 true,显然它是会对 ref 语法糖执行特定的代码转换。

接着,我们就可以在 .vue 文件中使用 ref 语法糖,这里我们看一个简单的例子:

<template>
 <div>{
   {
   count}}</div>
 <button @click="add">click me</button>
</template>

<script setup>
let count = $ref(1)

function add() {
   
 count++
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

对应渲染到页面上:

img

可以看到,我们可以使用 ref 语法糖的方式创建响应式的变量,而不用思考使用的时候要加 .value 的问题。此外,ref 语法糖还支持其他的写法,个人比较推荐的是这里介绍的 $ref 的方式,有兴趣的同学可以去 RFC 上了解其他的写法。

那么,在了解完 ref 语法糖在项目中的使用后,我们算是解答了第一个疑问(怎么在项目中使用)。下面,我们来解答第二个疑问,它又是怎么实现的,也就是在源码中做了哪些处理?

2 Ref 语法糖的实现

首先,我们通过 Vue Playground 来直观地感受一下,前面使用 ref 语法糖的例子中的 <script setup> 块(Block)在编译后的结果:

import {
    ref as _ref } from 'vue'

const __sfc__ = {
   
  setup(__props) {
   
  let count = _ref(1)

  function add() {
   
    count.value++
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到,虽然我们在使用 ref 语法糖的时候不需要处理 .value,但是它经过编译后仍然是使用的 .value。那么,这个过程肯定不难免要做很多编译相关的代码转换处理。因为,我们需要找到使用 $ref 的声明语句和变量,给前者重写为 _ref,给后者添加 .value

而在前面,我们也提及 @vitejs/plugin-vue 插件会对 .vue 文件进行代码的转换,这个过程则是使用的 Vue3 提供的 @vue/compiler-sfc 包(Package),它分别提供了对 <script><template><style> 等块的编译相关的函数。

那么,显然这里我们需要关注的是 <script> 块编译相关的函数,这对应的是 @vue/compiler-sfc 中的 compileScript() 函数。

2.1 compileScript() 函数

compileScript() 函数定义在 vue-nextpackages/compiler-sfc/src/compileScript.ts 文件中,它主要负责对 <script><script setup> 块内容的编译处理,它会接收 2 个参数:

  • sfc 包含 .vue 文件的代码被解析后的内容,包含 scriptscriptSetupsource 等属性
  • options 包含一些可选和必须的属性,例如组件对应的 scopeId 会作为 options.id、前面提及的 refTransform

compileScript() 函数的定义(伪代码):

// packages/compiler-sfc/src/compileScript.ts
export function 
  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/871955
推荐阅读
相关标签
  

闽ICP备14008679号