Vue3核心考点

Vue3核心考点

核心流程(记忆关键)

记忆口诀:响应式用Proxy,组合式API,性能更优,TypeScript友好

核心对比:Vue2 vs Vue3(面试必考)


第一部分:核心概念

1. Vue3 vs Vue2 核心区别

1.1 响应式原理

Vue2:Object.defineProperty

1
2
3
4
5
6
7
8
9
// 缺点:
// 1. 无法监听数组索引和length变化
// 2. 无法监听对象属性的新增/删除
// 3. 需要递归遍历所有属性

Object.defineProperty(obj, 'name', {
get() { return value },
set(newVal) { value = newVal; notify() }
})

Vue3:Proxy(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 优点:
// 1. 可以监听数组索引和length
// 2. 可以监听对象属性的新增/删除
// 3. 懒代理,性能更好

const proxy = new Proxy(obj, {
get(target, key) {
track(target, key) // 依赖收集
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key) // 触发更新
return true
}
})

1.2 API 风格

Vue2:Options API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
data() {
return { count: 0 }
},
methods: {
increment() { this.count++ }
},
computed: {
double() { return this.count * 2 }
},
mounted() {
console.log('mounted')
}
}

Vue3:Composition API(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ref, computed, onMounted } from 'vue'

export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
const increment = () => count.value++

onMounted(() => {
console.log('mounted')
})

return { count, double, increment }
}
}

1.3 核心对比表

特性 Vue2 Vue3
响应式 Object.defineProperty Proxy
API风格 Options API Composition API
TypeScript 支持较弱 完美支持
性能 较慢 更快(编译优化)
体积 较大 更小(Tree Shaking)
生命周期 beforeCreate/created等 setup + onMounted等
多根节点 不支持 支持(Fragment)
Teleport 不支持 支持
Suspense 不支持 支持

2. Composition API 核心

2.1 响应式 API

ref:基本类型响应式

1
2
3
4
5
import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0
count.value++ // 修改需要.value

reactive:对象响应式

1
2
3
4
5
6
7
8
import { reactive } from 'vue'

const state = reactive({
count: 0,
name: 'Vue3'
})
console.log(state.count) // 0,不需要.value
state.count++

ref vs reactive

1
2
3
4
5
6
7
8
9
10
11
// ref:适合基本类型,需要.value
const count = ref(0)
count.value++

// reactive:适合对象,不需要.value
const state = reactive({ count: 0 })
state.count++

// 注意:reactive不能直接赋值
state = { count: 1 } // ❌ 失去响应式
state.count = 1 // ✅ 正确

computed:计算属性

1
2
3
4
5
6
7
8
9
10
11
12
import { ref, computed } from 'vue'

const count = ref(0)
const double = computed(() => count.value * 2)

// 可写计算属性
const fullName = computed({
get() { return firstName.value + ' ' + lastName.value },
set(value) {
[firstName.value, lastName.value] = value.split(' ')
}
})

watch:侦听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { ref, watch } from 'vue'

const count = ref(0)

// 侦听单个ref
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal)
})

// 侦听多个
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(newCount, newName)
})

// 侦听对象属性
watch(() => state.count, (newVal) => {
console.log(newVal)
})

// 立即执行
watch(count, (newVal) => {
console.log(newVal)
}, { immediate: true })

// 深度侦听
watch(state, (newVal) => {
console.log(newVal)
}, { deep: true })

watchEffect:自动侦听

1
2
3
4
5
6
7
8
import { ref, watchEffect } from 'vue'

const count = ref(0)

// 自动收集依赖,立即执行
watchEffect(() => {
console.log(count.value) // 自动侦听count
})

2.2 生命周期钩子

Vue2 vs Vue3 生命周期对比

1
2
3
4
5
6
7
8
9
10
11
12
// Vue2              // Vue3
beforeCreate → setup()
created → setup()
beforeMount → onBeforeMount
mounted → onMounted
beforeUpdate → onBeforeUpdate
updated → onUpdated
beforeDestroy → onBeforeUnmount
destroyed → onUnmounted
errorCaptured → onErrorCaptured
onRenderTracked(调试用)
onRenderTriggered(调试用)

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
setup() {
onMounted(() => {
console.log('组件挂载')
})

onUpdated(() => {
console.log('组件更新')
})

onUnmounted(() => {
console.log('组件卸载')
})
}
}

