乐趣区

关于前端:Vue3又出新语法-到底何时才能折腾完

前言

大家应该晓得如果用 Vue3Composition API 定义一个响应式变量通常有两种模式,一种是用ref,另一种是reactive

<script setup>
import {ref, reactive} from 'vue'

const isLoading = ref(true)

const user = reactive({
  name: '令狐冲',
  age: 22,
  gender: '男'
})
</script>

一般来说定义一个根本数据类型会用 ref,而援用类型则会采纳reactive,那么问题来了,ref 尽管定义了一个根本数据类型,但实际上它却是一个援用类型,取值和赋值时必须要带上 .value 属性:

<script setup>
import {ref} from 'vue'

const isLoading = ref(true)

if (isLoading.value) {isLoading.value = false}
</script>

这就有点不太合乎直觉了,很有可能一不小心就被写成了这样:

<script setup>
import {ref} from 'vue'

let isLoading = ref(true)

if (isLoading) {isLoading = false}
</script>

这要是有 TSESLint的加持还好,要是没有的话可就不好找谬误了,也不会产生什么有用的报错信息,而且每次都要带上这个 .value 切实是不难看,而且写起来也麻烦呀!

reactive的弊病是不能解构,解构就会失去响应性:

<script setup>
import {reactive} from 'vue'

const user = reactive({
  name: '令狐冲',
  age: 22,
  gender: '男'
})

// 这种写法通常达不到预期的成果
let {age} = user
age = 18
</script>

可能有人会说,不是有 toRefs 吗?用了 toRefs,就又会回到那个.value 的问题上了:

<script setup>
import {reactive, toRefs} from 'vue'

const user = reactive({
  name: '令狐冲',
  age: 22,
  gender: '男'
})

let {age} = toRefs(user)
age.value = 18
</script>

其实我集体感觉还好啦,因为曾经写习惯了,再加上始终用 TS 有提醒和主动补全,所以感觉没什么问题。

