feat:更新文档架构
This commit is contained in:
367
docs/Web/Vue/Vue3-Architecture.md
Normal file
367
docs/Web/Vue/Vue3-Architecture.md
Normal file
@@ -0,0 +1,367 @@
|
||||
---
|
||||
title: Vue3架构原理
|
||||
date: 2025-12-15 23:25
|
||||
---
|
||||
|
||||
# 一、Vue3 整体架构图
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Vue 3] --> B[Compiler]
|
||||
A --> C[Runtime]
|
||||
A --> D[Reactivity]
|
||||
|
||||
B --> B1[Parser]
|
||||
B --> B2[Transformer]
|
||||
B --> B3[Codegen]
|
||||
|
||||
C --> C1[Virtual DOM]
|
||||
C --> C2[Renderer]
|
||||
C --> C3[Component]
|
||||
|
||||
D --> D1[Proxy]
|
||||
D --> D2[Effect]
|
||||
D --> D3[Dependency Graph]
|
||||
|
||||
B3 -->|生成| C1
|
||||
C1 -->|Diff/Patch| C2
|
||||
D3 -->|驱动更新| C1
|
||||
```
|
||||
|
||||
## 1.1 编译器模块
|
||||
  编译器负责将模板转换为可执行的渲染函数,包含**解析器(Parser)、转换器(Transformer)和代码生成器(Codegen)** 三个子模块。
|
||||
|
||||
- compiler-core: 核心编译器,负责基础的编译功能,如生成抽象语法树(AST)、基础的模板编译等,是平台无关的编译器核心。
|
||||
- compiler-dom: 基于compiler-core,针对浏览器环境的编译模块,重写了编译和解析方法,处理与原生 DOM API 相关的编译工作。
|
||||
- compiler-sfc: 用于处理 Vue 单文件组件(.vue)的编译,将单文件组件中的模板、脚本和样式进行解析和编译。
|
||||
- compiler-ssr:服务端渲染的编译模块,负责在服务端将 Vue 组件编译为适合服务器端渲染的代码。
|
||||
|
||||
## 1.2 运行时模块
|
||||
  运行时系统处理**虚拟DOM的创建、更新和渲染**,包含组件实例管理、虚拟DOM diff算法和平台渲染器。
|
||||
|
||||
- runtime-core:运行时核心模块,包含了 Vue 组件的生命周期、虚拟节点(vnode)、观察者(watch)等核心 API,是平台无关的运行时核心。
|
||||
- runtime-dom:运行时与 DOM 相关的核心模块,提供了与浏览器 DOM 交互的功能,如创建应用实例、操作 DOM 元素等。
|
||||
|
||||
## 1.3 响应式系统
|
||||
  响应式系统基于Proxy实现数据劫持,通过依赖收集和触发更新机制驱动视图变化。
|
||||
  三大系统协同工作:编译器生成的渲染函数由运行时执行,响应式系统追踪数据变化触发组件更新,形成完整的渲染闭环。
|
||||
|
||||
# 二、基础模块
|
||||
## 2.1 响应式系统
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(原始对象):::process -->|Proxy 包装| B(响应式对象):::process
|
||||
B -->|属性读取| C(依赖收集):::process
|
||||
B -->|属性修改| D(触发更新):::process
|
||||
C --> E(副作用函数):::process
|
||||
D --> E
|
||||
E -->|更新视图| F(真实 DOM):::process
|
||||
```
|
||||
|
||||
  Vue3的响应式系统通过Proxy代理对象属性,在get操作时通过track函数收集当前正在执行的副作用(如组件渲染函数)作为依赖,存储到依赖关系图中。当set操作发生时,trigger函数根据依赖关系图找到所有关联的副作用函数,通过调度器批量执行。
|
||||
  这种设计使得数据变化能精准触发组件更新,同时通过异步批处理避免重复渲染。
|
||||
  响应式系统独立于其他模块,可单独使用,为Vue3提供了高效的状态管理能力。
|
||||
|
||||
## 2.2 虚拟 DOM
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(旧虚拟 DOM 树):::process -->|Diff 算法| B(差异比较):::process
|
||||
C(新虚拟 DOM 树):::process --> B
|
||||
B -->|更新操作| D(真实 DOM):::process
|
||||
```
|
||||
|
||||
  当组件更新时,Vue会比较新旧虚拟DOM树的差异。通过Patch Flags快速识别动态节点类型(如文本、样式等),仅对标记为动态的部分进行比较;对于带key的子节点列表,使用最长递增子序列算法最小化DOM操作;完全静态的子树则直接跳过diff过程。
|
||||
  这种靶向更新策略将传统虚拟DOM算法O(n³)的时间复杂度优化到接近O(n),同时配合异步批量更新机制,确保复杂应用也能保持流畅渲染
|
||||
|
||||
## 2.3 编译器
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(模板字符串):::process -->|解析| B(抽象语法树 - AST):::process
|
||||
B -->|转换| C(优化后的 AST):::process
|
||||
C -->|生成| D(渲染函数代码):::process
|
||||
```
|
||||
|
||||
  Vue编译器将模板字符串转换为渲染函数,经历解析、转换和代码生成三个阶段。