3. Vue3 新特性

3.1 Teleport(传送门)

作用:将组件渲染到 DOM 的其他位置

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<!-- 渲染到body下 -->
<Teleport to="body">
<div class="modal">
<p>这是一个模态框</p>
</div>
</Teleport>

<!-- 渲染到指定元素 -->
<Teleport to="#modal-container">
<div>内容</div>
</Teleport>
</template>

使用场景:

  • 模态框(Modal)
  • 通知提示(Toast)
  • 下拉菜单(Dropdown)

3.2 Suspense(异步组件)

作用:处理异步组件的加载状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<Suspense>
<!-- 异步组件 -->
<template #default>
<AsyncComponent />
</template>

<!-- 加载中显示 -->
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>

<script setup>
// AsyncComponent.vue
const data = await fetch('/api/data') // 可以直接使用await
</script>

3.3 Fragment(多根节点)

Vue2:必须有单个根节点

1
2
3
4
5
6
<template>
<div> <!-- 必须有包裹元素 -->
<h1>Title</h1>
<p>Content</p>
</div>
</template>

Vue3:支持多根节点

1
2
3
4
5
<template>
<h1>Title</h1>
<p>Content</p>
<footer>Footer</footer>
</template>

4. Proxy 响应式原理深入

4.1 Proxy 基础

什么是 Proxy?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Proxy 是 ES6 新增的对象代理功能
// 可以拦截对象的各种操作

const obj = { name: 'Vue3', age: 3 }

const proxy = new Proxy(obj, {
// 拦截读取操作
get(target, key, receiver) {
console.log(`读取 ${key}`)
return Reflect.get(target, key, receiver)
},

// 拦截设置操作
set(target, key, value, receiver) {
console.log(`设置 ${key} = ${value}`)
return Reflect.set(target, key, value, receiver)
},

// 拦截删除操作
deleteProperty(target, key) {
console.log(`删除 ${key}`)
return Reflect.deleteProperty(target, key)
},

// 拦截 in 操作符
has(target, key) {
console.log(`检查 ${key}`)
return Reflect.has(target, key)
}
})

proxy.name // 读取 name
proxy.age = 4 // 设置 age = 4
delete proxy.age // 删除 age
'name' in proxy // 检查 name

Proxy vs Object.defineProperty

特性 Object.defineProperty Proxy
监听对象属性新增 ❌ 不支持 ✅ 支持
监听对象属性删除 ❌ 不支持 ✅ 支持
监听数组索引变化 ❌ 不支持 ✅ 支持
监听数组 length ❌ 不支持 ✅ 支持
性能 需要递归遍历 懒代理,性能更好
兼容性 IE9+ 不支持 IE

4.2 Vue3 响应式实现原理

核心流程:响应式转换 → 依赖收集 → 派发更新

步骤1:响应式转换(reactive)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 简化版 reactive 实现
function reactive(target) {
// 如果不是对象,直接返回
if (typeof target !== 'object' || target === null) {
return target
}

// 创建 Proxy 代理
const proxy = new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)

// 依赖收集
track(target, key)

// 如果是对象,递归代理(懒代理)
if (typeof result === 'object' && result !== null) {
return reactive(result)
}

return result
},

set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)

// 值变化才触发更新
if (oldValue !== value) {
trigger(target, key)
}

return result
},

deleteProperty(target, key) {
const hadKey = Object.prototype.hasOwnProperty.call(target, key)
const result = Reflect.deleteProperty(target, key)

// 删除成功且属性存在,触发更新
if (result && hadKey) {
trigger(target, key)
}

return result
}
})

return proxy
}

步骤2:依赖收集(track)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 当前正在执行的副作用函数
let activeEffect = null

// 存储依赖关系:WeakMap<target, Map<key, Set<effect>>>
const targetMap = new WeakMap()

// 依赖收集
function track(target, key) {
// 没有正在执行的副作用函数,不收集
if (!activeEffect) return

// 获取 target 对应的依赖 Map
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}

// 获取 key 对应的依赖 Set
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}

// 添加当前副作用函数
dep.add(activeEffect)
}

// 副作用函数
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn() // 执行函数,触发 get,收集依赖
activeEffect = null
}

effectFn()
return effectFn
}

