【前沿技术】Vue 3.0

简介: 【前沿技术】Vue 3.0

1. 引言

Vue 3.0 的发布引起了轩然,让我们解读下它的 function api RFC 详细了解一下 Vue 团队是怎么想的吧!

首先官方回答了几个最受关注的问题:

Vue 3.0 是否有 break change,就像 Python 3 / Angular 2 一样?

不,100% 兼容 Vue 2.0,且暂未打算废弃任何 API(未来也不)。之前有草案试图这么做,但由于用户反馈太猛,被撤回了。

Vue 3.0 的设计盖棺定论了吗?

没有呀,这次精读的稿子就是 RFC(Request For Comments),翻译成中文就是 “意见征求稿”,还在征求大家意见中哦。

这 RFC 咋这么复杂?

RFC 是写给贡献者/维护者的,要考虑许多边界情况与细节,所以当然会复杂很多喽!当然 Vue 本身使用起来还是很简单的。

Vue 本身 Mutable + Template 就注定了是个用起来简单(约定 + 自然),实现起来复杂(解析 + 双绑)的框架。

这次改动很像在模仿 React,为啥不直接用 React?

首先 Template 机制还是没变,其次模仿的是 Hooks 而不是 React 全部,如果你不喜欢这个改动,那你更不会喜欢用 React。

PS: 问这个问题的人,一定没有同时理解 React 与 Vue,其实这两个框架到现在差别蛮大的,后面精读会详细说明。

下面正式进入 Vue 3.0 Function API 的介绍。

2. 概述

Vue 函数式基本 Demo:

1. <template>
2. <div>
3. <span>count is {{ count }}</span>
4. <span>plusOne is {{ plusOne }}</span>
5. <button @click="increment">count++</button>
6. </div>
7. </template>
8. 
9. <script>
10. import { value, computed, watch, onMounted } from 'vue'
11. 
12. export default {
13.   setup() {
14. // reactive state
15.     const count = value(0)
16. // computed state
17.     const plusOne = computed(() => count.value + 1)
18. // method
19.     const increment = () => { count.value++ }
20. // watch
21.     watch(() => count.value * 2, val => {
22.       console.log(`count * 2 is ${val}`)
23.     })
24. // lifecycle
25.     onMounted(() => {
26.       console.log(`mounted`)
27.     })
28. // expose bindings on render context
29. return {
30. count,
31.       plusOne,
32.       increment
33.     }
34.   }
35. }
36. </script>

函数式风格的入口是 函数,采用了函数式风格后可以享受如下好处:类型自动推导、减少打包体积。setup

setup函数返回值就是注入到页面模版的变量。我们也可以返回一个函数,通过使用 这个 API 产生属性并修改:value

1. import { value } from 'vue'
2. 
3. const MyComponent = {
4.   setup(props) {
5.     const msg = value('hello')
6.     const appendName = () => {
7.       msg.value = `hello ${props.name}`
8.     }
9. return {
10.       msg,
11.       appendName
12.     }
13.   },
14.   template: `<div @click="appendName">{{ msg }}</div>`
15. }

要注意的是, 返回的是一个对象,通过 才能访问到其真实值。value().value

为何 返回的是 Wrappers 而非具体值呢?原因是 Vue 采用双向绑定,只有对象形式访问值才能保证访问到的是最终值,这一点类似 React 的 API 的 规则。value()useRef().current

那既然所有 返回的值都是 Wrapper,那直接给模版使用时要不要调用 呢?答案是否定的,直接使用即可,模版会自动 Unwrapping:value().value

1. const MyComponent = {
2.   setup() {
3. return {
4. count: value(0)
5.     }
6.   },
7.   template: `<button @click="count++">{{ count }}</button>`
8. }

接下来是 Hooks,下面是一个使用 Hooks 实现获得鼠标实时位置的例子:

1. function useMouse() {
2.   const x = value(0)
3.   const y = value(0)
4.   const update = e => {
5.     x.value = e.pageX
6.     y.value = e.pageY
7.   }
8.   onMounted(() => {
9.     window.addEventListener('mousemove', update)
10.   })
11.   onUnmounted(() => {
12.     window.removeEventListener('mousemove', update)
13.   })
14. return { x, y }
15. }
16. 
17. // in consuming component
18. const Component = {
19.   setup() {
20.     const { x, y } = useMouse()
21.     const { z } = useOtherLogic()
22. return { x, y, z }
23.   },
24.   template: `<div>{{ x }} {{ y }} {{ z }}</div>`
25. }

可以看到, 将所有与 “处理鼠标位置” 相关的逻辑都封装了进去,乍一看与 React Hooks 很像,但是有两个区别:useMouse

  1. useMouse 函数内改变 、 后,不会重新触发 执行。xysetup
  2. xy 拿到的都是 Wrapper 而不是原始值,且这个值会动态变化。

另一个重要 API 就是 watch,它的作用类似 React Hooks 的 useEffect,但实现原理和调用时机其实完全不一样。

watch 的目的是监听某些变量变化后执行逻辑,比如当 变化后重新取数:id

1. const MyComponent = {
2.   props: {
3.     id: Number
4.   },
5.   setup(props) {
6.     const data = value(null)
7.     watch(() => props.id, async (id) => {
8. data.value = await fetchData(id)
9.     })
10.   }
11. }

之所以要 ,因为在 Vue 中, 函数仅执行一次,所以不像 React Function Component,每次组件 变化都会重新执行,因此无论是在变量、 变化时如果想做一些事情,都需要包裹在 中。watchsetuppropspropswatch

后面还有 、生命周期函数、依赖注入,都是一些语法定义,感兴趣可以继续阅读原文,笔者就不赘述了。unwatching

3. 精读

对于 Vue 3.0 的 Function API + Hooks 与 React Function Component + Hooks,笔者做一些对比。

Vue 与 React 逻辑结构

React Function Component 与 Hooks,虽然在实现原理上,与 Vue3.0 存在 Immutable 与 Mutable、JSX 与 Template 的区别,但逻辑理解上有着相通之处。

1. const MyComponent = {
2.   setup(props) {
3.     const x = value(0)
4. 
5.     const setXRandom = () => {
6.       x.value = Math.random()
7.     }
8. 
9. return { x, setXRandom }
10.   },
11.   template: `
12. <button @onClick="setXRandom"/>{{x}}</button>
13.   `
14. }

虽然在 Vue 中, 函数仅执行一次,看上去与 React 函数完全不一样(React 函数每次都执行),但其实 Vue 将渲染层(Template)与数据层(setup)分开了,而 React 合在了一起。setup

我们可以利用 React Hooks 将数据层与渲染层完全隔离:

1. // 类似 vue 的 setup 函数
2. function useMyComponentSetup(props) {
3.   const [x, setX] = useState(0)
4. 
5.   const setXRandom = useCallback(() => {
6.     setX(Math.random())
7.   }, [setX])
8. 
9. return { x, setXRandom }
10. }
11. 
12. // 类似 vue 的 template 函数
13. function MyComponent(props: { name: String }) {
14.   const { x, setXRandom } = useMyComponentSetup(props)
15. 
16. return (
17. <button onClick={setXRandom}>{x}</button>
18.   )
19. }

这源于 JSX 与 Template 的根本区别。JSX 使模版与 JS 可以写在一起,因此数据层与渲染层可以耦合在一起写(也可以拆分),但 Vue 采取的 Template 思路使数据层强制分离了,这也使代码分层更清晰了。

而实际上 Vue3.0 的 函数也是可选的,再配合其支持的 TSX 功能,与 React 真的只有 Mutable 的区别了:setup

1. // 这是个 Vue 组件
2. const MyComponent = createComponent((props: { msg: string }) => {
3. return () => h('div', props.msg)
4. })