|
||||
  解析阶段通过词法分析生成原始AST抽象语法树;转换阶段对AST进行优化,包括静态节点提升(将不变节点提取到渲染函数外部)、补丁标记(给动态节点添加优化标识)、树结构打平(减少动态子节点的嵌套层级);最后代码生成阶段输出优化的渲染函数。
|
||||
  这些优化使得运行时能跳过静态内容比较,直接定位动态节点,大幅提升渲染性能。
|
||||
|
||||
## 2.4 渲染器
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(虚拟 DOM 树):::process -->|创建节点| B(真实 DOM 节点):::process
|
||||
B -->|插入节点| C(页面):::process
|
||||
A -->|更新节点| B
|
||||
```
|
||||
|
||||
  Vue渲染器是连接虚拟DOM与真实平台的桥梁,分为虚拟DOM处理层、Patch算法层和平台操作层。
|
||||
  它通过抽象平台API(如createElement、insert等)实现跨平台能力,开发者可配置不同平台的底层操作来支持Web、Canvas甚至原生应用。
|
||||
  渲染器内部采用增量更新策略,结合编译器提供的优化提示(如静态标记、动态子节点索引),确保DOM操作最精简。
|
||||
  这种设计使Vue在保证Web端高性能的同时,也能灵活适配各种渲染环境。
|
||||
|
||||
# 三、Vue3应用生命周期
|
||||
## 3.1 初始化阶段
|
||||
  流程:
|
||||
1. 调用`createApp()`创建应用实例
|
||||
2. 创建平台专属渲染器(DOM/SSR/Test)
|
||||
3. 初始化应用上下文和插件系统
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-dom/index.ts
|
||||
const renderer = createRenderer({
|
||||
patchProp, // DOM属性操作
|
||||
...nodeOps // DOM节点操作集合
|
||||
})
|
||||
|
||||
function createApp(rootComponent) {
|
||||
const context = createAppContext()
|
||||
const app = {
|
||||
_component: rootComponent,
|
||||
mount(container) {
|
||||
const vnode = createVNode(rootComponent)
|
||||
renderer.render(vnode, container)
|
||||
return vnode.component.proxy
|
||||
}
|
||||
}
|
||||
return app
|
||||
}
|
||||
```
|
||||
|
||||
## 3.2 组件挂载阶段
|
||||
  流程:
|
||||
1. 标准化容器(字符串选择器 → DOM元素)
|
||||
2. 创建根组件虚拟节点(vnode)
|
||||
3. 初始化组件:解析props和slots、执行setup函数、建立响应式连接等
|
||||
4. 建立渲染effect,为组件创建响应式的副作用函数
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function mountComponent(vnode, container) {
|
||||
// 1. 创建组件实例
|
||||
const instance = createComponentInstance(vnode)
|
||||
|
||||
// 2. 初始化组件
|
||||
setupComponent(instance)
|
||||
|
||||
// 3. 建立渲染effect
|
||||
setupRenderEffect(instance, container)
|
||||
}
|
||||
|
||||
function setupRenderEffect(instance, container) {
|
||||
instance.update = effect(() => {
|
||||
if (!instance.isMounted) {
|
||||
// 首次渲染
|
||||
const subTree = (instance.subTree = renderComponentRoot(instance))
|
||||
patch(null, subTree, container)
|
||||
instance.isMounted = true
|
||||
} else {
|
||||
// 更新渲染
|
||||
const nextTree = renderComponentRoot(instance)
|
||||
patch(instance.subTree, nextTree, container)
|
||||
instance.subTree = nextTree
|
||||
}
|
||||
}, { scheduler: queueJob })
|
||||
}
|
||||
```
|
||||
|
||||
## 3.3 响应式更新阶段
|
||||
  流程:
|
||||
1. 数据变更触发Proxy的set拦截
|
||||
2. 通过trigger触发依赖的effect
|
||||
3. 调度器将更新任务加入队列
|
||||
4. 下一个tick执行队列中的任务
|
||||
5. 执行组件render函数生成新vnode
|
||||
6. 调用patch进行差异更新
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// reactivity/reactive.ts
|
||||
function createReactiveObject(target) {
|
||||
return new Proxy(target, {
|
||||
set(target, key, value, receiver) {
|
||||
const oldValue = target[key]
|
||||
const result = Reflect.set(target, key, value, receiver)
|
||||
if (hasChanged(value, oldValue)) {
|
||||
trigger(target, key) // 触发更新
|
||||
}
|
||||
return result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// runtime-core/scheduler.ts
|
||||
const queue = []
|
||||
function queueJob(job) {
|
||||
if (!queue.includes(job)) {
|
||||
queue.push(job)
|
||||
queueFlush()
|
||||
}
|
||||
}
|
||||
|
||||
function queueFlush() {
|
||||
if (!isFlushing) {
|
||||
nextTick(flushJobs)
|
||||
}
|
||||
}
|
||||
|
||||
function flushJobs() {
|
||||
isFlushing = true
|
||||
queue.sort((a, b) => a.id - b.id) // 保证父组件先更新
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
queue[i]()
|
||||
}
|
||||
queue.length = 0
|
||||
isFlushing = false
|
||||
}
|
||||
```
|
||||
|
||||
## 3.4 虚拟DOM Patch阶段
|
||||
  流程:
|
||||
1. 比较新旧vnode的类型
|
||||
2. 不同类型:卸载旧节点,挂载新节点
|
||||
3. 相同类型:更新节点
|
||||
4. 子节点对比采用高效diff算法
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function patch(n1, n2, container) {
|
||||
// 1. 类型不同直接卸载
|
||||
if (n1 && !isSameVNodeType(n1, n2)) {
|
||||
unmount(n1)
|
||||
n1 = null
|
||||
}
|
||||
|
||||
const { type, patchFlag } = n2
|
||||
|
||||
// 2. 根据类型处理
|
||||
switch (type) {
|
||||
case Text:
|
||||
processText(n1, n2, container)
|
||||
break
|
||||
case Fragment:
|
||||
processFragment(n1, n2, container)
|
||||
break
|
||||
default:
|
||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||
processElement(n1, n2, container)
|
||||
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||
processComponent(n1, n2, container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processElement(n1, n2, container) {
|
||||
if (n1 == null) {
|
||||
mountElement(n2, container)
|
||||
} else {
|
||||
patchElement(n1, n2)
|
||||
}
|
||||
}
|
||||
|
||||
function patchElement(n1, n2) {
|
||||
// 根据patchFlag进行优化更新
|
||||
if (n2.patchFlag & PatchFlags.FULL_PROPS) {
|
||||
// 全量props更新
|
||||
} else {
|
||||
// 只更新动态props
|
||||
}
|
||||
|
||||
// 更新子节点
|
||||
patchChildren(n1, n2)
|
||||
}
|
||||
```
|
||||
|
||||
## 3.5 组件卸载阶段
|
||||
  流程:
|
||||
1. 触发beforeUnmount生命周期钩子
|
||||
2. 停止组件渲染effect
|
||||
3. 递归卸载子组件
|
||||
4. 移除DOM节点
|
||||
5. 触发unmounted生命周期钩子
|
||||
6. 清理响应式依赖
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function unmount(vnode) {
|
||||
if (vnode.type === Fragment) {
|
||||
unmountChildren(vnode.children)
|
||||
return
|
||||
}
|
||||
|
||||
const { component } = vnode
|
||||
if (component) {
|
||||
unmountComponent(component)
|
||||
} else {
|
||||
remove(vnode.el!)
|
||||
}
|
||||
}
|
||||
|
||||
function unmountComponent(instance) {
|
||||
// 1. 触发beforeUnmount
|
||||
if (instance.bum) {
|
||||
invokeArrayFns(instance.bum)
|
||||
}
|
||||
|
||||
// 2. 停止响应式effect
|
||||
stop(instance.update)
|
||||
|
||||
// 3. 递归卸载子树
|
||||
unmount(instance.subTree)
|
||||
|
||||
// 4. 触发unmounted
|
||||
if (instance.um) {
|
||||
queuePostRenderEffect(instance.um)
|
||||
}
|
||||
|
||||
// 5. 清理引用
|
||||
instance.isUnmounted = true
|
||||
}
|
||||
```
|
||||
|
||||
## 3.6 编译阶段
|
||||
  流程:
|
||||
1. 将模板解析为AST
|
||||
2. 转换AST(静态提升/标记PatchFlag)
|
||||
3. 生成可执行的渲染函数代码
|
||||
4. 运行时直接使用优化后的渲染函数
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// compiler-core/src/compile.ts
|
||||
function baseCompile(template, options) {
|
||||
// 1. 解析阶段
|
||||
const ast = parse(template)
|
||||
|
||||
// 2. 转换阶段
|
||||
transform(ast, {
|
||||
hoistStatic: true, // 静态提升
|
||||
nodeTransforms: [
|
||||
transformIf,
|
||||
transformFor,
|
||||
transformExpression
|
||||
]
|
||||
})
|
||||
|
||||
// 3. 代码生成
|
||||
const code = generate(ast, {
|
||||
mode: 'function',
|
||||
runtimeGlobalName: 'Vue',
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
|
||||
return {
|
||||
ast,
|
||||
code: `with(this){return ${code}}` // 生成渲染函数
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user