步骤3:派发更新(trigger)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 派发更新
function trigger(target, key) {
// 获取 target 对应的依赖 Map
const depsMap = targetMap.get(target)
if (!depsMap) return

// 获取 key 对应的依赖 Set
const dep = depsMap.get(key)
if (!dep) return

// 执行所有依赖的副作用函数
dep.forEach(effect => {
effect()
})
}

完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 创建响应式对象
const state = reactive({
count: 0,
user: {
name: 'Vue3'
}
})

// 创建副作用函数(类似组件渲染)
effect(() => {
console.log('count:', state.count) // 触发 get,收集依赖
})
// 输出:count: 0

// 修改数据
state.count++ // 触发 set,派发更新
// 输出:count: 1

// 新增属性(Proxy 支持)
state.age = 3 // 触发 set,派发更新

// 删除属性(Proxy 支持)
delete state.age // 触发 deleteProperty,派发更新

// 嵌套对象(懒代理)
state.user.name = 'Vue' // 触发 get(user) → get(name) → set(name)

4.3 ref 的实现原理

ref 是对基本类型的响应式包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 简化版 ref 实现
function ref(value) {
// 创建包装对象
const wrapper = {
value
}

// 标记为 ref
Object.defineProperty(wrapper, '__v_isRef', {
value: true
})

// 使用 reactive 代理
return reactive(wrapper)
}

// 使用
const count = ref(0)
console.log(count.value) // 0

effect(() => {
console.log('count:', count.value) // 收集依赖
})

count.value++ // 触发更新

4.4 响应式 API 对比

reactive vs ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// reactive:对象响应式
const state = reactive({
count: 0,
user: { name: 'Vue3' }
})
state.count++ // 不需要 .value

// ref:基本类型响应式
const count = ref(0)
count.value++ // 需要 .value

// ref 也可以包装对象
const user = ref({ name: 'Vue3' })
user.value.name = 'Vue' // 需要 .value

// 模板中自动解包
<template>
<div>{{ count }}</div> <!-- 不需要 .value -->
</template>

shallowReactive vs reactive

1
2
3
4
5
6
7
8
9
10
11
12
// reactive:深层响应式
const state = reactive({
user: { name: 'Vue3' }
})
state.user.name = 'Vue' // 触发更新

// shallowReactive:浅层响应式
const state = shallowReactive({
user: { name: 'Vue3' }
})
state.user = { name: 'Vue' } // 触发更新
state.user.name = 'React' // 不触发更新(浅层)

readonly vs reactive

1
2
3
4
5
6
7
// reactive:可读可写
const state = reactive({ count: 0 })
state.count++ // ✅ 可以修改

// readonly:只读
const readonlyState = readonly({ count: 0 })
readonlyState.count++ // ❌ 警告:不能修改

4.5 响应式陷阱与解决方案

陷阱1:reactive 解构丢失响应式

1
2
3
4
5
6
7
8
9
10
11
12
// ❌ 错误:解构后丢失响应式
const state = reactive({ count: 0 })
let { count } = state
count++ // 不会触发更新

// ✅ 解决方案1:使用 toRefs
import { toRefs } from 'vue'
const { count } = toRefs(state)
count.value++ // 触发更新

// ✅ 解决方案2:不解构
state.count++ // 触发更新

陷阱2:reactive 直接赋值丢失响应式

1
2
3
4
5
6
7
8
9
10
// ❌ 错误:直接赋值丢失响应式
let state = reactive({ count: 0 })
state = { count: 1 } // 丢失响应式

// ✅ 解决方案1:修改属性
state.count = 1 // 触发更新

// ✅ 解决方案2:使用 ref
let state = ref({ count: 0 })
state.value = { count: 1 } // 触发更新

陷阱3:数组响应式

1
2
3
4
5
6
7
8
9
10
// Vue3 Proxy 完美支持数组
const arr = reactive([1, 2, 3])

arr[0] = 10 // ✅ 触发更新
arr.length = 0 // ✅ 触发更新
arr.push(4) // ✅ 触发更新

// Vue2 需要使用特殊方法
// this.$set(arr, 0, 10)
// arr.splice(0, 1)

4.6 性能优化

1. 使用 shallowRef/shallowReactive

1
2
3
4
5
6
7
8
9
10
// 大对象只需要浅层响应式
const state = shallowReactive({
list: [/* 1000+ items */]
})

