很长一段时间以来,老有人私信跟我说,Vue 很先进,代表了未来,你不要沉迷在 React 这个年老色衰的技术栈里自娱自乐啦,睁开眼睛去看看世界吧,别的技术栈都发展早就比 React 更先进啦!!
然后这些人估计都不会相信,我真的有非常认真的去把 Vue 从头到尾学了个遍,甚至 Vue2、Vue3 的原理我也理清楚了... 很多年前,我甚至还在我的付费小册《JavaScript核心进阶》里,聊设计模式的时候,分享一个 Vue2 简易版的实现原理作为案例...
不仅如此,我还睁眼看了 rust 生态的 Leptos
,看了 Solid.js,看了 Svelte,还看了鸿蒙开发的 ArkUI,看了 Android 开发的 compose,这些都是近些年新出的技术方案,我都有认真的学过奥
也不知道这些喊我睁眼看世界的,有没有我看得多...
然后这篇文章,我就主要以吐槽 Vue 的语法设计为主,来聊一下我 Vue3 的学习和使用体会
一、ref 与 reactive
一个很不好的体验就是 ref 与 reactive 都太容易丢失响应了。为了防止丢失响应,我需要随时注意我的数据使用方式,我不能随心所欲的按照 JavaScript 的基础语法去任意妄为。
首先严格践行语义化的我,第一反应是不想使用 ref。因为 ref 是 引用的缩写,从语义上来说,他是不应该具备响应性的。但是偏偏 Vue3 的语法设计就没这么讲究,于是我的语义化思维,在我学习和使用响应式数据时给我造成了极大的困扰...
我刚开始在项目中,就偏好于使用 reactive
。但是现实很快就把我的偏好捏碎了。比如下面这个例子,我将一个列表作为响应性数据定义在 reactive
中
let data = reactive([])
但是我万万没想到的是,这样使用是有问题的。因为当我从接口里面获得一个新数据的时候,想要直接用新的列表覆盖初始列表,结果居然没有什么好的办法能让这种覆盖生效!!!
代码语言:javascript复制// 接口请求成功之后执行,数据失去响应
data = result.data
然后我就只能这么写
代码语言:javascript复制// 定义
const res = reactive({ list: [] })
// 接口请求成功之后调用
res.list = result.data
我这个组件只有一个响应式数据的时候,就贼难受,所以我就想着法又加了一个,这样就舒服一点了
代码语言:javascript复制const res = reactive({
list: [],
show: false
})
这样处理之后呢,我想着用的时候,就很自然的想着用解构语法来使用吧。但是呢,响应性又丢失了...
这样写不行
代码语言:javascript复制const {lsit, show} = reactive({
list: [],
show: false
})
这样也不行
代码语言:javascript复制const data = reactive({
list: [],
show: false
})
const {list, show} = data
必须要引入一个新的 api 来解决这个问题 toRefs
let data = reactive({
list: [],
open: false
})
const {list, open} = toRefs(data)
然后我就含泪看着我的 reactive 被强行变成了 ref. 这其实我还勉强可以接受,最令我崩溃的是,由于 list 和 open 都被转化成 ref,因此使用的时候,我必须这样用,把 .value
的尾巴加回来...
list.value = [
{message: 'hello'},
{message: 'world'}
]
所以 reactive
一个符合语义的响应式 api,给我的使用感受就是,在设计上就是非常失败。但是一个不太符合语义的的响应式 api ref
被处理得还相对好一些。在这样的情况之下,也就不得不更多的在项目中使用 ref
。
但是使用 ref 的时候,除了不符合语义化之外,还不符合一致性。因为在 script
中使用,我们必须加上 .value
来处理。但是呢,template
中又不用... 甚至如果我为了一致性在模板中用了还会出问题...
直接给我一直以来自认为良好的编码标准干碎了...
所以我现在的用法是,使用 reactive
,但是忍痛放弃解构,从而避免使用 toRefs
。尽量避免使用 ref
。虽然很多人发文章说官方强烈建议使用 ref
,但是确实语义和一致性有点挑战我的底线。当然我也知道他在能力上处理得更好一些。
二、传参设计得真复杂
从一个新手的角度,要理解 Vue3 的参数传递,居然是一件学习成本非常高的事情。原因就是因为为了确保响应性和区分普通参数,这里又设计了许多新的 api 来解决问题
首先是参数的类型很复杂。
因为 Vue 中设计了一个指令系统,用于处理一些条件渲染的逻辑。比如 v-if
<h1 v-if="awesome">Vue is awesome!</h1>
等价于
代码语言:javascript复制awesome && <h1>Vue is awesome!</h1>
但是这个机制就由此就导致了在父组件使用自定义组件时,往子组件传参就变得非常复杂。因为在子组件内部就没办法统一接收属性参数了。因为有的属性呢,他是自定义指令,是不应该往下传的,但是有的指令呢,又需要往下传
例如事件回调
代码语言:javascript复制<button v-on:click="greet">Greet</button>
这个时候我们在学习的时候,就必须的保证区分如下几种传参
一种就是有特殊含义的内置指令或者自定义指令
代码语言:javascript复制<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no