我们很难评价 Template 与 JSX 的好坏,但为了更透彻的理解 Vue 与 React,需要抛开 JSX&Template,Mutable&Immutable 去看,其实去掉这两个框架无关的技术选型,React@16 与 Vue@3 已经非常像了。

Vue3.0 的精髓是学习了 React Hooks 概念,因此正好可以用 Hooks 在 React 中模拟 Vue 的 setup 函数。

关于这两套技术选型,已经是相对完美的组合,不建议在 JSX 中再实现类似 Mutable + JSX 的花样来(因为喜欢 Mutable 可以用 Vue 呀):

  • Vue:Mutable + Template
  • React:Immutable + JSX

真正影响编码习惯的就是 Mutable 与 Immutable,使用 Vue 就坚定使用 Mutable,使用 React 就坚定使用 Immutable,这样能最大程度发挥两套框架的价值。

Vue Hooks 与 React Hooks 的差异

先看 React Hooks 的简单语法:

const [ count, setCount ] = useState(0)


const setToOne = () => setCount(1)

Vue Hooks 的简单语法:

const count = value(0)


const setToOne = () => count.value = 1

之所以 React 返回的 是一个数字,是因为 Immutable 规则,而 Vue 返回的 是个对象,拥有 属性,也是因为 Vue Mutable 规则导致,这使得 Vue 定义的所有变量都类似 React 中 定义变量,因此不存 React 的特性。countcountcount.valueuseRefcapture value

关于 capture value 更多信息,可以阅读 精读《Function VS Class 组件》 Capute Value 介绍

另外,对于 Hooks 的值变更机制也不同,我们看 Vue 的代码:

1. const Component = {
2.   setup() {
3.     const { x, y } = useMouse()
4.     const { z } = useOtherLogic()
5.     return { x, y, z }
6.   },
7.   template: `<div>{{ x }} {{ y }} {{ z }}</div>`
8. }

由于 函数仅执行一次,怎么做到当 导致 、 值变化时,可以在 中拿到最新的值?setupuseMousexysetup

在 React 中, 如果修改了 的值,那么使用 的函数就会被重新执行,以此拿到最新的 ,而在 Vue 中,将 Hooks 与 Mutable 深度结合,通过包装 ,使得当 变更时,引用保持不变,仅值发生了变化。所以 Vue 利用 Proxy 监听机制,可以做到 函数不重新执行,但 Template 重新渲染的效果。useMousexuseMousexx.valuexsetup

这就是 Mutable 的好处,Vue Hooks 中,不需要 等机制,仅需一个 函数,直观的 Mutable 修改,就可以实现 React 中一套 Immutable 性能优化后的效果,这个是 Mutable 的魅力所在。useMemouseCallbackuseRefvalue

Vue Hooks 的优势

笔者对 RFC 中对 Vue、React Hooks 的对比做一个延展解释:

首先最大的不同: 仅执行一遍,而 React Function Component 每次渲染都会执行。setup

Vue 的代码使用更符合 JS 直觉。

这句话直截了当戳中了 JS 软肋,JS 并非是针对 Immutable 设计的语言,所以 Mutable 写法非常自然,而 Immutable 的写法就比较别扭。

当 Hooks 要更新值时,Vue 只要用等于号赋值即可,而 React Hooks 需要调用赋值函数,当对象类型复杂时,还需借助第三方库才能保证进行了正确的 Immutable 更新。

对 Hooks 使用顺序无要求,而且可以放在条件语句里。

对 React Hooks 而言,调用必须放在最前面,而且不能被包含在条件语句里,这是因为 React Hooks 采用下标方式寻找状态,一旦位置不对或者 Hooks 放在了条件中,就无法正确找到对应位置的值。