// 修改整个 list 触发更新
state.list = newList // ✅ 触发更新

// 修改 list 内部不触发更新(性能更好)
state.list[0] = newItem // ❌ 不触发更新

2. 使用 markRaw 标记非响应式

1
2
3
4
5
6
7
8
import { reactive, markRaw } from 'vue'

const state = reactive({
// 第三方库实例不需要响应式
chart: markRaw(new Chart())
})

// chart 不会被代理,性能更好

3. 使用 triggerRef 手动触发更新

1
2
3
4
5
6
7
8
9
import { shallowRef, triggerRef } from 'vue'

const state = shallowRef({ count: 0 })

// 修改内部属性不触发更新
state.value.count++

// 手动触发更新
triggerRef(state)

第二部分:面试准备

4. 面试口述版本

4.1 Vue3 相比 Vue2 有哪些改进?(高频)

“Vue3 相比 Vue2 主要有以下改进:

第一,响应式原理升级。Vue2 使用 Object.defineProperty,无法监听数组索引和对象属性的新增删除。Vue3 使用 Proxy,可以监听所有操作,性能更好。

第二,Composition API。Vue2 的 Options API 在大型项目中逻辑分散,难以复用。Vue3 的 Composition API 可以按功能组织代码,逻辑更清晰,复用性更强。

第三,性能优化。Vue3 通过编译优化(静态提升、事件缓存)、Proxy 懒代理、Tree Shaking 等方式,性能提升约 1.5-2 倍,打包体积减少约 40%。

第四,TypeScript 支持。Vue3 使用 TypeScript 重写,类型推导更完善,开发体验更好。

第五,新特性。支持 Teleport 传送门、Suspense 异步组件、Fragment 多根节点等新特性。

总的来说,Vue3 在性能、开发体验、可维护性方面都有显著提升。”

4.2 Composition API 相比 Options API 有什么优势?(高频)

“Composition API 相比 Options API 主要有三个优势:

第一,逻辑组织更清晰。Options API 中,同一个功能的代码分散在 data、methods、computed 等不同选项中。Composition API 可以将相关逻辑组织在一起,代码更易读。

第二,逻辑复用更方便。Options API 使用 Mixins 复用逻辑,容易命名冲突,来源不清晰。Composition API 可以通过组合函数(Composables)复用逻辑,更灵活、更清晰。

第三,TypeScript 支持更好。Composition API 的类型推导更完善,IDE 提示更准确,开发体验更好。

不过 Options API 对初学者更友好,代码结构更固定。实际项目中可以根据团队情况选择,Vue3 两种 API 都支持。”

4.3 Vue3 响应式原理?(高频)

“Vue3 的响应式原理基于 Proxy,分为三个核心步骤:

第一,响应式转换。通过 reactive 或 ref 将数据转换为响应式对象。reactive 使用 Proxy 代理整个对象,ref 将值包装成对象并代理。

第二,依赖收集。当组件渲染时访问响应式数据,会触发 Proxy 的 get 拦截器,通过 track 函数收集当前组件的副作用函数(effect)。

第三,派发更新。当修改响应式数据时,触发 Proxy 的 set 拦截器,通过 trigger 函数找到依赖该数据的所有副作用函数,加入更新队列,异步批量执行。

相比 Vue2 的 Object.defineProperty,Proxy 可以监听对象属性的新增删除、数组索引变化,性能更好,且是懒代理,只有访问时才递归代理。”

4.4 详细说说 Proxy 响应式的实现过程?(深入)

“Proxy 响应式的实现分为三个核心部分:

第一,创建 Proxy 代理。使用 new Proxy 创建代理对象,拦截 get、set、deleteProperty 等操作。get 时收集依赖,set 时触发更新。

第二,依赖收集机制。使用 WeakMap 存储依赖关系,结构是 WeakMap<target, Map<key, Set>>。当访问属性时,将当前的副作用函数添加到对应的 Set 中。

第三,派发更新机制。当修改属性时,从 WeakMap 中找到对应的副作用函数集合,遍历执行。Vue3 会将更新放入队列,异步批量执行,避免重复渲染。

关键优化点:一是懒代理,只有访问嵌套对象时才递归代理,性能更好。二是使用 WeakMap,target 被回收时依赖关系自动清除,避免内存泄漏。三是批量更新,多次修改只触发一次渲染。”