但知乎上相似于《为什么 vue3 删不掉 ref() 这样冗余的函数,但 svelte 能够?》这种问题深深的刺痛了大佬的心田,大佬本人的强迫症也犯了,毕竟他当年发明 Vue 的最胜利因素之一就是 不便 。而现在这种冗余的写法却与 不便 毫不搭边儿,所以尤大无论如何也必须要解决这个问题,不能让人背地嚼耳根子说 Vue 写起来还没 Svelte 不便是不是?于是乎大佬先后创立了三次不同的语法糖,它们别离是:

  • [《[译]尤雨溪: Ref 语法糖提案》](https://juejin.cn/post/689460…)
  • 《Vue 第二波 ref 语法提案来袭 这次会进入到规范吗?》
  • 本文 (第二波语法糖的修改版)

咱们先来简略的看一下,这三次语法糖的写法:

第一波语法糖

第一波次要是模拟了 Svelte 的写法,咱们先来看看 Svelte 的中武官网给进去的一段例子:

<script>
export let title;

// 这将在“title”的 prop 属性更改时更新“document.title”$: document.title = title;

$: {console.log(`multiple statements can be combined`);
  console.log(`the current title is ${title}`);
}
</script>

这个 $: 是一种叫做 label 的语法,这种语法并不是 Svelte 借鉴的语法,而是一种长期在被废除的边缘上疯狂试探的非法语法,只不过这种语法本来并不是这么用的,人家是用在嵌套循环上的:

let num = 0

outermost:
for (let i = 0; i < 10; i++) {for (let j = 0; j < 10; j++) {if (i == 5 && j == 5) {continue outermost} else {console.log(i, j, 88)
        }
        num++
    }
}

console.log(num) //95

看不懂没关系啊,也没必要去弄懂这种语法,因为它不够直观,用途也不是很大,所以简直没什么人用它!我在编辑器写这段代码的时候 ESLint 都直报错:

翻译:Label语法源于 GOTO 语句,应用它将会令代码变得难以了解、难以保护。—ESLint

不过既然没什么人在用,同时它还是 JS 的非法语法,那用它来通知编译器这里是申明了一个 ref 变量岂不是很完满?于是乎尤大也搞了个和 Svelte 相似的语法:

<script setup>
ref: isLoading = true

if (isLoading) {isLoading = false}
</script>

那么大家为何会如此拥护呢?就是因为 label 语法压根儿就不是这么用的,人家本来是为了和 breakcontinue 配合应用的,尽管在别的中央用也不算是语法错误,但你这么做显著是批改了 JS 本来的语意!尽管尤大示意很不服啊:为什么 Svelte 用这玩意你们都没说啥,我一用这玩意你们就开喷?!

个人感觉是因为 Svelte 从一开始就说本人是一个编译器,没有惨重的历史包袱,而 Vue 却恰恰相反。而且 Svelte 自身也不是什么支流框架,属于给那帮爱折腾的人玩的。但 Vue 不一样,曾经有多少人要靠着 Vue 吃饭呢,并不是所有人都那么爱折腾的。

于是在万般无奈之下,尤大只好放弃了这个提案,但这件事在尤大心里始终还是挥之不去、如鲠在喉,于是乎他汲取了第一波语法糖的教学,卷头重来又起草了一份新提案:

第二波语法糖

<script setup>
let loading = $ref(true)

if (loading) {loading = false}
</script>

能够看到咱们并没有引入 $ref 这个变量,这个变量是从哪来的的呢?是只有在 <script> 标签里写了 setup 这个属性就会主动注入的一个全局变量(须要先开启实验性语法开关

尤大心想:你们不是嫌我之前用了不标准的语法么?那我这回这么写应该没问题了吧!想想之前咱们定义一个 ref 变量,首先须要先把 ref 引进来而后能力用:

import {ref} from 'vue'

const loading = ref(true)

而新语法不必引,间接就能用,相似于全局变量的感觉。除了 $ref 这个非凡的全局变量呢,这次提案还有:$computed$fromRefs$raw 这几个玩意。咱们一个个来看,先看$computed

<!-- 以前 -->
<script setup>
import {ref, computed} from 'vue'

const num = ref(1)
const num_10 = computed(() => num.value * 10)
</script>

<!-- 当初 -->
<script setup>
let num = $ref(1)
const num_10 = $computed(() => num * 10)
</script>

$fromRefs又是个啥呢?这玩意在之前没有啊!只据说过toRefs

<script setup>
import {fromRefs} from 'vue' // 这个 API 并不存在
import {toRefs} from 'vue' // 这个 API 倒是有 也就是只有 to 没有 from
</script>

其实这个 $fromRefs 正是为了配合 toRefs 而产生的,比方说咱们在别的中央写了一个useXxx

import {reactive} from 'vue'

const state = reactive({
    x: 0,
    y: 0
})

export default = (x = 0, y = 0) => {
    state.x = x
    state.y = y
    
    return toRefs(state)
}

于是咱们在应用的时候就:

<script setup>
import {useXxx} form '../useXxx.js'

const {x, y} = useXxx(100, 200)

console.log(x.value, y.value)
</script>

这岂不是又要呈现尤大最不想看到的 .value 属性了吗?所以 $fromRefs 就是为了解决这个问题而生的:

<script setup>
import {useXxx} form '../useXxx.js'

const {x, y} = $fromRefs(useXxx(100, 200))

console.log(x, y)
</script>

最初一个 API 就是 $raw 了,raw 不是原始的意思嘛!那么看名字也能猜到,就是咱们用 $ref 所创立进去的其实是一个响应式 对象 ,而不是一个根本数据类型,但语法糖会让咱们在应用的过程中像是在用根本数据类型那样能够改来改去,但有时咱们想看看这个 对象 长什么样,那么咱们就须要用到 $raw 了:

<script setup>
const loading = $ref(true)

console.log(loading) // 其实打印的不是 loading 这个对象 而是它外面的值 相当于 loading.value
console.log($raw(loading)) // 这回打印的就是 loading 这个对象了
</script>

改进版

这一版语法糖没过多久就又被改良了,改进版次要是把全局变量改为只有 $$$这俩变量了,如果咱们不必语法糖时是这么写:

<script setup>
import {ref} from 'vue'

const loading = ref(true)

console.log(loading.value)
</script>

用语法糖当前就变成了这样:

<script setup>
import {ref} from 'vue'

const loading = $(ref(true))

console.log(loading)
</script>

如果咱们想还原 loading 这个变量,咱们就须要用到 $$ 了:

<script setup>
import {ref} from 'vue'

let loading = $(ref(true))

console.log($$(loading))
</script>

或者也能够写成这样:

<script setup>
import {ref} from 'vue'

const loadingRef = ref(true)
let loading = $(loadingRef)

console.log(loadingRef === $$(loading))
</script>

第三波语法糖

第三波语法糖次要是在第二波语法的根底上又进行了改良,除了许多人感觉要写成 $(ref()) 的话切实是太那什么了…

另一方面则是实现了 props 的语法糖,新的语法次要是为每个可能创立带有 .value 变量的办法都有一个 $ 前缀的等价物,比方:

  • ref
  • computed
  • shallowRef
  • customRef
  • toRef

与此同时保留了改进版中的 $ 变量与 $$ 变量,用于对 props 的解构:

<script setup>
const {isLoading} = $(defineProps({ isLoading: Boolean}))
</script>

要晓得在以前咱们是不能对 props 进行解构的,而当初还能够利用 ES6 的解构默认值写法来为 props 设置默认值:

<!-- 以前 -->
<script setup>
const props = defineProps({
  isLoading: {
    type: Boolean,
    default: true
  }
}))

console.log(props.isLoading)
</script>

<!-- 当初 -->
<script setup>
const {isLoading = true} = $(defineProps({ isLoading: Boolean}))

console.log(isLoading)
</script>

三波语法糖提案地址

  • 第一波:https://github.com/vuejs/rfcs/pull/228
  • 第二波:https://github.com/vuejs/rfcs/discussions/369
  • 第三波:https://github.com/vuejs/rfcs/discussions/413

这个框架明明是中国人用的最多,但可笑的是竟然是一群外国人在磋商 Vue 的下一步打算,看到这里必定有人会说:中国人都忙着 996 呢,哪有空去探讨那些货色…

那就看你是感觉:这些乌七八糟的语法糖对你来说无所谓,出什么语法我学什么就是了,我就是一只 缄默的羔羊

还是说:你只是在这篇文章的上面留个言说本人喜爱这些新语法或者厌恶这些新语法,懒得去 GitHub 说英文。

链接曾经给大家贴上来了,就看大家是一副凑热闹的态度,还是点进去链接怯懦的表白出本人的声音了。当然,如果去 GitHub 咱们还是要说英文的,虽说用中文的话尤大也能够看得懂,但评论区不全是中国人,Vue还是有相当一批外国粉丝的。而且也不全是美国人,那些不是英国人美国人的开发者,他们如果也只图本人畅快而说本人国家的母语的话,想必咱们就没有方法进行沟通了,同时这也会进一步拉近国人在海内的形象:他人都用英文,就你们中国人用本人的语言,不遵守规则。

那可能有人英文程度真的很差,咱们能够这样嘛:找到百度翻译,输出中文后翻译成英文,而后再把英文复制过来。尽管这样做翻译的可能不齐全精确,但最起码能达到勉强看懂的境地。同时还有一个技巧就是把翻译成英文的句子再翻译回中文,看看有哪些地方的语意产生了显著的变动,咱们再针对那个中央从新本人写一遍。

如果你喜爱这个语法,那就去多点几个赞多夸几句,这样的话想必它很快就会被纳入到 Vue 的规范语法外面去。

如果你不喜爱,那么就赶快去多喷几句,这样的话这个语法很有可能就会像第一波语法糖提案那样被放弃掉了。

如果你感觉无所谓,爱什么样什么样,去 GitHub 一趟多麻烦啊,间接在这篇文章下发表评论多不便。那么也欢送你在评论区下留言。

本文首发于公众号:前端学不动

退出移动版