而 Vue Function API 中的 Hooks 可以放在任意位置、任意命名、被条件语句任意包裹的,因为其并不会触发 的更新,只在需要的时候更新自己的引用值即可,而 Template 的重渲染则完全继承 Vue 2.0 的依赖收集机制,它不管值来自哪里,只要用到的值变了,就可以重新渲染了。setup

不会再每次渲染重复调用,减少 GC 压力。

这确实是 React Hooks 的一个问题,所有 Hooks 都在渲染闭包中执行,每次重渲染都有一定性能压力,而且频繁的渲染会带来许多闭包,虽然可以依赖 GC 机制回收,但会给 GC 带来不小的压力。

而 Vue Hooks 只有一个引用,所以存储的内容就非常精简,也就是占用内存小,而且当值变化时,也不会重新触发 的执行,所以确实不会造成 GC 压力。setup

必须要总包裹 useCallback 函数确保不让子元素频繁重渲染。

React Hooks 有一个问题,就是完全依赖 Immutable 属性。而在 Function Component 内部创建函数时,每次都会创建一个全新的对象,这个对象如果传给子组件,必然导致子组件无法做性能优化。 因此 React 采取了 作为优化方案:useCallback

const fn = useCallback(() => /* .. */, [])

只有当第二个依赖参数变化时才返回新引用。但第二个依赖参数需要 lint 工具确保依赖总是正确的(关于为何要对依赖诚实,感兴趣可以移步 精读《Function Component 入门》 - 永远对依赖诚实)。

回到 Vue 3.0,由于 仅执行一次,因此函数本身只会创建一次,不存在多实例问题,不需要 的概念,更不需要使用 lint 插件 保证依赖书写正确,这对开发者是实实在在的友好。setupuseCallback

不需要使用 useEffect useMemo 等进行性能优化,所有性能优化都是自动的。

这也是实在话,毕竟 Mutable + 依赖自动收集就可以做到最小粒度的精确更新,根本不会触发不必要的 Rerender,因此 这个概念也不需要了。useMemo

而 也需要传递第二个参数 “依赖项”,在 Vue 中根本不需要传递 “依赖项”,所以也不会存在用户不小心传错的问题,更不需要像 React 写一个 lint 插件保证依赖的正确性。(这也是笔者想对 React Hooks 吐槽的点,React 团队如何保障每个人都安装了 lint?就算装了 lint,如果 IDE 有 BUG,导致没有生效,随时可能写出依赖不正确的 “危险代码”,造成比如死循环等严重后果)useEffect

4. 总结

通过对比 Vue Hooks 与 React Hooks 可以发现,Vue 3.0 将 Mutable 特性完美与 Hooks 结合,规避了一些 React Hooks 的硬伤。所以我们可以说 Vue 借鉴了 React Hooks 的思想,但创造出来的确实一个更精美的艺术品。

但 React Hooks 遵循的 Immutable 也有好的一面,就是每次渲染中状态被稳定的固化下来了,不用担心状态突然变更带来的影响(其实反而要注意状态用不变更带来的影响),对于数据记录、程序运行的稳定性都有较高的可预期性。

最后,对于喜欢 Mutable 的开发者,Vue 3.0 是你的最佳选择,基于 React + Mutable 搞的一些小轮子做到顶级可能还不如 Vue 3.0。对于 React 开发者来说,坚持你们的 Immutable 信仰吧,Vue 3.0 已经将 Mutable 发挥到极致,只有将 React Immutable 特性发挥到极致才能发挥 React 的最大价值。