4.5 ref 和 reactive 有什么区别?如何选择?(高频)

“ref 和 reactive 是 Vue3 中创建响应式数据的两种方式,主要区别有四点:

第一,适用类型不同。ref 适合基本类型(string、number、boolean),也可以包装对象。reactive 只适合对象和数组。

第二,访问方式不同。ref 需要通过 .value 访问和修改值,在模板中会自动解包。reactive 直接访问属性,不需要 .value。

第三,重新赋值的表现不同。ref 可以直接重新赋值,不会丢失响应式。reactive 直接赋值会丢失响应式,只能修改属性。

第四,实现原理不同。ref 本质是将值包装成对象 { value: xxx },然后用 reactive 代理。reactive 直接用 Proxy 代理整个对象。

选择建议:基本类型用 ref,对象可以用 reactive,但为了保持一致性,也可以统一用 ref。如果需要解构,用 ref 配合 toRefs 更方便。”

4.6 为什么 reactive 解构会丢失响应式?如何解决?(高频)

“reactive 解构丢失响应式的原因是:解构操作相当于取出了对象的属性值,得到的是一个普通变量,不再是 Proxy 代理对象,所以失去了响应式。

举个例子,const { count } = reactive({ count: 0 }),这里的 count 就是普通的数字 0,修改它不会触发更新。

解决方案有三种:

第一,使用 toRefs。toRefs 会将 reactive 对象的每个属性都转换为 ref,解构后仍然是响应式的。

第二,使用 toRef。如果只需要解构某个属性,可以用 toRef 单独转换。

第三,不解构,直接使用对象。这是最简单的方式,但在模板中需要写完整路径。

实际开发中,推荐使用 toRefs 配合解构,既保持响应式,又方便使用。”


5. 高频追问(7题)

Q1: ref 和 reactive 的区别?

ref:

  • 适合基本类型(string、number、boolean)
  • 访问和修改需要 .value
  • 可以重新赋值
  • 底层也是用 reactive 实现

reactive:

  • 适合对象和数组
  • 不需要 .value
  • 不能直接重新赋值(会失去响应式)
  • 底层使用 Proxy

选择建议:

  • 基本类型用 ref
  • 对象用 reactive
  • 统一用 ref 也可以(更一致)

Q2: watch 和 watchEffect 的区别?

watch:

  • 需要明确指定监听的数据源
  • 可以访问新值和旧值
  • 默认不立即执行(可配置 immediate)
  • 适合需要对比新旧值的场景

watchEffect:

  • 自动收集依赖,不需要指定数据源
  • 无法访问旧值
  • 立即执行一次
  • 适合简单的副作用场景
1
2
3
4
5
6
7
8
9
// watch
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal)
})

// watchEffect
watchEffect(() => {
console.log(count.value) // 自动收集依赖
})

Q3: Vue3 如何实现 Tree Shaking?

原理:

  1. Vue3 使用 ES Module 模块化
  2. 所有 API 都是具名导出(named export)
  3. 打包工具可以静态分析,删除未使用的代码

示例:

1
2
3
4
// 只导入使用的 API
import { ref, computed } from 'vue'

// 未使用的 reactive、watch 等不会被打包

效果:

  • Vue2 全量引入约 32KB
  • Vue3 按需引入约 13KB(减少 60%)

Q4: Composition API 如何实现逻辑复用?

