feat:增加前端工程博客
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { defineConfig } from 'vitepress'
|
import { defineConfig } from 'vitepress'
|
||||||
|
import { withMermaid } from 'vitepress-plugin-mermaid'
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default withMermaid({
|
||||||
title: "拾光记",
|
title: "拾光记",
|
||||||
description: "Cxx0822 Blog",
|
description: "Cxx0822 Blog",
|
||||||
head: [
|
head: [
|
||||||
@@ -26,12 +27,16 @@ export default defineConfig({
|
|||||||
text: 'Vue',
|
text: 'Vue',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Vue3-Common', link: '/Web-Front/Vue/Vue3-Common' },
|
{ text: 'Vue3-Common', link: '/Web-Front/Vue/Vue3-Common' },
|
||||||
|
{ text: 'Vue3架构原理', link: '/Web-Front/Vue/Vue3-Architecture' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '其他',
|
text: '其他',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'JavaScript知识点整理', link: '/Web-Front/Others/JavaScript-Guide' }
|
{ text: 'JavaScript知识点整理', link: '/Web-Front/Others/JavaScript-Guide' },
|
||||||
|
{ text: '前端包管理器', link: '/Web-Front/Others/Package' },
|
||||||
|
{ text: 'Vite核心原理', link: '/Web-Front/Others/Vite-Principle' },
|
||||||
|
{ text: '浏览器渲染全流程解析', link: '/Web-Front/Others/Browser-Process' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -91,13 +96,17 @@ export default defineConfig({
|
|||||||
{
|
{
|
||||||
text: 'Vue',
|
text: 'Vue',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Vue3-Common', link: '/Web-Front/Vue/Vue3-Common' }
|
{ text: 'Vue3-Common', link: '/Web-Front/Vue/Vue3-Common' },
|
||||||
|
{ text: 'Vue3架构原理', link: '/Web-Front/Vue/Vue3-Architecture' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Others',
|
text: 'Others',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'JavaScript知识点整理', link: '/Web-Front/Others/JavaScript-Guide' }
|
{ text: 'JavaScript知识点整理', link: '/Web-Front/Others/JavaScript-Guide' },
|
||||||
|
{ text: '前端包管理器', link: '/Web-Front/Others/Package' },
|
||||||
|
{ text: 'Vite核心原理', link: '/Web-Front/Others/Vite-Principle' },
|
||||||
|
{ text: '浏览器渲染全流程解析', link: '/Web-Front/Others/Browser-Process' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ Python FastAPI 框架学习:
|
|||||||
- [Flyway简单使用](/Web-Backend/Others/Flyway)
|
- [Flyway简单使用](/Web-Backend/Others/Flyway)
|
||||||
- [MyBatis简介和使用](/Web-Backend/Others/MyBatis)
|
- [MyBatis简介和使用](/Web-Backend/Others/MyBatis)
|
||||||
- [RustFS简介和使用](/Web-Backend/Others/RustFS)
|
- [RustFS简介和使用](/Web-Backend/Others/RustFS)
|
||||||
- [JsonView和MapStruct使用](/Web-Backend/SpringBoot/JsonView-MapStruct)
|
- [JsonView和MapStruct使用](/Web-Backend/Others/JsonView-MapStruct)
|
||||||
95
docs/Web-Front/Others/Browser-Process.md
Normal file
95
docs/Web-Front/Others/Browser-Process.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# 一、网络请求阶段
|
||||||
|
1. DNS 解析
|
||||||
|
|
||||||
|
  浏览器先解析输入的网址对应的 IP 地址,先检查本地 DNS 缓存,若没有则向本地 DNS 服务器发起查询。最终通过 DNS 系统获取服务器的 IP 地址。
|
||||||
|
|
||||||
|
2. 建立网络连接(TCP/IP 握手)
|
||||||
|
|
||||||
|
  通过三次握手建立客户端(浏览器)与服务器的 TCP 连接。若网址使用 HTTPS,还需完成 TLS/SSL 握手,确保数据加密传输。
|
||||||
|
|
||||||
|
3. 发起 HTTP/HTTPS 请求
|
||||||
|
|
||||||
|
  浏览器向服务器发送请求报文,包含:
|
||||||
|
- **请求行**:方法(GET)、URL、协议版本(如 HTTP/1.1)。
|
||||||
|
- **请求头**:包含浏览器信息(User-Agent)、缓存策略(Cache-Control)等。
|
||||||
|
- **若为 POST 请求**:还包含请求体数据。
|
||||||
|
|
||||||
|
4. 服务器处理请求
|
||||||
|
|
||||||
|
  服务器接收到请求后,根据 URL 路由到对应的后端服务(如 Node.js、Java 等)。
|
||||||
|
- **若为 Vue3 项目**:
|
||||||
|
* 若为单页应用(SPA),服务器通常返回一个通用的 `index.html` 页面(前端路由由 Vue Router 处理)。
|
||||||
|
* 若涉及 API 请求(如获取数据),服务器返回 JSON 格式的数据(如通过 Express、Spring Boot 等接口)。
|
||||||
|
|
||||||
|
5. 返回响应结果
|
||||||
|
|
||||||
|
  服务器返回响应报文,包含:
|
||||||
|
- **状态行**:状态码(如 200 OK、404 Not Found)。
|
||||||
|
- **响应头**:包含内容类型(Content-Type: text/html)、缓存控制(Cache-Control: max-age=3600)等。
|
||||||
|
- **响应体**:
|
||||||
|
* 若为 `index.html`:返回 HTML 文本(包含 Vue3 项目的入口脚本引用,如 `<script src="/static/js/app.js"></script>`)。
|
||||||
|
* 若为 API 数据:返回 JSON 格式的数据(如用户信息、列表数据等)。
|
||||||
|
|
||||||
|
|
||||||
|
# 二、解析与执行阶段
|
||||||
|
1. 解析 HTML 并构建 DOM 树
|
||||||
|
|
||||||
|
  浏览器逐行解析 HTML 内容,构建 **DOM 树**(Document Object Model)。
|
||||||
|
- 遇到 `<link rel="stylesheet">` 标签时,异步加载 CSS 文件并构建 **CSSOM 树**(CSS Object Model)。
|
||||||
|
- 遇到 `<script>` 标签时:
|
||||||
|
* 若为 **同步脚本**(`<script src="app.js">`):暂停 HTML 解析,下载并执行脚本。
|
||||||
|
* 若为 **异步脚本**(`<script async src="app.js">`):异步下载脚本,不阻塞 HTML 解析。
|
||||||
|
|
||||||
|
2. 加载 Vue3 相关资源
|
||||||
|
|
||||||
|
  浏览器下载并执行 Vue3 的核心库(`vue.runtime.global.js`)、项目打包后的 JS 文件(如 `app.js`)和 CSS 文件(如 `app.css`)。
|
||||||
|
::: tip
|
||||||
|
- `app.js`:包含 Vue3 应用的入口逻辑(如 `createApp(App).mount('#app')`)、组件定义、路由配置等。
|
||||||
|
- `app.css`:包含全局样式或组件样式(通过 `<style>` 标签在组件中定义的样式会被编译为内联样式)。
|
||||||
|
:::
|
||||||
|
|
||||||
|
3. 执行 Vue3 应用初始化
|
||||||
|
|
||||||
|
  当浏览器执行到 Vue3 入口脚本时,会触发以下流程:
|
||||||
|
- **创建应用实例**:通过 `createApp(App)` 创建 Vue 应用实例。
|
||||||
|
- **配置插件和路由**:
|
||||||
|
* 安装路由插件 `vue-router`:`app.use(router)`。
|
||||||
|
* 安装状态管理插件(如 `pinia`):`app.use(pinia)`。
|
||||||
|
- **挂载应用到 DOM**:通过 `app.mount('#app')` 将 Vue 组件渲染到 HTML 中的指定容器(如 `<div id="app"></div>`)。
|
||||||
|
|
||||||
|
4. Vue3 组件渲染流程
|
||||||
|
|
||||||
|
  **初始化组件**:从根组件(`App.vue`)开始,递归解析组件树,初始化响应式数据。
|
||||||
|
- **模板编译**:
|
||||||
|
* 若使用单文件组件(`.vue`),模板(`<template>`)会被编译为渲染函数(`render`)。
|
||||||
|
- **响应式依赖收集**:Vue3 通过 `Proxy` 和 `Reflect` 实现响应式,当组件访问数据(如 `message`)时,自动建立依赖关系。
|
||||||
|
- **首次渲染(挂载阶段)**:
|
||||||
|
* 根据渲染函数生成 **虚拟 DOM(VNode)**。
|
||||||
|
* 通过 `patch` 函数将虚拟 DOM 渲染为真实 DOM,并插入到页面中。
|
||||||
|
* 例如,根组件渲染后,`#app` 容器内会生成对应的 HTML 结构。
|
||||||
|
|
||||||
|
5. 处理异步操作(如数据请求)
|
||||||
|
|
||||||
|
  在组件的 `setup` 函数或生命周期钩子(如 `onMounted`)中,通过 `axios` 或 Vue3 内置的 `useFetch` 发起 API 请求:
|
||||||
|
- **数据更新与重新渲染**:当响应数据返回后,更新响应式数据(如 `data.value`),Vue3 检测到变化后,重新生成虚拟 DOM 并与真实 DOM 对比,执行 **差异化更新(Diff 算法)**,只更新变化的部分。
|
||||||
|
|
||||||
|
# 三、页面渲染与交互阶段
|
||||||
|
1. 合成图层并绘制页面
|
||||||
|
|
||||||
|
  浏览器将 DOM 树和 CSSOM 树合并为 **渲染树(Render Tree)**,计算每个元素的布局(Layout)和样式(Style)。通过 **图层合成(Composite)** 将不同层级的元素绘制到页面上,最终显示完整的 UI。
|
||||||
|
|
||||||
|
2. 用户交互与组件更新
|
||||||
|
|
||||||
|
- 当用户触发事件(如点击按钮、输入表单)时:
|
||||||
|
* Vue3 通过 `v-on` 指令绑定事件处理函数(如 `@click="handleClick"`)。
|
||||||
|
* 事件处理函数中修改响应式数据,触发组件重新渲染。
|
||||||
|
- 点击按钮时,`count` 的值更新,Vue3 自动重新渲染按钮文本。
|
||||||
|
|
||||||
|
# 四、总结
|
||||||
|
::: tip
|
||||||
|
输入网址 → DNS解析 → TCP连接 → 服务器返回index.html → 解析HTML加载Vue3脚本 →
|
||||||
|
执行createApp初始化 → 挂载组件到DOM → 首次渲染(虚拟DOM转真实DOM) →
|
||||||
|
异步请求数据 → 数据更新触发重新渲染 → 页面最终呈现 → 用户交互驱动组件更新
|
||||||
|
:::
|
||||||
|
|
||||||
|
  通过以上流程,浏览器从输入网址到完成 Vue3 应用的渲染,充分利用了前端框架的响应式机制和虚拟 DOM 技术,实现高效的页面更新与交互。
|
||||||
@@ -917,7 +917,7 @@ console.log(person1.sayName === person2.sayName)
|
|||||||
  所有的属性和方法都直接添加到`Person`的`prototype`属性上了,使用这种原型模式定义的属性和方法是由所有的实例对象共享的。
|
  所有的属性和方法都直接添加到`Person`的`prototype`属性上了,使用这种原型模式定义的属性和方法是由所有的实例对象共享的。
|
||||||
|
|
||||||
#### 深入理解原型
|
#### 深入理解原型
|
||||||

|

|
||||||
  在`JavaScript`中,只要创建了一个函数,就会按照特定的规则**为这个函数创建一个`prototype`属性**(指向原型对象)。所有的**原型对象自动获得一个名为`constructor`的属性,指回与之关联的构造函数**。
|
  在`JavaScript`中,只要创建了一个函数,就会按照特定的规则**为这个函数创建一个`prototype`属性**(指向原型对象)。所有的**原型对象自动获得一个名为`constructor`的属性,指回与之关联的构造函数**。
|
||||||
  在自定义构造函数时,原型对象默认只会获得`constructor`属性,其他的所有方法都继承自`Object`(本例中的其他方法是在后面自定义的)。每次调用构造函数会创建一个新实例对象,这个实例对象的内部`[[Prototype]]`指针就会被赋值为构造函数的原型对象(大部分浏览器将这个指针定义为实例对象上的`__proto__`属性)。通过这个属性就可以访问对象的原型。
|
  在自定义构造函数时,原型对象默认只会获得`constructor`属性,其他的所有方法都继承自`Object`(本例中的其他方法是在后面自定义的)。每次调用构造函数会创建一个新实例对象,这个实例对象的内部`[[Prototype]]`指针就会被赋值为构造函数的原型对象(大部分浏览器将这个指针定义为实例对象上的`__proto__`属性)。通过这个属性就可以访问对象的原型。
|
||||||
  因此,实例对象通过`__proto__`可以链接到原型对象,它实际上指向隐藏特性`[[Prototype]]`,构造函数通过`Prototype`属性链接到原型对象。**实例对象与构造函数原型之间有直接的联系,但实例对象与构造函数之间没有关系。**
|
  因此,实例对象通过`__proto__`可以链接到原型对象,它实际上指向隐藏特性`[[Prototype]]`,构造函数通过`Prototype`属性链接到原型对象。**实例对象与构造函数原型之间有直接的联系,但实例对象与构造函数之间没有关系。**
|
||||||
@@ -1280,7 +1280,7 @@ function compare(value1, value2) {
|
|||||||
let result = compare(5, 10)
|
let result = compare(5, 10)
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
  如图所示,`compare()`函数是在全局上下文中调用的,第一次调用时,会为它创建一个包含`arguments`、`value1`和`value2`的活动对象,该对象是其作用域链上的第一个对象,而全局上下文的变量对象则是`compare()`作用域链上的第二个对象,包含`this`、`result`和`compare`。
|
  如图所示,`compare()`函数是在全局上下文中调用的,第一次调用时,会为它创建一个包含`arguments`、`value1`和`value2`的活动对象,该对象是其作用域链上的第一个对象,而全局上下文的变量对象则是`compare()`作用域链上的第二个对象,包含`this`、`result`和`compare`。
|
||||||
  全局上下文会在代码执行期间始终存在,而函数局部上下文只在函数执行期间存在。
|
  全局上下文会在代码执行期间始终存在,而函数局部上下文只在函数执行期间存在。
|
||||||
  在定义`compare()`函数时,会为其创建作用域链,预装载全局变量对象,并保存在内部的`[[Scope]]`中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的`[[Scope]]`来创建其作用域链,接着会创建函数的活动对象并将其推入作用域链的前端。
|
  在定义`compare()`函数时,会为其创建作用域链,预装载全局变量对象,并保存在内部的`[[Scope]]`中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的`[[Scope]]`来创建其作用域链,接着会创建函数的活动对象并将其推入作用域链的前端。
|
||||||
@@ -1307,7 +1307,7 @@ let compare = createComparisonFunction('name')
|
|||||||
let result = compare({'name': 'Cxx'}, {'name': 'Pjm'})
|
let result = compare({'name': 'Cxx'}, {'name': 'Pjm'})
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
  **在一个函数内部定义的函数会把其包含函数的活动对象添加到自己的作用域链中。**
|
  **在一个函数内部定义的函数会把其包含函数的活动对象添加到自己的作用域链中。**
|
||||||
  在`createComparisonFunction()`返回匿名函数后,其作用域链会被初始化为包含`createComparisonFunction()`的活动对象和全局变量对象,这样匿名函数就可以访问到`createComparisonFunction()`可以访问到的所有变量。
|
  在`createComparisonFunction()`返回匿名函数后,其作用域链会被初始化为包含`createComparisonFunction()`的活动对象和全局变量对象,这样匿名函数就可以访问到`createComparisonFunction()`可以访问到的所有变量。
|
||||||
  而当`createComparisonFunction()`执行完毕后,匿名函数仍然有它的引用,其执行上下文的作用域链会销毁,但它的活动对象仍然会保留在内存中,直到匿名函数被销毁后才会被销毁。
|
  而当`createComparisonFunction()`执行完毕后,匿名函数仍然有它的引用,其执行上下文的作用域链会销毁,但它的活动对象仍然会保留在内存中,直到匿名函数被销毁后才会被销毁。
|
||||||
@@ -1608,7 +1608,7 @@ setTimeout(() => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
  `Promise`一共有3种状态:待定`pending`、兑现`fulfilled`(或者称为解决`resolved`)和拒绝`rejected`。无论何时,`Promise`有且只可能是其中一种状态,且不可逆。待定是`Promise`的最初始状态,也是默认状态,当在`Promise`内部调用控制`Promise`状态的函数时,则会进行相应的状态切换,否则一致保持待定状态。
|
  `Promise`一共有3种状态:待定`pending`、兑现`fulfilled`(或者称为解决`resolved`)和拒绝`rejected`。无论何时,`Promise`有且只可能是其中一种状态,且不可逆。待定是`Promise`的最初始状态,也是默认状态,当在`Promise`内部调用控制`Promise`状态的函数时,则会进行相应的状态切换,否则一致保持待定状态。
|
||||||

|

|
||||||
  在`p1`中,立即调用`resolve()`函数将其状态转为解决,而在`p2`中,通过延迟1秒再将其状态转为拒绝,而`p3`会一直保持默认状态。
|
  在`p1`中,立即调用`resolve()`函数将其状态转为解决,而在`p2`中,通过延迟1秒再将其状态转为拒绝,而`p3`会一直保持默认状态。
|
||||||
  因此,`p1`的状态会一直保持解决状态,并带有解决的返回值。`p2`的状态一开始为待定,1秒后会变成拒绝状态,也带有拒绝的返回值,并且控制台会提示错误信息。`p3`则会一直处于待定状态,并没有返回值。
|
  因此,`p1`的状态会一直保持解决状态,并带有解决的返回值。`p2`的状态一开始为待定,1秒后会变成拒绝状态,也带有拒绝的返回值,并且控制台会提示错误信息。`p3`则会一直处于待定状态,并没有返回值。
|
||||||
|
|
||||||
|
|||||||
90
docs/Web-Front/Others/Package.md
Normal file
90
docs/Web-Front/Others/Package.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# npm
|
||||||
|
  2010年随Node.js发布的npm,首次为JavaScript引入了标准的包管理系统。其核心创新是**package.json文件**,通过语义化版本规范定义了依赖声明标准。这一设计解决了手动管理依赖时的版本混乱问题,为模块化开发提供了基础设施。
|
||||||
|
|
||||||
|
## 第一阶段:嵌套依赖架构
|
||||||
|
  早期版本采用树状嵌套的依赖结构。每个包将其依赖安装在自身的node_modules目录中,形成深层嵌套。这种设计保证了依赖隔离,但导致了几个严重问题:
|
||||||
|
- 路径深度问题:依赖树深度可达十几层,触发Windows 260字符路径限制
|
||||||
|
- 空间极度浪费:相同包在不同层级重复安装,项目体积指数级膨胀
|
||||||
|
- 安装性能低下:大量递归操作导致I/O效率极低
|
||||||
|
|
||||||
|
## 第二阶段:扁平化依赖重构
|
||||||
|
  2015年发布的npm v3引入扁平化依赖安装策略。该方案将**可共享的依赖提升到顶层node_modules**,减少嵌套深度。
|
||||||
|
  主要解决方案为:
|
||||||
|
- 依赖提升算法:将兼容版本的依赖包提升至顶层
|
||||||
|
- 冲突处理:版本冲突时,低版本保持嵌套结构
|
||||||
|
- 确定性牺牲:提升顺序影响最终结构
|
||||||
|
|
||||||
|
  但同时也引入了新问题:
|
||||||
|
- 幻影依赖:未声明但被提升的包意外可用
|
||||||
|
- 依赖不确定性:相同package.json产生不同目录结构
|
||||||
|
- 模块解析复杂化:Node.js模块解析算法与扁平结构不匹配
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
  2016年发布的Yarn针对npm核心痛点提出了系统性解决方案:
|
||||||
|
- 确定性版本锁定:yarn.lock文件记录精确版本和依赖树
|
||||||
|
- 并行下载优化:多线程并发下载提升网络利用率
|
||||||
|
- 离线缓存机制:全局缓存支持离线安装
|
||||||
|
- 完整性校验:checksum验证确保包完整性
|
||||||
|
|
||||||
|
  Yarn的成功迫使npm进行重大改进。npm v5版本借鉴了Yarn的核心设计,增加了package-lock.json和缓存优化,形成了技术竞争的良性循环。
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
  2017年发布的pnpm从存储层面重新设计了包管理模型。其核心是**基于内容寻址的全局存储与符号链接架构**。
|
||||||
|
  实现原理:
|
||||||
|
- 内容可寻址存储:包按内容哈希存储,全局唯一
|
||||||
|
- 硬链接复用:相同包文件在所有项目间共享
|
||||||
|
- 符号链接树:通过软链接构建符合Node.js解析规则的依赖树
|
||||||
|
- 严格依赖隔离:每个包只能访问其声明的依赖
|
||||||
|
|
||||||
|
  npm/yarn的node_modules目录:
|
||||||
|
```cmd
|
||||||
|
项目A: node_modules/lodash@4.17.21
|
||||||
|
项目B: node_modules/lodash@4.17.21 # 重复存储
|
||||||
|
项目C: node_modules/lodash@4.17.21 # 重复存储
|
||||||
|
```
|
||||||
|
|
||||||
|
  pnpm的node_modules目录:
|
||||||
|
```cmd
|
||||||
|
全局存储: .pnpm-store/lodash@4.17.21
|
||||||
|
项目A: 硬链接 → 全局存储
|
||||||
|
项目B: 硬链接 → 全局存储
|
||||||
|
项目C: 硬链接 → 全局存储
|
||||||
|
```
|
||||||
|
|
||||||
|
  npm/Yarn采用扁平化结构,包可访问非声明依赖,pnpm采用符号链接嵌套结构,包仅可访问声明依赖。
|
||||||
|
  pnpm的符号链接架构在Monorepo场景中表现优异。跨包依赖通过本地文件系统链接实现,避免了重复安装。Workspaces功能通过优化符号链接策略,确保开发环境与生产环境一致性。
|
||||||
|
|
||||||
|
## 简单使用
|
||||||
|
```cmd
|
||||||
|
# 通过 npm 安装
|
||||||
|
npm install -g pnpm
|
||||||
|
|
||||||
|
# 检查版本
|
||||||
|
pnpm --version
|
||||||
|
|
||||||
|
# 安装所有依赖(根据 package.json)
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 安装生产依赖
|
||||||
|
pnpm add <package-name>
|
||||||
|
|
||||||
|
# 安装开发依赖
|
||||||
|
pnpm add -D <package-name>
|
||||||
|
|
||||||
|
# 移除依赖
|
||||||
|
pnpm remove <package-name>
|
||||||
|
|
||||||
|
# 查看所有配置
|
||||||
|
pnpm config list
|
||||||
|
|
||||||
|
# 设置存储路径
|
||||||
|
pnpm config set store-dir ~/.pnpm-store
|
||||||
|
|
||||||
|
# 使用淘宝镜像
|
||||||
|
pnpm config set registry https://registry.npmmirror.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
npm确立了基本范式,Yarn解决了确定性问题,pnpm重构了存储模型
|
||||||
|
:::
|
||||||
|
|
||||||
166
docs/Web-Front/Others/Vite-Principle.md
Normal file
166
docs/Web-Front/Others/Vite-Principle.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# 一、Vite简介
|
||||||
|
  Vite是新一代的前端构建工具,在尤雨溪开发Vue3.0的时候诞生。类似于Webpack+ Webpack-dev-server。
|
||||||
|
  其主要利用浏览器**ESModule特性**导入组织代码,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。生产中利用Rollup作为打包工具,号称下一代的前端构建工具。
|
||||||
|
|
||||||
|
# 二、核心原理
|
||||||
|
## 2.1 ESModule
|
||||||
|
  ESModule是JavaScript提出的官方标准化模块系统,不同于之前的CJS,AMD,CMD等等,ESModule提供了更原生以及更动态的模块加载方案,最重要的就是它是浏览器原生支持的,也就是说我们可以**直接在浏览器中去执行import**,动态引入我们需要的模块,而不是把所有模块打包在一起。
|
||||||
|
  例如声明一个script标签:`<script type="module" src="/src/main.js"></script>`,当浏览器解析资源时,会往当前域名发起一个GET请求`main.js`文件:
|
||||||
|
```javascript
|
||||||
|
// main.js
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
createApp(App).mount('#app')
|
||||||
|
```
|
||||||
|
  请求到了main.js文件,会检测到内部含有import引入的包,又会import引用发起HTTP请求获取模块的内容文件,如App.vue文件。
|
||||||
|
|
||||||
|
## 2.2 基于ESModule的Dev Server
|
||||||
|
  Vite其核心原理是利用浏览器现在已经支持ES6的import,碰见import就会发送一个HTTP请求去加载文件,**Vite启动一个koa服务器拦截这些请求,并在后端进行相应的处理将项目中使用的文件通过简单的分解与整合,然后再以ESM格式返回返回给浏览器**。
|
||||||
|
  Vite整个过程中没有对文件进行打包编译,做到了真正的按需加载,所以其运行速度比原始的webpack开发编译速度快出许多。
|
||||||
|
|
||||||
|
## 2.3 基于ESModule的热更新
|
||||||
|
  热更新主要思路是通过WebSocket创建浏览器和服务器的通信监听文件的改变,当文件被修改时,服务端发送消息通知客户端修改相应的代码,客户端对应不同的文件进行不同的操作的更新。
|
||||||
|
  **Vite 通过 chokidar 来监听文件系统的变更,只用对发生变更的模块重新加载**,只需要精确的使相关模块与其临近的 HMR边界连接失效即可,这样HMR 更新速度就不会因为应用体积的增加而变慢而 Webpack 还要经历一次打包构建。所以 HMR 场景下,Vite 表现也要好于 Webpack。
|
||||||
|
|
||||||
|
## 2.4 基于Esbuild的开发环境编译
|
||||||
|
  Esbuild是一个JavaScript Bundler 打包和压缩工具,它提供了与Webpack、Rollup等工具相似的资源打包能力。可以将JavaScript 和TypeScript代码打包分发在网页上运行。但其打包速度却是其他工具的10~100倍。
|
||||||
|
  在开发环境中,Vite 使用 esbuild 进行快速的热重载和模块编译。
|
||||||
|
|
||||||
|
### 2.4.1 Esbuild高性能原理
|
||||||
|
1. Go 语言实现:避开 JavaScript 运行时的性能瓶颈。
|
||||||
|
2. 并行编译:利用多核 CPU 同时处理多个文件。
|
||||||
|
3. 内存优化:在单个进程中完成解析、转换和生成,减少内存开销。
|
||||||
|
|
||||||
|
### 2.4.2 Esbuild关键特性
|
||||||
|
1. 语法转换:TypeScript → JS(保留类型信息或移除)、现代 JS 语法降级(如 ?.、?? 转换)等。
|
||||||
|
2. 压缩优化:变量重命名、死代码消除、支持 CSS 压缩(通过 esbuild-plugin-css-minify)等。
|
||||||
|
|
||||||
|
## 2.5 基于Rollup的生产环境打包
|
||||||
|
  Rollup是基于ESM的JavaScript打包工具。相比于其他打包工具如Webpack,他总是能打出更小、更快的包。因为 Rollup 基于 ESM 模块,比 Webpack 和 Browserify 使用的 CommonJS模块机制更高效。Rollup的亮点在于同一个地方,一次性加载。能针对源码进行 Tree Shaking(去除那些已被定义但没被使用的代码),以及 Scope Hoisting 以减小输出文件大小提升运行性能。
|
||||||
|
  在生产环境中,Vite 使用 Rollup 进行最终的打包和优化。
|
||||||
|
|
||||||
|
### 2.5.1 Rollup构建流程
|
||||||
|
1. 解析入口文件:从 index.js 开始,递归分析所有 import 语句。
|
||||||
|
2. 生成抽象语法树(AST):使用 acorn 解析 JS 文件为 AST。
|
||||||
|
3. 应用插件转换:如 @rollup/plugin-typescript 处理 TypeScript。
|
||||||
|
4. 生成依赖图:分析模块间的依赖关系。
|
||||||
|
5. 输出优化:
|
||||||
|
Tree-shaking:基于 ESM 静态分析,移除未使用的代码
|
||||||
|
代码分割:识别动态导入(import())并拆分包。。
|
||||||
|
|
||||||
|
# 三、Mini-Vite
|
||||||
|
## 3.1 初始化工程
|
||||||
|
  初始化NodeJs工程,并添加Vue3依赖
|
||||||
|
|
||||||
|
## 3.2 Node Server
|
||||||
|
```javascript
|
||||||
|
const http = require('http');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const miniVueParse = require('./mini-vue-parser')
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
// 获取请求的路径
|
||||||
|
const reqPath = req.url;
|
||||||
|
// 构建本地文件路径
|
||||||
|
const filePath = path.join(__dirname, reqPath);
|
||||||
|
|
||||||
|
console.log('filePath: ', filePath)
|
||||||
|
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
fs.access(filePath, fs.constants.R_OK, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('文件不存在:', err);
|
||||||
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('文件不存在');
|
||||||
|
} else {
|
||||||
|
// 读取文件并发送给客户端
|
||||||
|
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('读取文件出错:', err);
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('内部服务器错误');
|
||||||
|
} else {
|
||||||
|
// 根据文件类型设置合适的 Content-Type
|
||||||
|
const extname = path.extname(filePath);
|
||||||
|
let contentType = 'text/plain';
|
||||||
|
switch (extname) {
|
||||||
|
case '.html':
|
||||||
|
contentType = 'text/html';
|
||||||
|
break;
|
||||||
|
case '.js':
|
||||||
|
contentType = 'text/javascript';
|
||||||
|
break;
|
||||||
|
case '.vue':
|
||||||
|
contentType = 'text/javascript';
|
||||||
|
break;
|
||||||
|
case '.css':
|
||||||
|
contentType = 'text/css';
|
||||||
|
break;
|
||||||
|
// 添加其他需要支持的文件类型
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(200, { 'Content-Type': contentType });
|
||||||
|
|
||||||
|
if (extname === '.js') {
|
||||||
|
data = data.replace(/from ['"]vue['"]/g, "from './node_modules/vue/dist/vue.esm-browser.js'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extname === '.vue') {
|
||||||
|
const fileName = path.basename(filePath);
|
||||||
|
data = miniVueParse.parse(data, fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const port = 3000;
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`代理服务器正在监听端口 ${port}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
  在浏览器输入`http://localhost:3000/index.html`,会首先解析`<script type="module" src="main.js"></script>`,获取`main.js`,然后会把`import { createApp } from 'vue'`中的`Vue`换成替换成:`./node_modules/vue/dist/vue.esm-browser.js'`,在遇到`Vue`文件时,会利用`Vue`的模板编译器转成Javascript文件:
|
||||||
|
  mini-vue-parser.js:
|
||||||
|
```JavaScript
|
||||||
|
const compilerSfc = require("@vue/compiler-sfc");
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function parse(vueFileString,fileName) {
|
||||||
|
const sfc = compilerSfc.parse(
|
||||||
|
vueFileString
|
||||||
|
);
|
||||||
|
const sfcTemplate = compilerSfc.compileTemplate({
|
||||||
|
id: 'sfc',
|
||||||
|
filename: fileName + '.template.vue',
|
||||||
|
source: sfc.descriptor.template.content
|
||||||
|
})
|
||||||
|
|
||||||
|
const sfcScript = compilerSfc.compileScript(sfc.descriptor, {
|
||||||
|
filename: 'sfc.script.vue'
|
||||||
|
})
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
result += sfcTemplate.code.replace("export", "") + '\n';
|
||||||
|
result += sfcScript.content.replace("export default ", "const __script = ") + '\n';
|
||||||
|
result += `__script.render=render;\n`
|
||||||
|
result += `export default __script;`
|
||||||
|
result = result.replace(/from ['"]vue['"]/g, "from './node_modules/vue/dist/vue.esm-browser.js'");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
parse
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 四、参考资料
|
||||||
|
1. [深入浅出vite(核心原理 + 手撕mini-vite)](https://juejin.cn/post/7026285200766140453#heading-37)
|
||||||
|
2. [深入理解Vite核心原理](https://juejin.cn/post/7064853960636989454)
|
||||||
|
3. [step-vite](https://github.com/Llyonss/step-vite)
|
||||||
362
docs/Web-Front/Vue/Vue3-Architecture.md
Normal file
362
docs/Web-Front/Vue/Vue3-Architecture.md
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
# 一、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}}` // 生成渲染函数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -11,8 +11,12 @@ description: Web前端开发技术文档
|
|||||||
### 🌿 Vue
|
### 🌿 Vue
|
||||||
Vue 框架学习:
|
Vue 框架学习:
|
||||||
|
|
||||||
- [Vue3-Common](/Web-Front/Vue3-Common)
|
- [Vue3-Common](/Web-Front/Vue/Vue3-Common)
|
||||||
|
- [Vue3架构原理](/Web-Front/Vue/Vue3-Architecture)
|
||||||
|
|
||||||
### 🔄 其他
|
### 🔄 其他
|
||||||
|
|
||||||
- [JavaScript知识点整理](/Web-Front/JavaScript-Guide)
|
- [JavaScript知识点整理](/Web-Front/Others/JavaScript-Guide)
|
||||||
|
- [前端包管理器](/Web-Front/Others/Package)
|
||||||
|
- [Vite核心原理](/Web-Front/Others/Vite-Principle)
|
||||||
|
- [浏览器渲染全流程解析](/Web-Front/Others/Browser-Process)
|
||||||
@@ -27,4 +27,5 @@ features:
|
|||||||
- title: 🔍 快速检索
|
- title: 🔍 快速检索
|
||||||
details: 清晰的分类导航和搜索功能,方便快速定位所需的技术知识点
|
details: 清晰的分类导航和搜索功能,方便快速定位所需的技术知识点
|
||||||
---
|
---
|
||||||
|
|
||||||
<Confetti />
|
<Confetti />
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"canvas-confetti": "^1.9.4",
|
"canvas-confetti": "^1.9.4",
|
||||||
"medium-zoom": "^1.1.0",
|
"medium-zoom": "^1.1.0",
|
||||||
"vitepress": "1.6.4"
|
"mermaid": "^11.12.2",
|
||||||
|
"vitepress": "1.6.4",
|
||||||
|
"vitepress-plugin-mermaid": "^2.0.17"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev docs --host 0.0.0.0",
|
"docs:dev": "vitepress dev docs --host 0.0.0.0",
|
||||||
|
|||||||
980
pnpm-lock.yaml
generated
980
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user