vue3详解
Vue 3 深度解析:从响应式原理到编译优化的完整指南
第一章:Vue 3 架构设计与核心思想
1.1 Vue 3 设计哲学与技术突破
Vue 3 是一次彻底的重构,其核心设计理念围绕 "渐进式"、"组合式" 和 "性能优先" 展开。让我们从架构层面深入理解 Vue 3 的设计选择。
架构演进:从 Monolithic 到 Module-based
// Vue 2 的架构(Monolithic)
class Vue2 {
constructor(options) {
// 所有功能耦合在一起
this.initData(options.data)
this.initComputed(options.computed)
this.initWatch(options.watch)
this.initMethods(options.methods)
// 渲染逻辑、响应式逻辑、生命周期逻辑高度耦合
}
}
// Vue 3 的架构(模块化)
import { reactive, computed, watchEffect } from '@vue/reactivity'
import { createRenderer } from '@vue/runtime-core'
import { compile } from '@vue/compiler-dom'
// 响应式系统:独立的包
const state = reactive({ count: 0 })
// 运行时核心:独立的包
const renderer = createRenderer(/* renderer options */)
// 编译器:独立的包
const { code } = compile('<div>{{ count }}</div>')
Composition API 的哲学转变
Composition API 不仅仅是 API 的改变,更是思维模式的转变:
// Options API (Vue 2 风格)
export default {
data() {
return { count: 0, message: 'Hello' }
},
computed: {
doubleCount() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
},
watch: {
count(newVal) {
console.log('count changed:', newVal)
}
}
}
// Composition API (Vue 3 风格)
import { ref, computed, watch, onMounted } from 'vue'
export default {
setup() {
// 逻辑关注点分离:计数器逻辑
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => count.value++
watch(count, (newVal) => {
console.log('count changed:', newVal)
})
// 逻辑关注点分离:消息逻辑
const message = ref('Hello')
const updateMessage = (newMsg) => {
message.value = newMsg
}
// 生命周期
onMounted(() => {
console.log('Component mounted')
})
// 按逻辑组织代码,而不是按选项类型
return {
// 计数器相关
count,
doubleCount,
increment,
// 消息相关
message,
updateMessage
}
}
}
1.2 响应式系统架构
Vue 3 的响应式系统是完全重写的,基于 ES6 Proxy 实现,其架构设计精妙且高效。
响应式系统核心架构图
响应式系统架构:
┌─────────────────────────────────────────────────────┐
│ 响应式数据 (reactive/ref) │
├─────────────────────────────────────────────────────┤
│ Proxy/Getter │ Dep (依赖收集) │
│ (数据访问拦截) │ (收集effect) │
├─────────────────────────────────────────────────────┤
│ Trigger (触发更新) │ Effect (副作用) │
│ (通知所有effect) │ (需要重新执行的函数) │
├─────────────────────────────────────────────────────┤
│ Scheduler (调度器) │ Component (组件) │
│ (控制更新时机) │ (UI更新) │
└─────────────────────────────────────────────────────┘
响应式系统的分层设计
// 第一层:原始响应式API
import { reactive, ref, computed, watch } from 'vue'
// 第二层:响应式核心(@vue/reactivity包)
import {
effect, // 副作用函数
track, // 依赖收集
trigger, // 触发更新
ReactiveEffect, // 响应式effect类
reactiveMap // 响应式对象缓存
} from '@vue/reactivity'
// 第三层:代理处理器
const baseHandlers = {
get(target, key, receiver) {
track(target, TrackOpTypes.GET, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return result
},
// ... 其他操作符
}
// 第四层:数据类型处理
const collectionHandlers = {
// Map, Set, WeakMap, WeakSet 的特殊处理
get(target, key, receiver) {
if (key === 'size') {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(target, key, receiver)
}
return mutableInstrumentations[key]
}
}
第二章:响应式系统深度解析
2.1 Proxy-based 响应式原理
Proxy 拦截器的完整实现
// 响应式对象的创建过程
function reactive(target: object) {
// 1. 如果已经是响应式对象,直接返回
if (target && (target as any).__v_isReactive) {
return target
}
// 2. 如果target是readonly响应式对象,直接返回
if (target && (target as any).__v_isReadonly) {
return target
}
// 3. 从缓存中查找
const existingProxy = reactiveMap.get(target)
if (existingProxy) {
return existingProxy
}
// 4. 创建代理
const proxy = new Proxy(
target,
baseHandlers
)
// 5. 设置标志并缓存
;(proxy as any).__v_isReactive = true
reactiveMap.set(target, proxy)
return proxy
}
// 基础处理器实现(简化版)
const baseHandlers: ProxyHandler<any> = {
get(target: Target, key: string | symbol, receiver: object) {
// 1. 特殊属性访问(__v_isReactive, __v_raw等)
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}
if (key === ReactiveFlags.RAW) {
return target
}
// 2. 依赖收集
track(target, TrackOpTypes.GET, key)
// 3. 获取值
const res = Reflect.get(target, key, receiver)
// 4. 如果是对象,递归响应化
if (isObject(res)) {
return reactive(res)
}
return res
},
set(target: Target, key: string | symbol, value: any, receiver: object): boolean {
// 1. 获取旧值
const oldValue = target[key]
// 2. 检查是否为新增属性
const hadKey = hasOwn(target, key)
// 3. 执行设置
const result = Reflect.set(target, key, value, receiver)
// 4. 触发更新(避免触发原型链上的setter)
if (target === toRaw(receiver)) {
if (!hadKey) {
// 新增属性
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 修改属性
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
},
deleteProperty(target: Target, key: string | symbol): boolean {
// 1. 检查属性是否存在
const hadKey = hasOwn(target, key)
const oldValue = target[key]
// 2. 执行删除
const result = Reflect.deleteProperty(target, key)
// 3. 触发更新
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
},
has(target: Target, key: string | symbol): boolean {
const result = Reflect.has(target, key)
// 依赖收集
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, TrackOpTypes.HAS, key)
}
return result
},
ownKeys(target: Target): Array<string | symbol> {
// 依赖收集
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
}
// 特殊集合类型的处理器
const collectionHandlers: ProxyHandler<any> = {
get(target: CollectionTypes, key: string | symbol, receiver: CollectionTypes) {
// 拦截size访问
if (key === 'size') {
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(target, key, target)
}
// 返回包装后的方法
return mutableInstrumentations[key]
}
}
// 集合方法的包装
const mutableInstrumentations: Record<string, Function> = {
add(this: CollectionTypes, value: any) {
const target = toRaw(this)
const hadKey = target.has(value)
const result = target.add(value)
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, value, value)
}
return result
},
delete(this: CollectionTypes, key: any) {
const target = toRaw(this)
const hadKey = target.has(key)
const result = target.delete(key)
if (hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, key)
}
return result
},
get(this: CollectionTypes, key: any) {
const target = toRaw(this)
// 依赖收集
track(target, TrackOpTypes.GET, key)
// 如果是Map,需要将值也响应化
if (isMap(target)) {
return wrap(target.get(key))
}
return target.get(key)
},
set(this: CollectionTypes, key: any, value: any) {
const target = toRaw(this)
const hadKey = target.has(key)
const oldValue = target.get(key)
// 设置值
target.set(key, value)
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return this
}
}
依赖收集与追踪系统
// 全局状态
let activeEffect: ReactiveEffect | undefined
let shouldTrack = true
const targetMap = new WeakMap<any, KeyToDepMap>()
// 依赖收集
function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
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 = createDep()))
}
// 添加effect到依赖集合
trackEffects(dep)
}
function trackEffects(dep: Dep) {
// 避免重复收集
if (!dep.has(activeEffect!)) {
dep.add(activeEffect!)
// effect也需要记录自己被哪些dep收集
activeEffect!.deps.push(dep)
}
}
// 触发更新
function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 收集所有需要触发的effect
const effects: ReactiveEffect[] = []
// 添加与key相关的effect
if (key !== void 0) {
const dep = depsMap.get(key)
if (dep) {
effects.push(...dep)
}
}
// 特殊处理:数组length变化
if (type === TriggerOpTypes.ADD && isArray(target)) {
const lengthDep = depsMap.get('length')
if (lengthDep) {
effects.push(...lengthDep)
}
}
// 添加与ITERATE_KEY相关的effect(用于for...in循环等)
if (
type === TriggerOpTypes.ADD ||
type === TriggerOpTypes.DELETE ||
(type === TriggerOpTypes.SET && target instanceof Map)
) {
const iterateDep = depsMap.get(ITERATE_KEY)
if (iterateDep) {
effects.push(...iterateDep)
}
}
// 特殊处理:Map的迭代
if (
(type === TriggerOpTypes.SET && target instanceof Map) ||
(type === TriggerOpTypes.ADD && !isArray(target))
) {
const keysDep = depsMap.get(MAP_KEY_ITERATE_KEY)
if (keysDep) {
effects.push(...keysDep)
}
}
// 触发所有收集到的effect
triggerEffects(createDep(effects))
}
function triggerEffects(dep: Dep) {
// 触发所有effect
for (const effect of dep) {
if (effect !== activeEffect || effect.allowRecurse) {
if (effect.scheduler) {
// 如果有调度器,通过调度器执行
effect.scheduler()
} else {
// 直接执行
effect.run()
}
}
}
}
Effect 系统实现
// Effect类定义
class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
constructor(
public fn: () => T,
public scheduler: EffectScheduler | null = null,
scope?: EffectScope
) {
recordEffectScope(this, scope)
}
run() {
if (!this.active) {
return this.fn()
}
try {
// 设置当前激活的effect
activeEffect = this
shouldTrack = true
// 清理之前的依赖
cleanupEffect(this)
// 执行函数,期间会收集依赖
return this.fn()
} finally {
// 恢复状态
shouldTrack = false
activeEffect = undefined
}
}
stop() {
if (this.active) {
cleanupEffect(this)
this.active = false
}
}
}
// 清理effect的依赖
function cleanupEffect(effect: ReactiveEffect) {
const { deps } = effect
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
}
}
// 创建effect
function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
// 如果fn已经是effect,获取其原始函数
if ((fn as any).effect) {
fn = (fn as any).effect.fn
}
// 创建effect实例
const _effect = new ReactiveEffect(fn)
// 合并选项
if (options) {
extend(_effect, options)
if (options.scope) {
recordEffectScope(_effect, options.scope)
}
}
// 如果没有设置lazy,立即执行
if (!options || !options.lazy) {
_effect.run()
}
// 返回runner
const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
runner.effect = _effect
return runner
}
// 停止effect
function stop(runner: ReactiveEffectRunner) {
runner.effect.stop()
}
2.2 ref 与 computed 的实现原理
ref 的完整实现
// ref函数实现
function ref<T>(value: T): Ref<T> {
return createRef(value, false)
}
// 创建ref的核心函数
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
// Ref实现类
class RefImpl<T> {
private _value: T
private _rawValue: T
public readonly __v_isRef = true
constructor(value: T, public readonly __v_isShallow: boolean) {
// 保存原始值
this._rawValue = __v_isShallow ? value : toRaw(value)
// 如果是浅ref,直接使用值;否则转换为响应式
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
// 依赖收集
trackRefValue(this)
return this._value
}
set value(newVal) {
// 使用原始值进行比较
const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
// 更新值
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
// 触发更新
triggerRefValue(this, newVal)
}
}
}
// ref依赖收集
function trackRefValue(ref: RefBase<any>) {
if (shouldTrack && activeEffect) {
// 为ref创建一个特殊的key
ref = toRaw(ref)
// 获取或创建ref的依赖集合
let dep = ref.dep
if (!dep) {
ref.dep = dep = createDep()
}
trackEffects(dep)
}
}
// ref触发更新
function triggerRefValue(ref: RefBase<any>, newVal?: any) {
ref = toRaw(ref)
if (ref.dep) {
triggerEffects(ref.dep)
}
}
// toRef和toRefs的实现
function toRef<T extends object, K extends keyof T>(
object: T,
key: K
): ToRef<T[K]> {
return new ObjectRefImpl(object, key) as any
}
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K
) {}
get value() {
return this._object[this._key]
}
set value(newVal) {
this._object[this._key] = newVal
}
}
// toRefs实现
function toRefs<T extends object>(object: T): ToRefs<T> {
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret
}
computed 的完整实现
// computed函数实现
function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
): ComputedRef<T> {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 处理两种参数形式
if (isFunction(getterOrOptions)) {
getter = getterOrOptions as ComputedGetter<T>
setter = NOOP
} else {
getter = (getterOrOptions as WritableComputedOptions<T>).get
setter = (getterOrOptions as WritableComputedOptions<T>).set
}
return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions)) as any
}
// ComputedRef实现类
class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
private _dirty = true
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
private getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
// 创建effect,但立即不执行
this.effect = new ReactiveEffect(
() => getter(this._value),
() => {
// 调度器:当依赖变化时,标记为脏,并触发更新
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
}
)
this.effect.computed = this
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// 依赖收集
trackRefValue(this)
// 如果脏,重新计算
if (this._dirty) {
this._dirty = false
try {
// 执行计算
this._value = this.effect.run()!
} catch (e) {
// 计算出错时,标记为脏,下次重新计算
this._dirty = true
throw e
}
}
return this._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
// computed缓存机制
function computedWithCache<T>(
getter: ComputedGetter<T>
): ComputedRef<T> {
const computedRef = computed(getter)
// 缓存上一次的计算结果
let cachedValue: T | undefined
let cachedDeps: Dep[] = []
const effect = new ReactiveEffect(
() => {
const value = getter()
// 检查依赖是否变化
const deps = effect.deps.slice()
if (cachedValue !== undefined) {
// 比较依赖
const depsChanged = deps.some((dep, i) => dep !== cachedDeps[i])
if (!depsChanged) {
return cachedValue
}
}
// 更新缓存
cachedValue = value
cachedDeps = deps
return value
},
// 调度器
() => {
// 清除缓存
cachedValue = undefined
cachedDeps = []
}
)
return computedRef
}
2.3 watch 和 watchEffect 的实现
// watch函数实现
function watch<T = any>(
source: WatchSource<T> | WatchSource<T>[],
cb: WatchCallback<T>,
options?: WatchOptions
): StopHandle {
// 参数标准化
const {
immediate = false,
deep = false,
flush = 'pre',
onTrack,
onTrigger
} = options || {}
// 创建watcher实例
const watcher = new Watcher(
source,
cb,
{
immediate,
deep,
flush,
onTrack,
onTrigger
}
)
// 返回停止函数
return () => watcher.stop()
}
// Watcher类实现
class Watcher<T = any> {
private getter: () => T
private cb: WatchCallback<T>
private value?: T
private oldValue?: T
private active = true
private cleanup?: () => void
constructor(
source: WatchSource<T> | WatchSource<T>[],
cb: WatchCallback<T>,
options: WatchOptionsInternal
) {
// 1. 标准化回调函数
this.cb = cb
// 2. 创建getter函数
if (isRef(source)) {
// ref情况
this.getter = () => source.value
} else if (isReactive(source)) {
// reactive情况
this.getter = () => source
options.deep = true // reactive默认深度监听
} else if (isArray(source)) {
// 数组情况
this.getter = () =>
source.map(s => {
if (isRef(s)) {
return s.value
} else if (isReactive(s)) {
return traverse(s)
} else if (isFunction(s)) {
return s()
} else {
warn(`无效的watch源`)
}
})
} else if (isFunction(source)) {
// 函数情况
this.getter = source as () => T
} else {
// 默认情况
this.getter = NOOP
}
// 3. 深度监听的处理
if (options.deep) {
const baseGetter = this.getter
this.getter = () => traverse(baseGetter())
}
// 4. 立即执行选项
if (options.immediate) {
this.run()
} else {
// 否则收集初始值
this.oldValue = this.getter()
}
// 5. 创建effect
this.effect = new ReactiveEffect(
() => this.getter(),
() => this.scheduleRun()
)
// 6. 收集依赖
this.value = this.effect.run()
}
// 调度运行
private scheduleRun() {
if (!this.active) {
return
}
const { flush } = this.options
if (flush === 'sync') {
// 同步执行
this.run()
} else if (flush === 'post') {
// 在组件更新后执行
queuePostFlushCb(this.run.bind(this))
} else {
// 默认:在组件更新前执行
queueJob(this.run.bind(this))
}
}
// 执行watch
private run() {
if (!this.active) {
return
}
// 获取新值
const newValue = this.effect.run()
// 清理上一次的清理函数
if (this.cleanup) {
this.cleanup()
this.cleanup = undefined
}
// 值变化时的处理
if (
newValue !== this.value ||
isObject(newValue) ||
options.deep
) {
// 调用回调
const callbackResult = this.cb(newValue, this.value, (cleanupFn) => {
this.cleanup = cleanupFn
})
// 更新旧值
this.oldValue = newValue
this.value = newValue
// 如果回调返回清理函数
if (isFunction(callbackResult)) {
this.cleanup = callbackResult
}
}
}
// 停止watch
stop() {
if (this.active) {
this.active = false
this.effect.stop()
if (this.cleanup) {
this.cleanup()
}
}
}
}
// watchEffect实现
function watchEffect(
effect: WatchEffect,
options?: WatchOptionsBase
): StopHandle {
return watch(
effect,
null,
options as WatchOptions
)
}
// 深度遍历函数(用于深度监听)
function traverse(value: unknown, seen?: Set<unknown>): unknown {
// 如果不是对象或者是null,直接返回
if (!isObject(value) || value === null) {
return value
}
// 避免循环引用
seen = seen || new Set()
if (seen.has(value)) {
return value
}
seen.add(value)
// 递归遍历
if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], seen)
}
} else if (value instanceof Map) {
value.forEach((val, key) => {
traverse(val, seen)
})
} else if (value instanceof Set) {
value.forEach(val => {
traverse(val, seen)
})
} else {
// 普通对象
for (const key in value) {
traverse((value as any)[key], seen)
}
}
return value
}
第三章:编译系统与模板优化
3.1 模板编译器架构
Vue 3 的编译器是完全重写的,支持编译时优化(Compile-time Optimization)。
编译器架构图
编译流程:
模板字符串
↓
解析器(Parser)
↓ 生成AST
抽象语法树(AST)
↓
转换器(Transformer)
↓ 静态提升、Patch Flag等优化
优化后的AST
↓
代码生成器(Codegen)
↓ 生成渲染函数
渲染函数字符串
解析器实现(简化版)
// 解析器状态
interface ParserContext {
source: string
offset: number
line: number
column: number
inPre: boolean
inVPre: boolean
}
// 解析器主函数
function parse(template: string): RootNode {
const context = createParserContext(template)
return parseChildren(context, [])
}
// 解析子节点
function parseChildren(
context: ParserContext,
ancestors: ElementNode[]
): TemplateChildNode[] {
const nodes: TemplateChildNode[] = []
while (!isEnd(context, ancestors)) {
const s = context.source
let node: TemplateChildNode | undefined
if (startsWith(s, '{{')) {
// 插值表达式
node = parseInterpolation(context)
} else if (s[0] === '<') {
if (s[1] === '/') {
// 结束标签
parseTag(context, TagType.End, ancestors)
continue
} else if (/[a-z]/i.test(s[1])) {
// 元素标签
node = parseElement(context, ancestors)
}
}
if (!node) {
// 文本节点
node = parseText(context)
}
pushNode(nodes, node)
}
return nodes
}
// 解析元素
function parseElement(
context: ParserContext,
ancestors: ElementNode[]
): ElementNode | undefined {
// 1. 解析开始标签
const element = parseTag(context, TagType.Start)
if (element.isSelfClosing) {
return element
}
// 2. 解析子节点
ancestors.push(element)
const children = parseChildren(context, ancestors)
ancestors.pop()
element.children = children
// 3. 解析结束标签
parseTag(context, TagType.End)
return element
}
// 解析标签
function parseTag(
context: ParserContext,
type: TagType,
ancestors?: ElementNode[]
): ElementNode {
// 匹配标签
const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)!
const tag = match[1]
// 前进到标签名后
advanceBy(context, match[0].length)
// 解析属性
const props = parseAttributes(context)
// 检查是否自闭合
const isSelfClosing = startsWith(context.source, '/>')
advanceBy(context, isSelfClosing ? 2 : 1)
return {
type: NodeTypes.ELEMENT,
tag,
props,
children: [],
isSelfClosing
}
}
转换器实现
// 转换器上下文
interface TransformContext {
root: RootNode
parent: ParentNode | null
currentNode: RootNode | TemplateChildNode | null
helpers: Set<symbol>
hoists: (JSChildNode | null)[]
cached: number[]
identifiers: { [name: string]: number | undefined }
// 方法
helper(name: string): symbol
hoist(exp: JSChildNode): string
cache(exp: JSChildNode, isVNode?: boolean): string
}
// 主转换函数
function transform(root: RootNode, options: TransformOptions) {
const context = createTransformContext(root, options)
// 遍历AST
traverseNode(root, context)
// 创建根代码生成节点
root.codegenNode = createRootCodegenNode(root, context)
// 应用元信息
root.helpers = [...context.helpers]
root.hoists = context.hoists
root.components = context.components
root.directives = context.directives
root.imports = context.imports
root.cached = context.cached
}
// 遍历节点
function traverseNode(
node: RootNode | TemplateChildNode,
context: TransformContext
) {
context.currentNode = node
// 应用节点转换
const { nodeTransforms } = context
const exitFns: (() => void)[] = []
for (let i = 0; i < nodeTransforms.length; i++) {
const onExit = nodeTransforms[i](node, context)
if (onExit) {
exitFns.push(onExit)
}
if (!context.currentNode) {
// 节点被移除
return
} else {
node = context.currentNode
}
}
switch (node.type) {
case NodeTypes.INTERPOLATION:
// 处理插值表达式
break
case NodeTypes.ELEMENT:
case NodeTypes.ROOT:
// 遍历子节点
traverseChildren(node, context)
break
}
// 执行退出函数
let i = exitFns.length
while (i--) {
exitFns[i]()
}
}
// 关键转换:静态提升(Static Hoisting)
const transformHoist: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
// 检查是否可以进行静态提升
if (isStaticNode(node)) {
// 生成静态节点
const hoisted = createVNodeCall(
context.helper(CREATE_VNODE),
node.tag,
node.props,
node.children
)
// 添加到提升列表
context.hoists.push(hoisted)
// 替换为引用
node.codegenNode = context.helper(CREATE_HOIST)
}
}
}
// 关键转换:Patch Flags
const transformElement: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
return function postTransformElement() {
const vnodeTag = `"${node.tag}"`
const vnodeProps = buildProps(node, context)
const vnodeChildren = node.children
// 分析Patch Flags
let patchFlag = 0
if (vnodeProps.dynamicProps) {
patchFlag |= PatchFlags.FULL_PROPS
}
if (hasDynamicKeys(vnodeProps)) {
patchFlag |= PatchFlags.PROPS
}
if (node.children.length > 0) {
patchFlag |= PatchFlags.TEXT
}
node.codegenNode = createVNodeCall(
context.helper(CREATE_VNODE),
vnodeTag,
vnodeProps,
vnodeChildren,
patchFlag
)
}
}
}
// 创建VNode调用
function createVNodeCall(
context: TransformContext,
tag: string,
props?: PropsExpression,
children?: TemplateChildNode[],
patchFlag?: number,
dynamicProps?: string[]
): VNodeCall {
return {
type: NodeTypes.VNODE_CALL,
tag,
props,
children,
patchFlag,
dynamicProps
}
}
代码生成器实现
// 代码生成器上下文
interface CodegenContext {
source: string
code: string
line: number
column: number
offset: number
// 方法
push(code: string, node?: CodegenNode): void
indent(): void
deindent(): void
newline(): void
}
// 生成代码
function generate(
ast: RootNode,
options: CodegenOptions = {}
): CodegenResult {
const context = createCodegenContext(ast, options)
const { push, indent, deindent, newline } = context
// 生成导入语句
genFunctionPreamble(ast, context)
// 生成渲染函数
push(`function render(_ctx, _cache) {`)
indent()
// 生成with语句(开发模式)或直接引用(生产模式)
if (!options.isBrowser) {
push(`with (_ctx) {`)
indent()
}
// 生成hoisted变量的声明
if (ast.hoists.length) {
genHoists(ast.hoists, context)
newline()
}
// 生成返回语句
push(`return `)
if (ast.codegenNode) {
genNode(ast.codegenNode, context)
} else {
push(`null`)
}
if (!options.isBrowser) {
deindent()
push(`}`)
}
deindent()
push(`}`)
return {
code: context.code,
ast,
map: context.map
}
}
// 生成节点
function genNode(node: CodegenNode, context: CodegenContext) {
switch (node.type) {
case NodeTypes.ELEMENT:
case NodeTypes.VNODE_CALL:
genVNodeCall(node, context)
break
case NodeTypes.TEXT:
genText(node, context)
break
case NodeTypes.INTERPOLATION:
genInterpolation(node, context)
break
case NodeTypes.COMPOUND_EXPRESSION:
genCompoundExpression(node, context)
break
}
}
// 生成VNode调用
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
const { push, helper } = context
const { tag, props, children, patchFlag, dynamicProps } = node
// 调用createVNode或createElementVNode(优化版本)
const callHelper = patchFlag ? helper(CREATE_ELEMENT_VNODE) : helper(CREATE_VNODE)
push(`${callHelper}(`)
// tag
genNode(tag, context)
// props
if (props) {
push(`, `)
genNode(props, context)
} else {
push(`, null`)
}
// children
if (children) {
push(`, `)
if (Array.isArray(children)) {
genNodeList(children, context)
} else {
genNode(children, context)
}
} else if (patchFlag) {
push(`, null`)
}
// patchFlag和dynamicProps
if (patchFlag) {
push(`, ${patchFlag}`)
if (dynamicProps) {
push(`, `)
genNodeList(dynamicProps, context)
}
}
push(`)`)
}
// 生成静态提升
function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
const { push, newline } = context
push(`const _hoisted = []`)
newline()
for (let i = 0; i < hoists.length; i++) {
const hoist = hoists[i]
if (hoist) {
push(`_hoisted[${i}] = `)
genNode(hoist, context)
newline()
}
}
}
// 生成属性
function genProps(props: PropsExpression, context: CodegenContext) {
if (!props || props.type === NodeTypes.JS_CALL_EXPRESSION) {
// 动态属性
genNode(props, context)
} else {
// 静态属性
push(`{ `)
for (let i = 0; i < props.properties.length; i++) {
const prop = props.properties[i]
// key
push(`${prop.key}: `)
// value
if (prop.value.type === NodeTypes.SIMPLE_EXPRESSION) {
const { content, isStatic } = prop.value
if (isStatic) {
push(JSON.stringify(content))
} else {
push(content)
}
} else {
genNode(prop.value, context)
}
if (i < props.properties.length - 1) {
push(`, `)
}
}
push(` }`)
}
}
3.2 编译时优化详解
Patch Flags 系统
// Patch Flags 枚举
export const enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 1 << 1, // 动态class
STYLE = 1 << 2, // 动态style
PROPS = 1 << 3, // 动态props(不包括class/style)
FULL_PROPS = 1 << 4, // 有动态key的props
HYDRATE_EVENTS = 1 << 5, // 带事件监听器
STABLE_FRAGMENT = 1 << 6, // 子节点顺序不会改变的Fragment
KEYED_FRAGMENT = 1 << 7, // 带key的Fragment
UNKEYED_FRAGMENT = 1 << 8, // 不带key的Fragment
NEED_PATCH = 1 << 9, // 只需要非props的patch
DYNAMIC_SLOTS = 1 << 10, // 动态slot
DEV_ROOT_FRAGMENT = 1 << 11, // 开发环境的根Fragment
// 特殊标志
HOISTED = -1, // 静态提升的节点
BAIL = -2 // 表示diff算法应该完全进行
}
// Patch Flag 分析器
function analyzePatchFlag(node: ElementNode): number {
let patchFlag = 0
// 分析props
if (node.props.length > 0) {
const hasDynamicProps = node.props.some(prop => {
if (prop.type === NodeTypes.ATTRIBUTE) {
// 静态属性
return false
} else if (prop.type === NodeTypes.DIRECTIVE) {
// 指令
return prop.name !== 'bind' || !prop.arg || prop.arg.isStatic
}
return true
})
if (hasDynamicProps) {
patchFlag |= PatchFlags.PROPS
}
}
// 分析class
const classBinding = findProp(node, 'class')
if (classBinding && classBinding.type === NodeTypes.DIRECTIVE) {
patchFlag |= PatchFlags.CLASS
}
// 分析style
const styleBinding = findProp(node, 'style')
if (styleBinding && styleBinding.type === NodeTypes.DIRECTIVE) {
patchFlag |= PatchFlags.STYLE
}
// 分析事件
if (node.props.some(isEvent)) {
patchFlag |= PatchFlags.HYDRATE_EVENTS
}
// 分析子节点
if (node.children.length > 0) {
const hasDynamicChildren = node.children.some(child => {
return !isStaticNode(child)
})
if (hasDynamicChildren) {
patchFlag |= PatchFlags.TEXT
}
}
return patchFlag
}
// 静态节点分析
function isStaticNode(node: TemplateChildNode): boolean {
switch (node.type) {
case NodeTypes.ELEMENT:
// 元素节点:检查所有属性和子节点是否都是静态的
return node.props.every(isStaticProp) &&
node.children.every(isStaticNode)
case NodeTypes.TEXT:
// 文本节点:总是静态的
return true
case NodeTypes.COMMENT:
// 注释节点:总是静态的
return true
case NodeTypes.INTERPOLATION:
case NodeTypes.COMPOUND_EXPRESSION:
// 插值和复合表达式:动态的
return false
default:
return false
}
}
// 静态属性分析
function isStaticProp(prop: AttributeNode | DirectiveNode): boolean {
if (prop.type === NodeTypes.ATTRIBUTE) {
// 普通属性:静态
return true
}
if (prop.type === NodeTypes.DIRECTIVE) {
// 指令:检查是否绑定
if (prop.name === 'bind') {
// v-bind:检查参数和表达式是否静态
const { arg, exp } = prop
return (!arg || arg.isStatic) &&
(!exp || exp.isStatic)
}
// 其他指令:动态
return false
}
return false
}
静态提升优化
// 静态提升分析
function analyzeStatic(node: RootNode, context: TransformContext) {
// 递归分析所有节点
walk(node, {
// 进入节点
enter(node, parent) {
if (node.type === NodeTypes.ELEMENT) {
// 检查是否可以提升
if (canHoist(node)) {
// 标记为静态提升
node.hoisted = true
// 添加到提升列表
const hoisted = createHoistedNode(node)
context.hoists.push(hoisted)
// 替换节点引用
replaceNode(node, parent, createHoistedRef(hoisted))
}
}
}
})
}
// 检查节点是否可以提升
function canHoist(node: ElementNode): boolean {
// 1. 不能有动态属性
if (node.props.some(isDynamicProp)) {
return false
}
// 2. 不能有指令(除了静态的v-bind)
if (node.props.some(prop =>
prop.type === NodeTypes.DIRECTIVE &&
prop.name !== 'bind'
)) {
return false
}
// 3. 不能有动态子节点
if (node.children.some(child => !isStaticNode(child))) {
return false
}
// 4. 不能是组件
if (isComponent(node.tag)) {
return false
}
return true
}
// 创建提升节点
function createHoistedNode(node: ElementNode): HoistedNode {
// 提取静态属性
const staticProps = node.props.filter(isStaticProp)
// 提取静态子节点
const staticChildren = node.children.filter(isStaticNode)
return {
type: NodeTypes.HOISTED,
props: staticProps,
children: staticChildren,
codegenNode: null
}
}
// 生成提升节点的代码
function genHoistedNode(node: HoistedNode, context: CodegenContext) {
const { push, helper } = context
// 调用createStaticVNode
push(`${helper(CREATE_STATIC_VNODE)}(`)
// 生成静态内容
push(`[`)
// 生成标签
push(`"${node.tag}"`)
// 生成属性
if (node.props.length > 0) {
push(`, `)
genProps(node.props, context)
}
// 生成子节点
if (node.children.length > 0) {
push(`, `)
genNodeList(node.children, context)
}
push(`]`)
push(`)`)
}
树结构压平优化
// 树结构压平分析
function analyzeTreeFlattening(node: ElementNode): boolean {
// 检查是否可以进行树结构压平
if (!node.children || node.children.length === 0) {
return false
}
// 1. 必须是Fragment或div等容器元素
if (!isFragmentLike(node)) {
return false
}
// 2. 不能有动态props
if (hasDynamicProps(node)) {
return false
}
// 3. 不能有条件或循环
if (hasConditionalOrLoop(node)) {
return false
}
// 4. 检查子节点是否都是静态或只有一层嵌套
let canFlatten = true
let dynamicChildCount = 0
for (const child of node.children) {
if (isStaticNode(child)) {
continue
}
if (child.type === NodeTypes.ELEMENT) {
// 嵌套元素
if (isFragmentLike(child)) {
// 嵌套Fragment,可以合并
dynamicChildCount += countDynamicChildren(child)
} else {
// 普通元素,计数+1
dynamicChildCount++
}
} else {
// 文本或插值
dynamicChildCount++
}
if (dynamicChildCount > 10) {
// 动态子节点太多,不适合压平
canFlatten = false
break
}
}
return canFlatten
}
// 应用树结构压平
function flattenTree(node: ElementNode, context: TransformContext) {
const flattenedChildren: TemplateChildNode[] = []
// 递归压平子节点
function flattenChildren(children: TemplateChildNode[]) {
for (const child of children) {
if (child.type === NodeTypes.ELEMENT && isFragmentLike(child)) {
// 压平Fragment
flattenChildren(child.children)
} else {
// 直接添加
flattenedChildren.push(child)
}
}
}
flattenChildren(node.children)
// 更新节点
node.children = flattenedChildren
node.patchFlag |= PatchFlags.STABLE_FRAGMENT
// 标记为已压平
node.flattened = true
}
// 在代码生成时处理压平
function genFlattenedVNodeCall(node: VNodeCall, context: CodegenContext) {
const { push, helper } = context
if (node.flattened) {
// 生成压平的Fragment
push(`${helper(CREATE_VNODE)}(`)
push(`${helper(FRAGMENT)}, null, `)
// 直接生成子节点数组
push(`[`)
genNodeList(node.children, context)
push(`]`)
push(`)`)
} else {
// 正常生成
genVNodeCall(node, context)
}
}
第四章:渲染系统与虚拟DOM
4.1 渲染器架构
渲染器核心实现
// 渲染器创建函数
function createRenderer(options: RendererOptions) {
const {
patchProp,
insert,
remove,
createElement,
createText,
createComment,
setText,
setElementText,
parentNode,
nextSibling,
querySelector
} = options
// 渲染函数
const render: RootRenderFunction = (vnode, container) => {
if (vnode == null) {
// 卸载
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
// 挂载或更新
patch(container._vnode || null, vnode, container)
}
// 保存vnode引用
container._vnode = vnode
}
// 核心patch函数
const patch: PatchFn = (
n1, // 旧vnode
n2, // 新vnode
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
namespace = undefined,
slotScopeIds = null,
optimized = false
) => {
// 如果新旧vnode相同,直接返回
if (n1 === n2) {
return
}
// 如果类型不同,卸载旧节点
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
unmount(n1, parentComponent, parentSuspense, true)
n1 = null
}
const { type, ref, shapeFlag } = n2
switch (type) {
case Text:
// 处理文本节点
processText(n1, n2, container, anchor)
break
case Comment:
// 处理注释节点
processCommentNode(n1, n2, container, anchor)
break
case Static:
// 处理静态节点
if (n1 == null) {
mountStaticNode(n2, container, anchor)
}
break
case Fragment:
// 处理Fragment
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
// 处理元素节点
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
// 处理组件
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
// 处理Teleport
;(type as typeof TeleportImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized,
internals
)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
// 处理Suspense
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized,
internals
)
}
}
// 处理ref
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2)
}
}
// 处理元素节点
const processElement = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean
) => {
if (n1 == null) {
// 挂载
mountElement(
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else {
// 更新
patchElement(
n1,
n2,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
}
}
// 挂载元素
const mountElement = (
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean
) => {
let el: RendererElement
let vnodeHook: VNodeHook | undefined | null
const { type, props, shapeFlag, transition, scopeId, patchFlag, dirs } = vnode
// 创建DOM元素
el = vnode.el = createElement(type as string, namespace)
// 设置文本内容
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(el, vnode.children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
// 挂载子节点
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
}
// 设置props
if (props) {
for (const key in props) {
if (key !== 'value' && !isReservedProp(key)) {
patchProp(el, key, null, props[key], namespace)
}
}
// 特殊处理value属性
if ('value' in props) {
patchProp(el, 'value', null, props.value, namespace)
}
}
// 设置scope id
if (scopeId) {
setScopeId(el, scopeId)
}
// 执行指令
if (dirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
}
// 触发vnode hook
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
// 插入到DOM
insert(el, container, anchor)
// 执行transition
if (transition && !transition.persisted) {
transition.enter(el)
}
// 执行指令的mounted钩子
if (dirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
}
// 触发vnode hook
if ((vnodeHook = props && props.onVnodeMounted)) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
}
// 更新元素
const patchElement = (
n1: VNode,
n2: VNode,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean
) => {
const el = (n2.el = n1.el!)
let { patchFlag, dynamicChildren, dirs } = n2
// 旧的props
const oldProps = n1.props || EMPTY_OBJ
// 新的props
const newProps = n2.props || EMPTY_OBJ
// 执行beforeUpdate钩子
if ((n2Hook = newProps.onVnodeBeforeUpdate)) {
invokeVNodeHook(n2Hook, parentComponent, n2, n1)
}
if (dirs) {
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
}
// 优化:如果有dynamicChildren,只更新动态部分
if (dynamicChildren && optimized) {
patchBlockChildren(
n1.dynamicChildren!,
dynamicChildren,
el,
parentComponent,
parentSuspense,
namespace,
slotScopeIds
)
} else if (!optimized) {
// 完全diff
patchChildren(
n1,
n2,
el,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
false
)
}
// 更新props
if (patchFlag > 0) {
// 有patchFlag,根据标志更新
if (patchFlag & PatchFlags.FULL_PROPS) {
// 全量更新props
patchProps(
el,
n2,
oldProps,
newProps,
parentComponent,
parentSuspense,
namespace
)
} else {
// 部分更新
if (patchFlag & PatchFlags.CLASS) {
if (oldProps.class !== newProps.class) {
patchProp(el, 'class', null, newProps.class, namespace)
}
}
if (patchFlag & PatchFlags.STYLE) {
patchProp(el, 'style', oldProps.style, newProps.style, namespace)
}
if (patchFlag & PatchFlags.PROPS) {
// 更新动态props
const propsToUpdate = n2.dynamicProps!
for (let i = 0; i < propsToUpdate.length; i++) {
const key = propsToUpdate[i]
const prev = oldProps[key]
const next = newProps[key]
if (next !== prev || key === 'value') {
patchProp(el, key, prev, next, namespace)
}
}
}
}
} else if (!optimized && dynamicChildren == null) {
// 没有优化,全量diff props
patchProps(
el,
n2,
oldProps,
newProps,
parentComponent,
parentSuspense,
namespace
)
}
// 执行updated钩子
if ((n2Hook = newProps.onVnodeUpdated)) {
queuePostRenderEffect(() => {
invokeVNodeHook(n2Hook!, parentComponent, n2, n1)
}, parentSuspense)
}
if (dirs) {
invokeDirectiveHook(n2, n1, parentComponent, 'updated')
}
}
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}
4.2 虚拟DOM Diff算法
Diff算法的核心实现
// 核心diff算法
function patchKeyedChildren(
c1: VNode[],
c2: VNodeArrayChildren,
container: RendererElement,
parentAnchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean
) {
let i = 0
const l2 = c2.length
let e1 = c1.length - 1
let e2 = l2 - 1
// 1. 从前面开始比较
while (i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i] as VNode
if (isSameVNodeType(n1, n2)) {
patch(
n1,
n2,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else {
break
}
i++
}
// 2. 从后面开始比较
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2] as VNode
if (isSameVNodeType(n1, n2)) {
patch(
n1,
n2,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else {
break
}
e1--
e2--
}
// 3. 情况1:新节点更多(需要挂载)
if (i > e1) {
if (i <= e2) {
const nextPos = e2 + 1
const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
while (i <= e2) {
patch(
null,
c2[i] as VNode,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
i++
}
}
}
// 4. 情况2:旧节点更多(需要卸载)
else if (i > e2) {
while (i <= e1) {
unmount(c1[i], parentComponent, parentSuspense, true)
i++
}
}
// 5. 情况3:未知序列(需要移动和patch)
else {
const s1 = i
const s2 = i
// 5.1 构建key到index的映射
const keyToNewIndexMap: Map<string | number | symbol, number> = new Map()
for (i = s2; i <= e2; i++) {
const nextChild = c2[i] as VNode
if (nextChild.key != null) {
keyToNewIndexMap.set(nextChild.key, i)
}
}
// 5.2 遍历旧节点
let j
let patched = 0
const toBePatched = e2 - s2 + 1
let moved = false
let maxNewIndexSoFar = 0
// 新节点在旧节点中的索引
const newIndexToOldIndexMap = new Array(toBePatched)
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
for (i = s1; i <= e1; i++) {
const prevChild = c1[i]
if (patched >= toBePatched) {
// 所有新节点都已经patch,剩下的旧节点需要卸载
unmount(prevChild, parentComponent, parentSuspense, true)
continue
}
let newIndex
if (prevChild.key != null) {
newIndex = keyToNewIndexMap.get(prevChild.key)
} else {
// 没有key,需要遍历查找
for (j = s2; j <= e2; j++) {
if (
newIndexToOldIndexMap[j - s2] === 0 &&
isSameVNodeType(prevChild, c2[j] as VNode)
) {
newIndex = j
break
}
}
}
if (newIndex === undefined) {
// 没有找到对应的新节点,卸载
unmount(prevChild, parentComponent, parentSuspense, true)
} else {
// 更新映射
newIndexToOldIndexMap[newIndex - s2] = i + 1
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex
} else {
moved = true
}
patch(
prevChild,
c2[newIndex] as VNode,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
patched++
}
}
// 5.3 移动和挂载
const increasingNewIndexSequence = moved
? getSequence(newIndexToOldIndexMap)
: EMPTY_ARR
j = increasingNewIndexSequence.length - 1
for (i = toBePatched - 1; i >= 0; i--) {
const nextIndex = s2 + i
const nextChild = c2[nextIndex] as VNode
const anchor =
nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
if (newIndexToOldIndexMap[i] === 0) {
// 新节点,需要挂载
patch(
null,
nextChild,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else if (moved) {
// 需要移动
if (j < 0 || i !== increasingNewIndexSequence[j]) {
move(nextChild, container, anchor, MoveType.REORDER)
} else {
j--
}
}
}
}
}
// 最长递增子序列算法(用于优化移动操作)
function getSequence(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i, j, u, v, c
const len = arr.length
for (i = 0; i < len; i++) {
const arrI = arr[i]
if (arrI !== 0) {
j = result[result.length - 1]
if (arr[j] < arrI) {
p[i] = j
result.push(i)
continue
}
u = 0
v = result.length - 1
while (u < v) {
c = (u + v) >> 1
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1]
}
result[u] = i
}
}
}
u = result.length
v = result[u - 1]
while (u-- > 0) {
result[u] = v
v = p[v]
}
return result
}
Patch Flag 在Diff中的优化应用
// 基于Patch Flag的优化diff
function patchBlockChildren(
oldChildren: VNode[],
newChildren: VNode[],
fallbackContainer: RendererElement,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null
) {
for (let i = 0; i < newChildren.length; i++) {
const oldVNode = oldChildren[i]
const newVNode = newChildren[i]
// 确定容器
const container =
oldVNode.el &&
(oldVNode.type === Fragment ||
isSameVNodeType(oldVNode, newVNode) ||
oldVNode.shapeFlag & (ShapeFlags.COMPONENT | ShapeFlags.TELEPORT))
? parentNode(oldVNode.el)!
: fallbackContainer
patch(
oldVNode,
newVNode,
container,
null,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
true // 优化模式
)
}
}
// 快速路径:处理静态节点
function patchStaticNode(
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null
) {
if (n1 == null) {
// 挂载静态节点
n2.el = n1 ? n1.el : createStaticVNode(n2.children)
insert(n2.el, container, anchor)
}
// 静态节点不需要更新
}
// 处理文本节点
function processText(
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null
) {
if (n1 == null) {
// 挂载
n2.el = createText(n2.children as string)
insert(n2.el, container, anchor)
} else {
// 更新
const el = (n2.el = n1.el!)
if (n2.children !== n1.children) {
setText(el, n2.children as string)
}
}
}
第五章:组件系统与生命周期
5.1 组件实例化过程
组件实例的创建与初始化
// 创建组件实例
function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
): ComponentInternalInstance {
const type = vnode.type as ConcreteComponent
// 创建组件实例
const instance: ComponentInternalInstance = {
uid: uid++,
vnode,
type,
parent,
appContext: parent ? parent.appContext : vnode.appContext!,
// 状态
isMounted: false,
isUnmounted: false,
isDeactivated: false,
// 响应式状态
props: EMPTY_OBJ as any,
attrs: EMPTY_OBJ,
slots: EMPTY_OBJ,
emit: null as any,
// 生命周期
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
setupState: EMPTY_OBJ,
setupContext: null,
// 渲染相关
subTree: null!,
update: null!,
render: null,
// 依赖注入
provides: parent ? parent.provides : Object.create(appContext.provides),
// 其他
components: Object.create(type.components || null),
directives: Object.create(type.directives || null),
propsOptions: normalizePropsOptions(type, instance.appContext),
emitsOptions: normalizeEmitsOptions(type, instance.appContext),
// 代理
proxy: null,
exposed: null,
exposeProxy: null,
// 异步组件
asyncDep: null,
asyncResolved: false,
// Suspense
suspense,
suspendId: suspense ? suspense.pendingId : 0,
asyncDep: null,
asyncResolved: false,
// 错误处理
error: null,
// 缓存
ce: null
}
// 开发环境额外属性
if (__DEV__) {
instance.ctx = createDevRenderContext(instance)
} else {
instance.ctx = { _: instance }
}
// 创建emit函数
instance.emit = emit.bind(null, instance) as any
// 创建代理
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
return instance
}
// 设置组件实例
function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
isInSSRComponentSetup = isSSR
const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance)
// 初始化props
initProps(instance, props, isStateful, isSSR)
// 初始化slots
initSlots(instance, children)
// 设置状态组件
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
isInSSRComponentSetup = false
return setupResult
}
// 设置状态组件
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
// 创建代理访问缓存
instance.accessCache = Object.create(null)
// 创建公共实例代理
instance.proxy = markRaw(
new Proxy(instance.ctx, PublicInstanceProxyHandlers)
)
// 执行setup函数
const { setup } = Component
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
// 设置当前实例
currentInstance = instance
// 暂停跟踪(setup中不跟踪依赖)
pauseTracking()
// 执行setup
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
// 恢复跟踪
resetTracking()
currentInstance = null
// 处理setup结果
if (isPromise(setupResult)) {
// 异步组件
if (isSSR) {
return setupResult.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
} else if (__FEATURE_SUSPENSE__) {
instance.asyncDep = setupResult
} else {
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`
)
}
} else {
handleSetupResult(instance, setupResult, isSSR)
}
} else {
// 没有setup函数,完成组件设置
finishComponentSetup(instance, isSSR)
}
}
// 处理setup结果
function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean
) {
if (isFunction(setupResult)) {
// setup返回渲染函数
instance.render = setupResult as InternalRenderFunction
} else if (isObject(setupResult)) {
// setup返回对象,作为响应式状态
if (__DEV__ && isVNode(setupResult)) {
warn(
`setup() should not return VNodes directly - ` +
`return a render function instead.`
)
}
// 将setup返回的状态设置为响应式
instance.setupState = proxyRefs(setupResult)
} else if (__DEV__ && setupResult !== undefined) {
warn(
`setup() should return an object. Received: ${setupResult === null ? 'null' : typeof setupResult}`
)
}
// 完成组件设置
finishComponentSetup(instance, isSSR)
}
// 完成组件设置
function finishComponentSetup(
instance: ComponentInternalInstance,
isSSR: boolean,
skipOptions?: boolean
) {
const Component = instance.type as ComponentOptions
// 模板/渲染函数标准化
if (!instance.render) {
// 只有运行时编译时才编译模板
if (!isSSR && compile && !Component.render) {
const template = Component.template
if (template) {
// 编译模板
const { isCustomElement, compilerOptions } = instance.appContext.config
const { delimiters, compilerOptions: componentCompilerOptions } = Component
const finalCompilerOptions: CompilerOptions = extend(
extend(
{
isCustomElement,
delimiters
},
compilerOptions
),
componentCompilerOptions
)
// 编译
Component.render = compile(template, finalCompilerOptions)
}
}
// 设置渲染函数
instance.render = (Component.render || NOOP) as InternalRenderFunction
// 使用with块(运行时编译)
if (instance.render._rc) {
instance.withProxy = new Proxy(
instance.ctx,
RuntimeCompiledPublicInstanceProxyHandlers
)
}
}
// 兼容Options API
if (__FEATURE_OPTIONS_API__ && !skipOptions) {
setCurrentInstance(instance)
pauseTracking()
// 应用Options API
applyOptions(instance)
resetTracking()
setCurrentInstance(null)
}
// 处理缺少渲染函数的情况
if (__DEV__ && !instance.render && !isSSR) {
if (!compile && Component.template) {
warn(
`Component provided template option but ` +
`runtime compilation is not supported in this build of Vue.` +
` Use "vue.esm-browser.js" instead.`
)
} else {
warn(`Component is missing template or render function.`)
}
}
}
5.2 生命周期实现
生命周期钩子的注册与调用
// 生命周期钩子类型
type LifecycleHook =
| 'beforeCreate'
| 'created'
| 'beforeMount'
| 'mounted'
| 'beforeUpdate'
| 'updated'
| 'beforeUnmount'
| 'unmounted'
| 'renderTracked'
| 'renderTriggered'
| 'activated'
| 'deactivated'
| 'errorCaptured'
| 'serverPrefetch'
// 生命周期钩子调用
function callHook(
hook: LifecycleHook,
instance: ComponentInternalInstance,
args?: any[]
) {
// 设置当前实例
const prevInstance = currentInstance
setCurrentInstance(instance)
// 获取钩子函数
const handlers = instance.type[hook]
if (handlers) {
// 调用所有注册的钩子
for (let i = 0; i < handlers.length; i++) {
invokeWithErrorHandling(
handlers[i],
instance,
ErrorCodes.LIFECYCLE_HOOK,
args
)
}
}
// 恢复之前的实例
setCurrentInstance(prevInstance)
}
// Composition API生命周期注册
function injectHook(
type: LifecycleHook,
hook: Function & { __weh?: Function },
target: ComponentInternalInstance | null = currentInstance,
prepend: boolean = false
): Function | undefined {
if (target) {
// 获取或创建钩子数组
const hooks = target[type] || (target[type] = [])
// 包装钩子函数
const wrappedHook =
hook.__weh ||
(hook.__weh = (...args: unknown[]) => {
if (target.isUnmounted) {
return
}
// 暂停跟踪
pauseTracking()
// 设置当前实例
setCurrentInstance(target)
// 调用钩子
const res = callWithAsyncErrorHandling(hook, target, type, args)
// 恢复当前实例
setCurrentInstance(null)
// 恢复跟踪
resetTracking()
return res
})
// 添加钩子
if (prepend) {
hooks.unshift(wrappedHook)
} else {
hooks.push(wrappedHook)
}
return wrappedHook
} else if (__DEV__) {
const apiName = `on${capitalize(type)}`
warn(
`${apiName} is called when there is no active component instance to be ` +
`associated with. ` +
`Lifecycle injection APIs can only be used during execution of setup().`
)
}
}
// 创建各种生命周期钩子函数
const createHook = (lifecycle: LifecycleHook) => {
return (hook: Function, target: ComponentInternalInstance | null = currentInstance) =>
injectHook(lifecycle, hook, target)
}
// 导出Composition API生命周期函数
export const onBeforeMount = createHook('beforeMount')
export const onMounted = createHook('mounted')
export const onBeforeUpdate = createHook('beforeUpdate')
export const onUpdated = createHook('updated')
export const onBeforeUnmount = createHook('beforeUnmount')
export const onUnmounted = createHook('unmounted')
export const onErrorCaptured = createHook('errorCaptured')
export const onRenderTracked = createHook('renderTracked')
export const onRenderTriggered = createHook('renderTriggered')
export const onActivated = createHook('activated')
export const onDeactivated = createHook('deactivated')
// 在组件挂载和更新时调用生命周期钩子
function componentUpdateFn() {
if (!instance.isMounted) {
// 挂载阶段
let vnodeHook: VNodeHook | null | undefined
// 调用beforeMount钩子
if (__FEATURE_OPTIONS_API__) {
callHook('beforeMount', instance)
}
// 执行渲染
const subTree = (instance.subTree = renderComponentRoot(instance))
// 挂载到容器
patch(null, subTree, container, anchor, instance, parentSuspense, namespace)
// 保存vnode
initialVNode.el = subTree.el
// 调用mounted钩子
if (__FEATURE_OPTIONS_API__) {
callHook('mounted', instance)
}
// 标记为已挂载
instance.isMounted = true
// 调用activated钩子(如果是从keep-alive中激活)
if (initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
instance.a && queuePostRenderEffect(instance.a, parentSuspense)
callHook('activated', instance)
}
} else {
// 更新阶段
let { next, vnode } = instance
// 调用beforeUpdate钩子
if (__FEATURE_OPTIONS_API__) {
callHook('beforeUpdate', instance)
}
// 更新vnode引用
if (next) {
next.el = vnode.el
updateComponentPreRender(instance, next, optimized)
}
// 执行渲染
const nextTree = renderComponentRoot(instance)
// 保存之前的子树
const prevTree = instance.subTree
instance.subTree = nextTree
// 执行patch更新
patch(
prevTree,
nextTree,
parentNode(prevTree.el!)!,
getNextHostNode(prevTree),
instance,
parentSuspense,
namespace
)
// 更新el引用
next.el = nextTree.el
// 调用updated钩子
if (__FEATURE_OPTIONS_API__) {
callHook('updated', instance)
}
}
}
5.3 组件更新调度
响应式更新与调度系统
// 组件更新函数
const componentUpdateFn = () => {
if (!instance.isMounted) {
// 初始挂载
// ... 挂载逻辑
} else {
// 更新
let { next, bu, u, parent, vnode } = instance
// 调用beforeUpdate钩子
if (bu) {
invokeArrayFns(bu)
}
// 执行render生成vnode
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
// patch更新
patch(
prevTree,
nextTree,
// 父节点可能在teleport中
hostParentNode(prevTree.el!)!,
// 锚点
getNextHostNode(prevTree),
instance,
parentSuspense,
isSVG
)
// 更新el引用
next.el = nextTree.el
// 调用updated钩子
if (u) {
queuePostRenderEffect(u, parentSuspense)
}
}
}
// 创建组件effect
const effect = (instance.effect = new ReactiveEffect(
componentUpdateFn,
() => queueJob(update),
instance.scope // 在组件effect作用域中跟踪它
))
// 组件更新函数
const update: SchedulerJob = (instance.update = () => effect.run())
update.id = instance.uid
// 调度器
const queue: SchedulerJob[] = []
let flushIndex = 0
// 队列任务
function queueJob(job: SchedulerJob) {
if (
!queue.length ||
!queue.includes(job, isFlushing ? flushIndex + 1 : flushIndex)
) {
// 将任务添加到队列
if (job.id == null) {
queue.push(job)
} else {
// 按id插入,确保父组件在子组件之前更新
queue.splice(findInsertionIndex(job.id), 0, job)
}
// 刷新队列
queueFlush()
}
}
// 刷新队列
function queueFlush() {
if (!isFlushing && !isFlushPending) {
isFlushPending = true
// 在微任务中执行刷新
currentFlushPromise = resolvedPromise.then(flushJobs)
}
}
// 执行队列中的任务
function flushJobs(seen?: CountMap) {
isFlushPending = false
isFlushing = true
if (__DEV__) {
seen = seen || new Map()
}
// 先执行预任务队列
flushPreFlushCbs(seen)
// 对队列排序:确保
// 1. 组件从父到子更新(因为父组件总是在子组件之前创建)
// 2. 如果父组件在更新期间卸载了组件,可以跳过它的更新
queue.sort((a, b) => getId(a) - getId(b))
try {
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
const job = queue[flushIndex]
if (job && job.active !== false) {
if (__DEV__ && checkRecursiveUpdates(seen!, job)) {
continue
}
// 执行任务
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
}
}
} finally {
// 重置状态
flushIndex = 0
queue.length = 0
// 执行后置任务队列
flushPostFlushCbs(seen)
isFlushing = false
currentFlushPromise = null
// 如果在刷新期间有新的任务被添加,重新执行
if (queue.length || pendingPostFlushCbs.length) {
flushJobs(seen)
}
}
}
第六章:高级特性与性能优化
6.1 异步组件与Suspense
异步组件实现
// 定义异步组件
function defineAsyncComponent(
source: AsyncComponentLoader | AsyncComponentOptions
): Component {
if (isFunction(source)) {
source = { loader: source }
}
const {
loader,
loadingComponent,
errorComponent,
delay = 200,
timeout, // undefined = 永不超时
suspensible = true,
onError: userOnError
} = source
// 存储最近加载的组件
let loadedComp: Component | undefined
let error: Error | undefined
let delayTimeout: NodeJS.Timeout | undefined
let timeoutTimeout: NodeJS.Timeout | undefined
// 加载状态
let loading = false
let loadingInstance: ComponentInternalInstance | null = null
// 重试次数
let retries = 0
const retry = () => {
retries++
error = undefined
return load()
}
// 加载函数
const load = (): Promise<Component> => {
let thisRequest: Promise<Component>
return (
loader()
.then(comp => {
// 处理ES模块默认导出
if (
comp &&
(comp.__esModule || comp[Symbol.toStringTag] === 'Module')
) {
comp = comp.default
}
// 开发环境检查
if (__DEV__ && !isObject(comp) && !isFunction(comp)) {
throw new Error(
`无效的异步组件加载结果:${comp}. 期望一个对象或函数。`
)
}
loadedComp = comp
return comp
})
.catch(err => {
error = err
if (userOnError) {
// 用户自定义错误处理
return new Promise((resolve, reject) => {
const userRetry = () => resolve(retry())
const userFail = () => reject(err)
userOnError(err, userRetry, userFail, retries + 1)
})
} else {
throw err
}
})
)
}
// 返回包装组件
return defineComponent({
name: 'AsyncComponentWrapper',
__asyncLoader: load,
__asyncResolved: false,
setup() {
const instance = currentInstance!
// 如果已经加载,直接返回
if (loadedComp) {
return () => createInnerComp(loadedComp!, instance)
}
// 错误处理
const onError = (err: Error) => {
error = err
loading = false
}
// 如果有延迟,设置定时器
if (delay) {
delayTimeout = setTimeout(() => {
loading = true
loadingInstance = instance
}, delay)
}
// 如果超时
if (timeout != null) {
timeoutTimeout = setTimeout(() => {
onError(new Error(`异步组件加载超时 (${timeout}ms)`))
}, timeout)
}
// 执行加载
load()
.then(() => {
if (!instance.isUnmounted) {
// 清除定时器
if (delayTimeout) {
clearTimeout(delayTimeout)
delayTimeout = undefined
}
if (timeoutTimeout) {
clearTimeout(timeoutTimeout)
timeoutTimeout = undefined
}
// 更新状态
loading = false
loadingInstance = null
error = undefined
loadedComp = loadedComp
}
})
.catch(err => {
onError(err)
})
// 返回渲染函数
return () => {
if (loadedComp && !error) {
// 加载成功,渲染组件
return createInnerComp(loadedComp, instance)
} else if (error && errorComponent) {
// 错误状态,渲染错误组件
return createVNode(errorComponent, {
error
})
} else if (loadingComponent && loading) {
// 加载中,渲染加载组件
return createVNode(loadingComponent)
}
// 默认返回注释节点
return createVNode(Comment, null, 'async component')
}
}
})
}
// 创建内部组件
function createInnerComp(
comp: Component,
parent: ComponentInternalInstance
) {
// 解析出选项
const { ref, props, children, ce } = parent.vnode
const vnode = createVNode(comp, props, children)
// 确保继承实例属性
vnode.ref = ref
vnode.ce = ce
// 标记为已解析
;(vnode.type as ComponentOptions).__asyncResolved = true
return vnode
}
Suspense实现
// Suspense组件实现
const SuspenseImpl = {
name: 'Suspense',
// 在Suspense边界内,Suspense有自己的vnode
__isSuspense: true,
process(
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals
) {
if (n1 == null) {
// 挂载Suspense
mountSuspense(
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized,
rendererInternals
)
} else {
// 更新Suspense
patchSuspense(
n1,
n2,
container,
anchor,
parentComponent,
namespace,
slotScopeIds,
optimized,
rendererInternals
)
}
}
}
// 挂载Suspense
function mountSuspense(
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals
) {
const {
p: patch,
o: { createElement }
} = rendererInternals
// 创建隐藏容器
const hiddenContainer = createElement('div')
// 创建Suspense边界
const suspense = (vnode.suspense = createSuspenseBoundary(
vnode,
parentSuspense,
parentComponent,
container,
hiddenContainer,
anchor,
namespace,
slotScopeIds,
optimized,
rendererInternals
))
// 开始挂载
suspense.pendingBranch = vnode.ssContent!
suspense.isInFallback = false
// 挂载默认内容
patch(
null,
vnode.ssContent!,
hiddenContainer,
null,
parentComponent,
suspense,
namespace,
slotScopeIds,
optimized
)
// 检查是否有异步依赖
if (suspense.deps > 0) {
// 有异步依赖,进入fallback状态
suspense.isInFallback = true
// 挂载fallback
patch(
null,
vnode.ssFallback!,
container,
anchor,
parentComponent,
null, // fallback tree不会在Suspense边界内
namespace,
slotScopeIds,
optimized
)
// 设置主内容为隐藏
setActiveBranch(suspense, vnode.ssFallback!)
} else {
// 没有异步依赖,直接显示内容
suspense.resolve()
}
}
// 创建Suspense边界
function createSuspenseBoundary(
vnode: VNode,
parentSuspense: SuspenseBoundary | null,
parentComponent: ComponentInternalInstance | null,
container: RendererElement,
hiddenContainer: RendererElement,
anchor: RendererNode | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
rendererInternals: RendererInternals
): SuspenseBoundary {
// 创建Suspense边界实例
const suspense: SuspenseBoundary = {
vnode,
parent: parentSuspense,
parentComponent,
isSVG: namespace === 'svg',
container,
hiddenContainer,
anchor,
namespace,
slotScopeIds,
optimized,
deps: 0,
pendingBranch: null,
pendingId: 0,
isInFallback: true,
isHydrating: false,
isUnmounted: false,
effects: [],
// 方法
resolve,
fallback,
move,
next,
registerDep,
unmount,
// 组件
p: patch,
um: unmount,
r: remove,
m: move,
o: rendererInternals.o
}
return suspense
}
// 注册异步依赖
function registerDep(
suspense: SuspenseBoundary,
dep: AsyncDep,
optimized: boolean
) {
const loaded = dep.loaded
if (suspense.isUnmounted) {
return
}
// 记录异步依赖
suspense.deps++
// 监听加载完成
dep
.then(
() => {
if (suspense.isUnmounted || !dep.loaded) {
return
}
dep.loaded = true
suspense.deps--
if (suspense.deps === 0) {
// 所有依赖都加载完成
if (!suspense.isInFallback) {
suspense.resolve(false)
} else if (suspense.pendingId === dep.__suspenseId) {
// 当前pending的依赖,立即解析
suspense.resolve()
}
}
},
err => {
if (suspense.isUnmounted || !dep.loaded) {
return
}
dep.loaded = true
dep.error = err
suspense.deps--
// 处理错误
if (suspense.deps === 0) {
suspense.resolve()
}
}
)
.catch(() => {
// 忽略错误
})
}
// 解析Suspense
function resolve(
suspense: SuspenseBoundary,
isSync = false
) {
const {
vnode,
pendingBranch: branch,
parentComponent,
container,
hiddenContainer
} = suspense
if (suspense.isResolving || suspense.isUnmounted) {
return
}
suspense.isResolving = true
// 移除fallback
if (suspense.isInFallback) {
// 卸载fallback
unmount(suspense.activeBranch!, parentComponent, suspense, true)
if (!suspense.isHydrating) {
// 移动隐藏的内容到容器
move(
branch,
container,
suspense.anchor,
MoveType.ENTER,
suspense.namespace
)
}
}
// 设置活动分支
setActiveBranch(suspense, branch!)
// 更新vnode el
vnode.el = branch!.el
// 检查是否已经挂载
if (!branch!.mounted) {
branch!.mounted = true
}
// 清空状态
suspense.pendingBranch = null
suspense.isInFallback = false
suspense.isResolving = false
// 执行effects
flushPostFlushCbs()
// 触发resolve事件
if (parentComponent && parentComponent.vnode.shapeFlag & ShapeFlags.SUSPENSE) {
parentComponent.emit('resolve')
}
}
6.2 Teleport实现
// Teleport组件实现
const TeleportImpl = {
__isTeleport: true,
process(
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace: ElementNamespace,
slotScopeIds: string[] | null,
optimized: boolean,
internals: RendererInternals
) {
const {
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
o: { insert, querySelector, createText, createComment }
} = internals
const disabled = isTeleportDisabled(n2.props)
const { shapeFlag, children } = n2
// 移动到目标容器
const target = (n2.target = resolveTarget(n2.props, querySelector))
const targetAnchor = (n2.targetAnchor = createText(''))
if (target) {
// 插入目标锚点
insert(targetAnchor, target)
}
if (disabled) {
// 禁用状态:作为普通Fragment处理
if (!n1) {
// 挂载
mountChildren(
children as VNodeArrayChildren,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
} else {
// 更新
patchChildren(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
}
} else {
// Teleport启用状态
if (!n1) {
// 挂载
// 在隐藏容器中挂载内容
const placeholder = (n2.placeholder = createComment('teleport'))
insert(placeholder, container, anchor)
const mainAnchor = (n2.anchor = createComment('teleport'))
insert(mainAnchor, container, anchor)
const mount = (container: RendererElement, anchor: RendererNode) => {
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
children as VNodeArrayChildren,
container,
anchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
}
}
if (target) {
// 挂载到目标
mount(target, targetAnchor)
}
// 更新占位符
updateCssVars(n2)
} else {
// 更新
;(n2.anchor = n1.anchor)!
;(n2.targetAnchor = n1.targetAnchor)!
// 更新内容
if (n2.children !== n1.children) {
const target = n2.target!
const targetAnchor = n2.targetAnchor!
// 更新子节点
patchChildren(
n1,
n2,
target,
targetAnchor,
parentComponent,
parentSuspense,
namespace,
slotScopeIds,
optimized
)
}
// 更新CSS变量
updateCssVars(n2)
}
}
},
remove(
vnode: VNode,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
optimized: boolean,
{ um: unmount, o: { remove: hostRemove } }: RendererInternals,
doRemove: boolean
) {
const { shapeFlag, children, anchor, targetAnchor, target, disabled } = vnode
if (target) {
hostRemove(targetAnchor!)
}
// 卸载子节点
if (doRemove || !isTeleportDisabled(vnode.props)) {
hostRemove(anchor!)
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNodeArrayChildren).length; i++) {
const child = (children as VNodeArrayChildren)[i]
unmount(
child,
parentComponent,
parentSuspense,
true,
optimized
)
}
}
}
},
move: moveTeleport,
hydrate: hydrateTeleport
}
// 移动Teleport
function moveTeleport(
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
moveType: MoveType,
parentSuspense: SuspenseBoundary | null = null
) {
const { shapeFlag, children, disabled } = vnode
if (disabled) {
// 禁用状态:作为普通Fragment移动
move(vnode, container, anchor, moveType, parentSuspense)
} else {
// 移动目标锚点
const target = vnode.target!
if (moveType === MoveType.ENTER) {
insert(vnode.targetAnchor!, target)
} else {
insert(vnode.anchor!, container, anchor)
}
// 移动子节点
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNodeArrayChildren).length; i++) {
move(
(children as VNodeArrayChildren)[i],
target,
vnode.targetAnchor!,
moveType,
parentSuspense
)
}
}
}
}
// 解析Teleport目标
function resolveTarget(props, querySelector): RendererElement | null {
const targetSelector = props && props.to
if (isString(targetSelector)) {
if (!querySelector) {
__DEV__ &&
warn(
`当前渲染器不支持querySelector。` +
`Teleport的"to"属性必须是显式的DOM元素。`
)
return null
}
const target = querySelector(targetSelector)
if (!target) {
__DEV__ &&
warn(
`找不到Teleport的目标元素:${targetSelector}`
)
return null
}
return target
} else {
if (__DEV__ && !targetSelector) {
warn(`无效的Teleport的"to"属性:${targetSelector}`)
}
return targetSelector
}
}
第七章:性能优化与最佳实践
7.1 编译时性能优化
静态节点提升优化
// 静态节点分析器
function analyzeStaticNodes(root: RootNode): {
hoisted: HoistedNode[]
cached: number[]
} {
const hoisted: HoistedNode[] = []
const cached: number[] = []
walk(root, {
enter(node, parent) {
// 标记静态节点
if (isStaticNode(node)) {
node.staticCount = (node.staticCount || 0) + 1
// 如果是频繁出现的静态节点,考虑缓存
if (node.staticCount > 3) {
const cachedIndex = cached.indexOf(node.staticId)
if (cachedIndex === -1) {
cached.push(node.staticId)
node.cached = true
}
}
}
// 检查是否可以提升
if (canHoistNode(node)) {
const hoistedNode = createHoistedNode(node)
hoisted.push(hoistedNode)
node.hoisted = true
}
}
})
return { hoisted, cached }
}
// 缓存策略
function applyCachingStrategy(
node: TemplateChildNode,
context: TransformContext
): string | null {
if (node.cached) {
// 生成缓存key
const cacheKey = `_cache[${node.staticId}]`
// 检查缓存是否已存在
if (!context.cache[cacheKey]) {
// 创建缓存
context.cache[cacheKey] = createCachedNode(node)
return cacheKey
}
return cacheKey
}
return null
}
// 事件处理优化
function transformEventHandlers(
node: ElementNode,
context: TransformContext
) {
const events: Record<string, string> = {}
// 收集事件处理函数
for (const prop of node.props) {
if (prop.type === NodeTypes.DIRECTIVE && prop.name === 'on') {
const eventName = prop.arg?.content
const handler = prop.exp
if (eventName && handler) {
events[eventName] = handler.content
// 内联简单的事件处理函数
if (isInlineableEventHandler(handler)) {
prop.exp.content = inlineEventHandler(handler)
}
}
}
}
// 应用事件委托优化
if (Object.keys(events).length > 3) {
// 多个事件,考虑使用事件委托
applyEventDelegation(node, events, context)
}
}
// 属性合并优化
function transformAttributes(
node: ElementNode,
context: TransformContext
) {
const staticAttrs: AttributeNode[] = []
const dynamicAttrs: DirectiveNode[] = []
// 分离静态和动态属性
for (const prop of node.props) {
if (prop.type === NodeTypes.ATTRIBUTE) {
staticAttrs.push(prop)
} else if (prop.type === NodeTypes.DIRECTIVE) {
dynamicAttrs.push(prop)
}
}
// 合并静态属性
if (staticAttrs.length > 0) {
const mergedAttrs = mergeStaticAttributes(staticAttrs)
node.props = [mergedAttrs, ...dynamicAttrs]
}
// 优化动态属性
for (const prop of dynamicAttrs) {
if (prop.name === 'bind' && prop.arg?.isStatic) {
// 内联简单的动态属性
if (isInlineableBinding(prop.exp)) {
prop.exp.content = inlineBindingExpression(prop.exp)
}
}
}
}
7.2 运行时性能优化
内存优化与垃圾回收
// 对象池管理
class VNodePool {
private pool: VNode[] = []
private size = 0
private maxSize = 1000
acquire(tag: string, props: any, children: any): VNode {
if (this.size > 0) {
this.size--
const vnode = this.pool.pop()!
// 复用vnode
vnode.tag = tag
vnode.props = props
vnode.children = children
vnode.el = null
vnode.component = null
vnode.shapeFlag = getShapeFlag(tag, children)
return vnode
}
// 创建新的vnode
return createVNode(tag, props, children)
}
release(vnode: VNode) {
if (this.size < this.maxSize) {
// 清理引用
vnode.el = null
vnode.component = null
vnode.ctx = null
vnode.suspense = null
vnode.ssContent = null
vnode.ssFallback = null
vnode.transition = null
this.pool.push(vnode)
this.size++
}
}
clear() {
this.pool.length = 0
this.size = 0
}
}
// 响应式对象缓存
class ReactiveCache {
private cache = new WeakMap<object, any>()
private proxyCache = new WeakMap<object, any>()
getReactive(target: object): any {
let reactive = this.cache.get(target)
if (!reactive) {
reactive = reactive(target)
this.cache.set(target, reactive)
}
return reactive
}
getProxy(target: object, handler: ProxyHandler<any>): any {
let proxy = this.proxyCache.get(target)
if (!proxy) {
proxy = new Proxy(target, handler)
this.proxyCache.set(target, proxy)
}
return proxy
}
clear() {
// 清空缓存
this.cache = new WeakMap()
this.proxyCache = new WeakMap()
}
}
// 批量更新优化
class BatchUpdater {
private updates: (() => void)[] = []
private scheduled = false
schedule(update: () => void) {
this.updates.push(update)
if (!this.scheduled) {
this.scheduled = true
// 在微任务中批量执行
Promise.resolve().then(() => {
this.flush()
})
}
}
flush() {
const updates = this.updates.slice()
this.updates.length = 0
this.scheduled = false
// 批量执行更新
for (const update of updates) {
try {
update()
} catch (error) {
console.error('批量更新错误:', error)
}
}
}
cancel() {
this.updates.length = 0
this.scheduled = false
}
}
虚拟DOM复用策略
// VNode复用策略
interface VNodeReuseStrategy {
canReuse(n1: VNode, n2: VNode): boolean
reuse(n1: VNode, n2: VNode): void
}
// 默认复用策略
const defaultReuseStrategy: VNodeReuseStrategy = {
canReuse(n1: VNode, n2: VNode): boolean {
// 检查类型是否相同
if (n1.type !== n2.type) {
return false
}
// 检查key是否相同
if (n1.key !== n2.key) {
return false
}
// 检查props是否兼容
if (!arePropsCompatible(n1.props, n2.props)) {
return false
}
// 检查子节点是否兼容
if (!areChildrenCompatible(n1.children, n2.children)) {
return false
}
return true
},
reuse(n1: VNode, n2: VNode): void {
// 复用el引用
n2.el = n1.el
// 复用组件实例
if (n1.component) {
n2.component = n1.component
}
// 复用transition状态
if (n1.transition) {
n2.transition = n1.transition
}
// 标记为复用
n2.reused = true
}
}
// 应用复用策略
function tryReuseVNode(
n1: VNode | null,
n2: VNode,
strategy: VNodeReuseStrategy = defaultReuseStrategy
): boolean {
if (n1 && strategy.canReuse(n1, n2)) {
strategy.reuse(n1, n2)
return true
}
return false
}
// 组件级别的复用
function reuseComponent(
n1: VNode,
n2: VNode,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null
): boolean {
const instance = n1.component!
// 检查props是否变化
const { props: nextProps } = n2
const { props: prevProps } = n1
if (hasPropsChanged(prevProps, nextProps)) {
// props变化,需要更新
instance.next = n2
instance.update()
return false
}
// 复用组件实例
n2.component = instance
n2.el = n1.el
// 更新vnode引用
instance.vnode = n2
// 标记组件为复用
instance.isReused = true
return true
}
第八章:TypeScript类型系统与类型安全
8.1 响应式类型系统
// 基础类型定义
type Primitive = string | number | boolean | bigint | symbol | undefined | null
type Builtin = Primitive | Function | Date | Error | RegExp
// 响应式类型映射
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
type UnwrapRef<T> = T extends Ref<infer V>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>
type UnwrapRefSimple<T> = T extends
| Function
| CollectionTypes
| BaseTypes
| Ref
| RefUnion
? T
: T extends Array<any>
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
: T extends object
? {
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
}
: T
// Ref类型
interface Ref<T = any> {
value: T
/**
* Type differentiator only.
* We need this to be in public d.ts but don't want it to show up in IDE
* autocomplete, so we use a private Symbol instead.
*/
[RefSymbol]: true
/**
* @internal
*/
_shallow?: boolean
}
// Reactive类型
type Reactive<T> = UnwrapNestedRefs<T>
// Computed类型
interface ComputedRef<T = any> extends WritableComputedRef<T> {
readonly value: T
[ComputedRefSymbol]: true
}
interface WritableComputedRef<T> extends Ref<T> {
readonly effect: ReactiveEffect<T>
}
// 工具类型
type MaybeRef<T> = T | Ref<T>
type MaybeRefOrGetter<T> = MaybeRef<T> | (() => T)
// 响应式函数类型
function reactive<T extends object>(target: T): Reactive<T>
function ref<T>(value: T): Ref<UnwrapRef<T>>
function ref<T = any>(): Ref<T | undefined>
function computed<T>(
getter: () => T,
debugOptions?: DebuggerOptions
): ComputedRef<T>
function computed<T>(
options: {
get: () => T
set: (value: T) => void
},
debugOptions?: DebuggerOptions
): WritableComputedRef<T>
// 组件Props类型推导
type ExtractPropTypes<O> = O extends object
? { [K in RequiredKeys<O>]: InferPropType<O[K]> } &
{ [K in OptionalKeys<O>]?: InferPropType<O[K]> }
: { [key: string]: any }
type RequiredKeys<T> = {
[K in keyof T]: T[K] extends
| { required: true }
| { default: any }
| BooleanConstructor
| { type: BooleanConstructor }
? T[K] extends { default: undefined | (() => undefined) }
? never
: K
: never
}[keyof T]
type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
type InferPropType<T> = T extends null
? any // null & true would fail to infer
: T extends { type: null | true }
? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829
: T extends ObjectConstructor | { type: ObjectConstructor }
? Record<string, any>
: T extends BooleanConstructor | { type: BooleanConstructor }
? boolean
: T extends DateConstructor | { type: DateConstructor }
? Date
: T extends Prop<infer V>
? V
: T extends ArrayConstructor | { type: ArrayConstructor }
? any[]
: T extends PropOptions<infer V>
? V
: T extends Constructor
? InstanceType<T>
: T
8.2 组件类型推导
// 组件选项类型
interface ComponentOptions<
Props = {},
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
S extends string = string,
EE extends string = string
> {
// 状态
data?: (this: CreateComponentPublicInstance<Props, RawBindings>) => D
computed?: C
methods?: M
// 生命周期
beforeCreate?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
created?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
beforeMount?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
mounted?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
beforeUpdate?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
updated?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
beforeUnmount?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
unmounted?: (this: CreateComponentPublicInstance<Props, RawBindings>) => void
errorCaptured?: ErrorCapturedHook
// 渲染
render?: (ctx: CreateComponentPublicInstance<Props, RawBindings>) => VNodeChild
template?: string
// Props
props?: (PropsOptions<Props> | Array<keyof Props>)
// Emits
emits?: (E | Array<EE>) | EmitsOptions
// 暴露
expose?: string[]
// 继承
mixins?: Mixin[]
extends?: Extends
// 自定义
[key: string]: any
}
// Setup上下文类型
interface SetupContext<
E extends EmitsOptions = {},
S extends SlotsType = {},
EE extends string = string
> {
attrs: Data
slots: UnwrapSlotsType<S>
emit: EmitFn<E, EE>
expose: (exposed?: Record<string, any>) => void
}
// 组合式函数类型
type UseFetchReturn<T> = {
data: Ref<T | null>
error: Ref<Error | null>
loading: Ref<boolean>
execute: () => Promise<void>
}
function useFetch<T>(url: MaybeRef<string>): UseFetchReturn<T>
function useFetch<T>(url: MaybeRef<string>, options: UseFetchOptions): UseFetchReturn<T>
// 自定义指令类型
type DirectiveHook<T = any, Prev = VNode<any, T> | null, V = any> = (
el: T,
binding: DirectiveBinding<V>,
vnode: VNode<any, T>,
prevVNode: Prev
) => void
interface DirectiveBinding<V = any> {
instance: ComponentPublicInstance | null
value: V
oldValue: V | null
arg?: string
modifiers: DirectiveModifiers
dir: ObjectDirective<any, V>
}
type DirectiveArguments<
Name extends string,
Modifiers extends string = string
> = [
name: Name,
value?: any,
modifiers?: Partial<Record<Modifiers, boolean>>
]
总结与展望
Vue 3 是一个工程学上的杰作,它在保持易用性的同时,通过精妙的设计实现了性能和开发体验的显著提升。通过本文的深度解析,我们可以总结出 Vue 3 的核心优势:
核心优势总结
-
性能飞跃:基于 Proxy 的响应式系统、编译时优化、虚拟 DOM Diff 算法优化等,使得性能相比 Vue 2 有 2-3 倍的提升。
-
组合式 API:更灵活的逻辑复用、更好的 TypeScript 支持、更清晰的逻辑组织。
-
更好的 TypeScript 支持:完整的类型推导、更严格的类型检查、更好的 IDE 支持。
-
更小的体积:Tree-shaking 支持、模块化设计,使得生产包体积更小。
-
更好的可维护性:清晰的模块边界、更好的代码组织、更完善的错误处理。
学习建议
-
深入理解响应式原理:这是 Vue 3 的核心,理解 Proxy、依赖收集、派发更新的机制。
-
掌握编译时优化:了解静态提升、Patch Flags、树结构压平等优化技术。
-
实践 Composition API:通过实际项目练习,掌握逻辑复用的最佳实践。
-
阅读源码:选择感兴趣的部分深入阅读,理解实现细节。
-
关注生态发展:Vue 3 的生态还在快速发展,关注新的工具和库。
未来展望
Vue 3 不仅是一个框架的升级,更是前端开发理念的进化。随着 Vue 3 生态的不断完善,我们有理由相信:
-
更多编译时优化:静态分析、代码生成优化等将持续改进。
-
更好的开发体验:Vite 等工具将进一步提升开发效率。
-
更丰富的生态:更多的组件库、工具链将支持 Vue 3。
-
更广泛的应用:从 Web 到桌面、移动端,Vue 3 的应用场景将更加广泛。
Vue 3 的成功在于它平衡了易用性、性能和可维护性,这为前端开发提供了一个优秀的范例。无论你是 Vue 新手还是资深开发者,深入理解 Vue 3 都将对你的技术成长大有裨益。