使用 Composables(组合函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// useCounter.js
import { ref } from 'vue'

export function useCounter() {
const count = ref(0)
const increment = () => count.value++
const decrement = () => count.value--

return { count, increment, decrement }
}

// 组件中使用
import { useCounter } from './useCounter'

export default {
setup() {
const { count, increment } = useCounter()
return { count, increment }
}
}

优势:

  • 逻辑清晰,易于理解
  • 避免命名冲突
  • 类型推导完善
  • 可以组合多个 Composables

Q5: Vue3 的编译优化有哪些?

1. 静态提升(Static Hoisting)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 编译前
<div>
<p>静态文本</p>
<p>{{ dynamic }}</p>
</div>

// 编译后
const _hoisted_1 = createVNode("p", null, "静态文本")

function render() {
return createVNode("div", null, [
_hoisted_1, // 静态节点提升,只创建一次
createVNode("p", null, dynamic)
])
}

2. 事件缓存(Event Caching)

1
2
3
4
5
6
// 编译前
<button @click="handleClick">Click</button>

// 编译后
const _cache = []
_cache[0] = handleClick // 缓存事件处理函数

3. 静态标记(PatchFlag)

1
2
// 标记动态节点类型,diff 时只对比动态部分
createVNode("div", null, text, 1 /* TEXT */)

Q6: setup 语法糖有什么优势?

传统 setup:

1
2
3
4
5
6
7
8
9
10
<script>
import { ref } from 'vue'

export default {
setup() {
const count = ref(0)
return { count } // 需要手动返回
}
}
</script>

setup 语法糖:

1
2
3
4
5
<script setup>
import { ref } from 'vue'

const count = ref(0) // 自动暴露给模板
</script>

优势:

  • 代码更简洁
  • 自动暴露变量
  • 更好的 TypeScript 支持
  • 编译时优化

Q7: Vue3 如何处理异步组件?

方式1:defineAsyncComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)

// 带选项
const AsyncComp = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})

方式2:Suspense

1
2
3
4
5
6
7
8
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<Loading />
</template>
</Suspense>

6. 实战经验(3个典型场景)

场景1:大型表单状态管理

问题: 表单字段多,Options API 代码分散

方案: 使用 Composition API 组织

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// useForm.js
import { reactive, computed } from 'vue'

export function useForm() {
const form = reactive({
name: '',
email: '',
age: 0
})

const isValid = computed(() => {
return form.name && form.email && form.age > 0
})

const submit = () => {
if (isValid.value) {
// 提交逻辑
}
}

return { form, isValid, submit }
}

结果: 逻辑清晰,易于维护


场景2:性能优化

问题: 大列表渲染卡顿

方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<!-- 使用 v-memo 缓存 -->
<div v-for="item in list" :key="item.id" v-memo="[item.id]">
{{ item.name }}
</div>
</template>

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

// 使用 shallowRef 避免深层响应式
const list = shallowRef([])
</script>

结果: 渲染性能提升 50%


场景3:逻辑复用

问题: 多个组件需要相同的鼠标位置追踪

方案: 提取 Composable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
const x = ref(0)
const y = ref(0)

const update = (e) => {
x.value = e.pageX
y.value = e.pageY
}

onMounted(() => {
window.addEventListener('mousemove', update)
})

onUnmounted(() => {
window.removeEventListener('mousemove', update)
})

return { x, y }
}

// 组件中使用
const { x, y } = useMouse()

结果: 逻辑复用,代码简洁


第三部分:代码参考

7. 核心 API 速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 响应式
import { ref, reactive, computed, watch, watchEffect } from 'vue'

const count = ref(0)
const state = reactive({ name: 'Vue3' })
const double = computed(() => count.value * 2)
watch(count, (newVal) => console.log(newVal))
watchEffect(() => console.log(count.value))

// 生命周期
import { onMounted, onUpdated, onUnmounted } from 'vue'

onMounted(() => console.log('mounted'))
onUpdated(() => console.log('updated'))
onUnmounted(() => console.log('unmounted'))

// 工具函数
import { toRef, toRefs, unref, isRef, isReactive } from 'vue'

const nameRef = toRef(state, 'name')
const { name, age } = toRefs(state)
const value = unref(count) // 自动解包

8. 记忆路线图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Vue3 核心
├── 响应式原理
│ ├── Proxy 代理
│ ├── 依赖收集(track)
│ └── 派发更新(trigger)

├── Composition API
│ ├── ref / reactive
│ ├── computed / watch
│ └── 生命周期钩子

├── 新特性
│ ├── Teleport
│ ├── Suspense
│ └── Fragment

└── 性能优化
├── 编译优化
├── Tree Shaking
└── 响应式优化

9. 总结

核心要点:

  1. Vue3 使用 Proxy 实现响应式,性能更好
  2. Composition API 逻辑组织更清晰,复用更方便
  3. 编译优化、Tree Shaking 提升性能和体积
  4. TypeScript 支持更完善
  5. 新特性:Teleport、Suspense、Fragment

记忆口诀:

  • Proxy 代理全监听,性能体积都优化
  • Composition API 逻辑清,复用方便类型强
  • 新特性多更灵活,开发体验大提升