Appearance
Vue3:响应式重构、Composition API 与现代工程化
Vue3 可以看成 不是对 Vue2 的小修小补,而是一轮面向现代工程化、复杂业务和类型系统体验的系统升级。
把主线压成一句话:Vue3 把 Vue 从一套经典组件框架,推进成了一套更适合复杂应用组织、逻辑复用和现代工具链协作的前端框架。
1. Vue3 是什么
Vue3 是 Vue 框架的新一代主流版本。
它最核心的变化主要落在这几层:
- 响应式系统从
Object.defineProperty升级到Proxy - 代码组织从以
Options API为主,扩展为更强的Composition API - 对 TypeScript 更友好
- 编译器和运行时优化更细
- 整体工程边界更现代
2. Vue3 主要解决什么问题
Vue2 并不是不能做复杂项目,但随着项目规模上来,几个问题会越来越明显:
- 复杂组件里逻辑分散
- 逻辑复用不自然
- 类型系统体验一般
- 响应式机制有历史边界
- 编译和运行优化空间还可以继续挖
Vue3 的很多设计,本质上都是在回答这些问题。
3. Vue3 最该抓住的 5 个关键词
理解 Vue3 时,最值得抓住的是:
ProxyComposition APIsetupscript setup- 更现代的应用和工具链边界
4. Vue3 的响应式为什么改成 Proxy
Vue3 最经典的一条变化,就是响应式系统升级。
把这层变化拉开:
- Vue2 更像“改造已有属性”
- Vue3 更像“给整个对象包一层代理”
这带来的好处包括:
- 对新增属性、删除属性支持更自然
- 数组处理更统一
- 更容易支持
Map、Set这类结构 - 整体扩展能力更强
所以讲“为什么 Vue3 改用 Proxy”时,不要只说:因为 Proxy 更高级。
更准确的说法应该是:因为 Vue2 的响应式在对象新增、删除、数组和复杂数据结构上存在边界,Vue3 改用 Proxy 后,响应式系统更完整,也更适合现代应用。
4.1 Vue3 的数据绑定到底是什么
Vue3 的“数据绑定”也不应该只理解成一句:数据变了,页面会变。
它其实仍然是两层组合:
- 组件状态驱动视图渲染
- 表单这类交互场景,再通过事件把用户输入回写状态
例如:
vue
<template>
<div>
<p>{{ count }}</p>
<input v-model="keyword" />
</div>
</template>这里至少发生了两件事:
- 模板读取
count和keyword,让渲染逻辑依赖这些响应式数据 - 输入事件再把用户输入写回
keyword
所以 Vue3 里也不是脱离单向渲染的“纯双向绑定模型”,而是:以状态驱动视图为主,再通过语法糖把输入回写接上。
4.2 Vue3 是怎么做到“数据一变,视图跟着变”的
可以把 Vue3 的主链路压缩成 5 步:
ref/reactive创建响应式数据- 组件渲染时读取这些数据
- 读取时触发
track,收集当前活动副作用effect - 写入时触发
trigger,通知相关effect - 相关渲染副作用通过调度器重新执行,最终 patch DOM
如果只记一条主线,可以记成:Proxy 拦截 -> track 收集依赖 -> trigger 触发依赖 -> scheduler 调度更新 -> patch DOM
下面这段可以当成“Vue3 响应式源码风格的简化版”来看:
ts
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// target: 被代理的原始对象
// key: 当前读取的属性名,例如 state.count 里的 count
// receiver: 本次读取操作的接收者,通常就是代理对象本身
// 读取属性时,记录“当前 effect 依赖了 target 的这个 key”
track(target, key)
// 通过 Reflect.get 保持 JS 原生取值语义:
// 1. 正确处理 getter
// 2. 正确处理原型链
// 3. 让 getter 内部的 this 指向 receiver,而不是错误地落回原始对象
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
// value: 准备写入的新值
// 通过 Reflect.set 真正完成赋值,并保持 setter / 原型链等行为正确
const result = Reflect.set(target, key, value, receiver)
// 写入属性后,通知依赖这个 key 的 effect 重新执行
trigger(target, key)
// Proxy 的 set 拦截器需要返回布尔值,表示本次写入是否成功
return result
}
})
}这段代码最重要的意思是:
- 读取属性时走
track - 写入属性时走
trigger - Vue3 的响应式更像“给对象包了一层代理”
4.2.1 ref 和 reactive 在底层关系上可以怎么理解
很多人学到这里会继续追问:
ref 和 reactive 到底是不是两套完全独立的东西。
更稳的理解方式是:
reactive更像“代理整个对象”ref更像“把单个值包进一个带.value的响应式壳里”- 它们最终都会接入同一套依赖收集和触发更新机制
可以把 ref 当成下面这种“源码风格的简化实现”来看:
ts
/**
* createRef
* 功能:把一个普通值包装成带 `.value` 的响应式引用对象。
* 核心职责:
* 1. 读取 `.value` 时收集依赖
* 2. 修改 `.value` 时触发依赖
* 参数:
* - rawValue: 原始值,可以是基本类型,也可以是对象
* 返回值:
* - 一个带 `value` 访问器的引用对象
*/
function createRef(rawValue) {
let value = rawValue
return {
get value() {
track(this, 'value')
return value
},
set value(newValue) {
if (newValue === value) return
value = newValue
trigger(this, 'value')
}
}
}这段代码最想说明的是:ref 和 reactive 表面用法不同,但底层仍然都在围绕“读取时 track、写入时 trigger”这条主线工作。`
4.3 track、trigger、effect 分别在做什么
这 3 个词,是理解 Vue3 响应式系统的核心。
4.3.1 track
track 可以看成 在读取数据时,记录“当前这段逻辑依赖了哪个目标对象的哪个属性”。
也就是在回答:谁正在用我。
4.3.2 trigger
trigger 可以看成 在写入数据时,把依赖这个属性的副作用重新唤起。
也就是在回答:我变了,该通知谁。
4.3.3 effect
effect 可以看成 一段依赖响应式数据、需要在依赖变化时重新执行的逻辑。
在 Vue3 语境里,可以把组件渲染理解成:一个特殊的响应式副作用。
所以组件之所以能“自动更新”,本质上是因为:组件渲染过程本身就被包进了一套响应式副作用机制里。
如果用一段更贴近底层实现的简化代码来理解,大致可以这样看:
ts
const targetMap = new WeakMap()
function track(target, key) {
let depsMap = targetMap.get(target)
if (!depsMap) {
// 第一层按目标对象建立依赖表
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
// 第二层按属性 key 建立 effect 集合
dep = new Set()
depsMap.set(key, dep)
}
if (activeEffect) {
// 当前正在执行的 effect 读取了这个属性,于是建立依赖关系
dep.add(activeEffect)
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
const dep = depsMap?.get(key)
if (!dep) return
dep.forEach(effect => {
if (effect.scheduler) {
// 有调度器时,先交给调度器决定什么时候执行
effect.scheduler()
} else {
// 没有调度器时,直接重跑 effect
effect()
}
})
}这段代码想表达的重点是:
target -> key -> effects之间会建立映射关系- 依赖不是散乱通知,而是按目标对象和具体属性精确触发
- 调度器存在时,不一定马上执行,而是可以交给统一更新机制
4.3.4 effect 自己内部大致长什么样
如果继续往下追一层,很多人会想知道:activeEffect 到底是怎么来的,为什么 track 时能知道“当前是谁在读数据”。`
可以把 effect 非常简化地理解成下面这样:
ts
let activeEffect = null
/**
* effect
* 功能:把一段函数包装成响应式副作用。
* 核心职责:
* 1. 执行前把当前副作用挂到 activeEffect 上
* 2. 执行过程中让 track 有机会收集它
* 3. 执行完后恢复现场
* 参数:
* - fn: 真正需要响应式联动的逻辑
* - options: 可选配置,例如 scheduler
* 返回值:
* - runner: 一个可重复执行的副作用函数
*/
function effect(fn, options = {}) {
const runner = () => {
const parent = activeEffect
activeEffect = runner
try {
return fn()
} finally {
activeEffect = parent
}
}
runner.scheduler = options.scheduler
runner()
return runner
}这样再回头看 track,就更容易明白了:
effect执行前把自己挂到activeEffect- 渲染过程中一旦读取响应式属性,就会进入
track track看到当前有activeEffect,于是把它收集起来- 后面属性变化时,
trigger就能把这段副作用重新唤起
所以更完整的底层主线是:effect 执行 -> activeEffect 建立上下文 -> getter 中 track 收集 -> setter 中 trigger 通知
如果把这条链路再压成一张图,可以这样看:
mermaid
flowchart TD
A[组件渲染读取 state.count] --> B[Proxy get]
B --> C[track target key]
C --> D[记录 activeEffect]
E[state.count = 2] --> F[Proxy set]
F --> G[trigger target key]
G --> H[找到依赖当前 key 的 effects]
H --> I[scheduler 入队]
I --> J[重新执行组件渲染 effect]
J --> K[生成新 vnode]
K --> L[patch DOM]4.4 Vue3 的更新调度为什么不是“改一次就渲染一次”
Vue3 也不会每次状态变化都立刻同步直接改 DOM。
更常见的流程是:
- 状态变化触发依赖
- 依赖进入调度器
- 同一轮事件循环里的多次更新尽量合并
- 最后统一刷新组件
这套机制的价值在于:
- 避免重复渲染
- 合并多次状态变更
- 让 DOM 更新成本更可控
这也是为什么 nextTick 仍然重要,因为:数据变更和 DOM 完成更新之间,通常隔着一层调度与批处理。
可以再看一段“组件渲染 effect + scheduler”的简化写法:
ts
const update = effect(
() => {
// 组件重新执行渲染逻辑,得到下一棵 vnode 树
const vnode = renderComponentRoot(instance)
// 把旧树和新树做 patch,落到真实 DOM
patch(instance.subTree, vnode)
instance.subTree = vnode
},
{
// 响应式状态变化时,不直接重跑,而是先入调度队列
scheduler: () => queueJob(instance.update)
}
)
instance.update = update这段代码最值得先理解的是:
- 组件渲染本身就是一个
effect - 状态变化后,并不是直接同步重跑,而是先进调度队列
- 真正的页面更新,仍然落在“重新生成 vnode -> patch”这条主线上
4.4.1 调度器队列在做什么
如果只知道“有调度器”,还是容易停留在抽象层。
更贴近运行时的理解是:
- 每个组件更新任务会先进入队列
- 队列会去重,避免同一个组件在一轮里重复进入多次
- 刷新时统一按顺序执行这些任务
下面这段可以当成“Vue3 调度器思路的简化版”来看:
ts
const queue = []
const queuedJobs = new Set()
let isFlushing = false
/**
* queueJob
* 功能:把组件更新任务放进调度队列。
* 核心职责:
* 1. 对同一个 job 做去重
* 2. 在本轮微任务中安排统一刷新
* 参数:
* - job: 组件更新任务,通常就是 instance.update
* 返回值:
* - 无显式返回值,副作用是维护调度队列
*/
function queueJob(job) {
if (queuedJobs.has(job)) return
queuedJobs.add(job)
queue.push(job)
if (!isFlushing) {
isFlushing = true
Promise.resolve().then(flushJobs)
}
}
function flushJobs() {
try {
for (const job of queue) {
job()
}
} finally {
queue.length = 0
queuedJobs.clear()
isFlushing = false
}
}这段代码的重点不是细节一字不差,而是帮你建立下面这层直觉:Vue3 不是谁变了就立刻同步重渲染,而是把“该更新谁”先收起来,再统一执行。
4.4.2 一张图看清“状态变化到调度刷新”
mermaid
flowchart TD
A[响应式属性被修改] --> B[trigger 找到依赖 effect]
B --> C{effect 是否带 scheduler}
C -- 否 --> D[直接执行 effect]
C -- 是 --> E[queueJob 放入更新队列]
E --> F[队列去重]
F --> G[微任务阶段 flushJobs]
G --> H[执行 instance.update]
H --> I[重新 render + patch]4.5 Vue3 的渲染流程是怎样的
如果把 Vue3 的渲染流程展开成一条完整主线,大致可以这样理解:
.vue文件先经过编译链路- 模板被编译成渲染函数
- 组件初始化时执行
setup - 渲染函数运行,生成虚拟 DOM
- 首次挂载时把虚拟 DOM 转成真实 DOM
- 响应式状态变化后,触发组件更新
- 重新执行渲染逻辑得到新的虚拟 DOM
- 新旧虚拟 DOM 做对比并 patch
也就是说,Vue3 的关键不是“直接改 HTML”,而是:响应式状态驱动渲染函数重新执行,再由运行时完成 vnode diff 和 DOM patch。
如果再往源码感受上靠一步,可以把组件更新理解成下面这种非常简化的链路:
ts
function componentUpdateFn() {
// 新一轮渲染拿到最新 vnode
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
// 只把前后差异 patch 到页面
patch(prevTree, nextTree)
}这段代码想表达的只有一件事:响应式系统负责把更新机会送到组件,运行时负责把前后两棵 vnode 树的差异真正落到 DOM。
4.5.1 组件首次挂载和后续更新其实是两条分支
Vue3 的组件渲染虽然可以概括成“render -> vnode -> patch”,但在运行时里,首次挂载和后续更新其实不是完全一样的路径。
更贴近实现的理解是:
- 首次挂载时,要先创建组件实例、执行
setup、建立渲染副作用,再把子树挂到页面 - 后续更新时,重点变成“拿新旧子树做 patch”
可以把这条线看成下面这种简化结构:
ts
/**
* setupRenderEffect
* 功能:为组件建立渲染副作用,统一处理首次挂载和后续更新。
* 核心职责:
* 1. 首次执行时完成 mount
* 2. 后续依赖变化时进入 update
* 参数:
* - instance: 当前组件实例
* - container: 当前组件将被挂载到的 DOM 容器
* 返回值:
* - 无显式返回值,副作用是把组件渲染结果同步到页面
*/
function setupRenderEffect(instance, container) {
instance.update = effect(
() => {
if (!instance.isMounted) {
const subTree = renderComponentRoot(instance)
patch(null, subTree, container)
instance.subTree = subTree
instance.isMounted = true
} else {
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
patch(prevTree, nextTree, container)
}
},
{
scheduler: () => queueJob(instance.update)
}
)
}这段代码最值得看懂的是:
- 组件更新函数本身就是一个
effect - 首次执行做的是 mount
- 再次执行做的是 update
- 两次都会经过
renderComponentRoot - 真正落地 DOM 的动作都在
patch里
4.5.2 一张图把运行时阶段串起来
mermaid
flowchart TD
A[createApp 挂载根组件] --> B[创建根 vnode]
B --> C[patch 进入组件分支]
C --> D[创建组件实例]
D --> E[执行 setup]
E --> F[建立 render effect]
F --> G{是否首次挂载}
G -- 是 --> H[renderComponentRoot 生成 subTree]
H --> I[patch null 与 subTree]
I --> J[生成真实 DOM]
G -- 否 --> K[renderComponentRoot 生成 nextTree]
K --> L[patch prevTree 与 nextTree]
L --> M[按差异更新 DOM]4.6 一张流程图先建立整体认知
mermaid
flowchart TD
A[ref reactive 创建状态] --> B[组件渲染读取状态]
B --> C[track 收集 effect]
D[状态被修改] --> E[trigger 通知依赖]
E --> F[scheduler 调度更新]
F --> G[重新执行组件渲染 effect]
G --> H[生成新 vnode]
H --> I[diff + patch]
I --> J[DOM 更新]4.7 v-model 在 Vue3 里本质是什么
Vue3 里的 v-model 也还是语法糖。
从本质上看,它仍然可以理解成:
- 一个值绑定
- 一个更新事件
在组件场景里,最常见的理解方式是:
vue
<Child :modelValue="keyword" @update:modelValue="keyword = $event" />所以它并不是一套和响应式系统平行存在的“魔法双向绑定机制”,而是:把状态下发和事件回传这两步封装成了更统一的写法。
如果把组件场景下的 v-model 也画成流程,可以这样看:
mermaid
flowchart LR
A[父组件 keyword] --> B[:modelValue 传给子组件]
B --> C[子组件内部输入框显示当前值]
D[用户输入] --> E[子组件触发 update:modelValue]
E --> F[父组件收到事件]
F --> G[keyword = event]
G --> H[父组件重新渲染]
H --> B4.8 Vue3 的设计模式可以怎么理解
这部分也不建议背成术语表,但值得建立工程直觉。
4.8.1 MVVM
Vue3 仍然可以放在 MVVM 语境里理解:
Model:状态View:模板和 DOMViewModel:组件实例和运行时桥接层
它的价值仍然是:开发者主要描述状态和视图关系,而不是手动写 DOM 更新。
4.8.2 观察者式依赖通知
Vue3 的 track / trigger / effect 明显带有观察者式设计特征:
- 依赖建立关系
- 数据变化时发出通知
- 相关副作用重新执行
实际写项目时,更多是这样,它不是只在背“观察者模式”这个词,而是在用一套:依赖收集 + 变更通知 + 副作用重跑
的机制组织响应式更新。
4.8.3 代理模式
Vue3 相比 Vue2,更明显的一层设计模式色彩就是代理思路。
因为 Proxy 本身就很接近:通过代理对象接管原对象的访问与修改。
这也是为什么 Vue3 能更自然地拦截读取、写入、删除等操作。
4.8.4 组件组合模式
Vue3 引入 Composition API 后,组件设计也更明显地往“组合”方向发展。
也就是:
- 用组件组合页面
- 用组合函数组合逻辑
这让它在复杂应用里更适合按业务关注点拆分能力。
5. Composition API 在解决什么问题
很多人第一次看到 Composition API,会把它理解成:只是换了一套写法。
这不准确。
它真正解决的是:复杂组件里,同一块业务逻辑会分散在 data、methods、computed、watch、生命周期等多个位置,后期很难维护和复用。
5.1 一个简单示例
vue
<script setup>
import { computed, ref } from 'vue'
const keyword = ref('')
const list = ref([
{ id: 1, name: 'Vue3' },
{ id: 2, name: 'Pinia' }
])
const filteredList = computed(() =>
list.value.filter(item => item.name.includes(keyword.value))
)
</script>
<template>
<section>
<input v-model="keyword" placeholder="请输入关键字" />
<ul>
<li v-for="item in filteredList" :key="item.id">
{{ item.name }}
</li>
</ul>
</section>
</template>这里最重要的不是语法本身,而是:
- 状态
- 派生值
- 相关逻辑
可以自然放在同一块。
6. ref 和 reactive 怎么理解
这是 Vue3 里很高频的一组概念。
6.1 ref
ref 更适合:
- 单值状态
- 基本类型
- 需要显式引用包装的场景
ts
const count = ref(0)6.2 reactive
reactive 更适合:
- 对象整体状态
- 多字段一起组织的状态块
ts
const form = reactive({
name: '',
age: 0
})6.3 更稳的理解方式
不要死背“哪个一定更高级”,更实用的判断是:
- 单值、小块状态常用
ref - 对象整体建模常用
reactive - 实际项目里二者经常一起使用
7. computed、watch、watchEffect 的边界
7.1 computed
适合:
- 派生状态
- 纯计算结果
- 有缓存的值
7.2 watch
适合:
- 监听变化后执行副作用
- 请求接口
- 同步外部系统
7.3 watchEffect
可以看成 自动收集依赖的一类副作用执行方式。
它更适合:
- 依赖关系比较直接
- 想快速建立副作用联动
但要注意:依赖太复杂时,watchEffect 也可能让触发边界变得不直观。
8. setup 和 script setup 是什么
8.1 setup
setup 是 Composition API 的入口。
它的意义不是“换个位置写代码”,而是:把组件逻辑组织从以 this 为中心,转向以显式变量和函数组合为中心。
8.2 script setup
script setup 可以看成 Vue3 为单文件组件提供的一种更精简、更适合现代开发的写法。
它的价值在于:
- 模板里可以更自然使用脚本变量
- 样板代码更少
- 类型推导更顺
9. Vue3 为什么对 TypeScript 更友好
不是因为“Vue3 才能用 TS”,而是因为:
- API 更函数化
- 返回值和参数边界更显式
setup、组合函数、defineProps更利于类型推导
这让 Vue3 在大型项目里,尤其在:
- 组件 Props 约束
- 组合函数复用
- 状态管理
这些场景下会更顺手。
10. Vue3 的编译优化在说什么
很多人会笼统说 Vue3 更快,但实际写项目时,更多是这样:Vue3 不只是运行时变了,编译器也更聪明了。
它会在编译阶段做更多静态分析,例如:
- 标记动态节点
- 静态提升
- 减少不必要的比较和更新
所以 Vue3 的优化不是单点的,而是:
- 响应式系统
- 编译阶段
- 运行时更新策略
一起在升级。
10.1 编译阶段到底帮运行时做了什么
如果把 Vue3 的性能优化只理解成运行时更快,会漏掉一半关键点。
编译阶段真正做的事,可以压成下面 4 步:
- 解析模板,知道节点结构长什么样
- 识别哪些内容是静态的,哪些是动态的
- 给动态节点打上标记,例如
Patch Flag - 生成更利于运行时快速更新的渲染函数
也就是说,编译器不是单纯“把模板翻译成 JS”,而是在提前告诉运行时:这里哪些地方将来可能变化,哪些地方可以放心跳过。
10.2 一个简化例子看 Patch Flag 和静态提升
例如模板:
vue
<div class="card">
<h1>{{ title }}</h1>
<p>固定说明文本</p>
</div>编译后可以非常粗略地理解成下面这种风格:
ts
/**
* hoistedNode
* 功能:把完全静态的节点提升到渲染函数外部复用。
* 作用:
* - 避免每次组件更新都重新创建完全相同的 vnode
*/
const hoistedNode = createElementVNode('p', null, '固定说明文本', -1)
/**
* render
* 功能:根据当前状态生成组件 vnode 树。
* 参数:
* - _ctx: 当前组件渲染上下文
* 返回值:
* - 当前组件这一轮渲染得到的 vnode
*/
function render(_ctx) {
return createElementVNode('div', { class: 'card' }, [
createElementVNode('h1', null, _ctx.title, 1),
hoistedNode
])
}这段代码里最值得抓住的是:
- 静态的
<p>可以被提升出去复用 title是动态的,所以对应节点会带动态标记- 运行时更新时,就不用把整棵树都当成完全未知来处理
10.3 一张图看清“编译优化如何衔接运行时”
mermaid
flowchart LR
A[template] --> B[compiler 解析 AST]
B --> C[识别静态节点与动态节点]
C --> D[静态提升 Hoist Static]
C --> E[动态节点打 Patch Flag]
D --> F[生成 render 函数]
E --> F
F --> G[运行时执行 render]
G --> H[patch 时更聚焦地比较和更新]11. Vue3 的新能力和生态方向
Vue3 时代更常见的一些能力包括:
- 更完整的组合式 API
TeleportSuspense- 多根节点支持
- 更自然地配合
Pinia - 更现代的构建链路
这些能力说明的是:Vue3 更强调现代前端应用的工程边界,而不只是模板层语法。
12. Vue3 更适合哪些场景
Vue3 更适合:
- 新项目
- 复杂业务页面
- 逻辑复用需求较多的项目
- 对 TypeScript 体验有要求的团队
- 需要长期演进的中大型前端项目
13. 一个更贴近工程的组合函数示例
ts
import { computed, ref, watch } from 'vue'
export function useUserSearch(fetchUsers: (keyword: string) => Promise<string[]>) {
const keyword = ref('')
const list = ref<string[]>([])
const loading = ref(false)
const total = computed(() => list.value.length)
watch(keyword, async (value) => {
loading.value = true
list.value = await fetchUsers(value)
loading.value = false
})
return {
keyword,
list,
total,
loading
}
}这段代码想表达的重点是:
- 状态、派生值、副作用可以聚合成一块逻辑
- 这块逻辑可以在多个组件中复用
- 这是 Vue3 很重要的一条能力线
14. 工程里最容易踩的坑
14.1 把 Composition API 当成“写法升级”
最后还是会用旧思路写出新的大组件。
14.2 滥用 watch
本该用 computed 的地方用副作用处理,状态流会变乱。
14.3 ref、reactive 混用没有边界
会让状态组织越来越不清晰。
14.4 只会说 Vue3 更快,不会解释为什么
这会让对版本升级的理解停留在口号层。
15. 小结
如果把 Vue3 压缩成一句话,可以记住:Vue3 是一轮围绕响应式系统、逻辑组织方式、类型体验和工程化边界展开的系统升级,它更适合现代前端应用,也更适合作为新项目的默认主线。
继续往下读,比较自然的下一步是进入 Vue2 与 Vue3 对比、Vuex 与 Pinia 和 Vue Router。