相关文章
|
2月前
|
缓存 JavaScript PHP
斩获开发者口碑!SnowAdmin:基于 Vue3 的高颜值后台管理系统,3 步极速上手!
SnowAdmin 是一款基于 Vue3/TypeScript/Arco Design 的开源后台管理框架,以“清新优雅、开箱即用”为核心设计理念。提供角色权限精细化管理、多主题与暗黑模式切换、动态路由与页面缓存等功能,支持代码规范自动化校验及丰富组件库。通过模块化设计与前沿技术栈(Vite5/Pinia),显著提升开发效率,适合团队协作与长期维护。项目地址:[GitHub](https://github.com/WANG-Fan0912/SnowAdmin)。
424 5
|
2月前
|
JavaScript API 容器
Vue 3 中的 nextTick 使用详解与实战案例
Vue 3 中的 nextTick 使用详解与实战案例 在 Vue 3 的日常开发中,我们经常需要在数据变化后等待 DOM 更新完成再执行某些操作。此时,nextTick 就成了一个不可或缺的工具。本文将介绍 nextTick 的基本用法,并通过三个实战案例,展示它在表单验证、弹窗动画、自动聚焦等场景中的实际应用。
184 17
|
3月前
|
JavaScript 前端开发 算法
Vue 3 和 Vue 2 的区别及优点
Vue 3 和 Vue 2 的区别及优点
|
3月前
|
存储 JavaScript 前端开发
基于 ant-design-vue 和 Vue 3 封装的功能强大的表格组件
VTable 是一个基于 ant-design-vue 和 Vue 3 的多功能表格组件,支持列自定义、排序、本地化存储、行选择等功能。它继承了 Ant-Design-Vue Table 的所有特性并加以扩展,提供开箱即用的高性能体验。示例包括基础表格、可选择表格和自定义列渲染等。
199 6
|
2月前
|
JavaScript 前端开发 API
Vue 2 与 Vue 3 的区别:深度对比与迁移指南
Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架,在过去的几年里,Vue 2 一直是前端开发中的重要工具。而 Vue 3 作为其升级版本,带来了许多显著的改进和新特性。在本文中,我们将深入比较 Vue 2 和 Vue 3 的主要区别,帮助开发者更好地理解这两个版本之间的变化,并提供迁移建议。 1. Vue 3 的新特性概述 Vue 3 引入了许多新特性,使得开发体验更加流畅、灵活。以下是 Vue 3 的一些关键改进: 1.1 Composition API Composition API 是 Vue 3 的核心新特性之一。它改变了 Vue 组件的代码结构,使得逻辑组
497 0
|
4月前
|
JavaScript 前端开发 UED
vue2和vue3的响应式原理有何不同?
大家好,我是V哥。本文详细对比了Vue 2与Vue 3的响应式原理:Vue 2基于`Object.defineProperty()`,适合小型项目但存在性能瓶颈;Vue 3采用`Proxy`,大幅优化初始化、更新性能及内存占用,更高效稳定。此外,我建议前端开发者关注鸿蒙趋势,2025年将是国产化替代关键期,推荐《鸿蒙 HarmonyOS 开发之路》卷1助你入行。老项目用Vue 2?不妨升级到Vue 3,提升用户体验!关注V哥爱编程,全栈开发轻松上手。
246 2
|
4月前
|
JavaScript 前端开发 算法
高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图
mermaid是一款非常优秀的基于 JavaScript 的图表绘制工具,可渲染 Markdown 启发的文本定义以动态创建和修改图表。非常适合新手学习或者做一些弱交互且自定义要求不高的图表 除了流程图以外,mermaid还支持序列图、类图、状态图、实体关系图等图表可供探索。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
4月前
|
JavaScript 前端开发 API
你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解
onMounted作为vue3中最常用的钩子函数之一,能够灵活、随心应手的使用是每个Vue开发者的必修课,同时根据其不同写法的特性,来选择最合适最有利于维护的写法。博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
4月前
|
资源调度 JavaScript 前端开发
Pinia 如何在 Vue 3 项目中进行安装和配置?
Pinia 如何在 Vue 3 项目中进行安装和配置?
319 4
|
4月前
|
JavaScript 前端开发 API
管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异
一篇文章同时搞定Vue2和Vue3的侦听器,是不是很棒?不要忘了Vue3中多了一个可选项watchEffect噢。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~