Vue3 源码解析
响应式系统
Proxy 实现
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key)
return result
}
})
}
依赖收集
let activeEffect = null
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
effect 函数
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
return effectFn
}
编译器
模板编译流程
模板字符串
↓
parse (解析)
↓
AST (抽象语法树)
↓
transform (转换)
↓
优化后的 AST
↓
generate (生成)
↓
render 函数
简化的 parse
function parse(template) {
const ast = {
type: 'Root',
children: []
}
// 解析模板生成 AST
// ...
return ast
}
简化的 transform
function transform(ast) {
// 静态提升
// PatchFlag 标记
// ...
}
简化的 generate
function generate(ast) {
return `
function render() {
return h('div', null, 'Hello')
}
`
}
虚拟 DOM
VNode 结构
const vnode = {
type: 'div',
props: {
id: 'app',
class: 'container'
},
children: [
{
type: 'p',
children: 'Hello'
}
]
}
h 函数
function h(type, props, children) {
return {
type,
props,
children
}
}
Diff 算法
function patch(n1, n2, container) {
if (n1 && n1.type !== n2.type) {
unmount(n1)
n1 = null
}
const { type } = n2
if (typeof type === 'string') {
if (!n1) {
mountElement(n2, container)
} else {
patchElement(n1, n2)
}
}
}
快速 Diff
Vue3 使用最长递增子序列算法优化 Diff:
function patchKeyedChildren(c1, c2, container) {
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]
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container)
} else {
break
}
i++
}
// 2. 从尾部开始比较
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container)
} else {
break
}
e1--
e2--
}
// 3. 处理剩余节点
// ...
}
组件化
组件实例
function createComponentInstance(vnode) {
const instance = {
vnode,
type: vnode.type,
props: {},
slots: {},
setupState: {},
ctx: {},
isMounted: false
}
return instance
}
组件挂载
function mountComponent(vnode, container) {
const instance = createComponentInstance(vnode)
setupComponent(instance)
setupRenderEffect(instance, container)
}