ES6 再学习 2021-01-08
2023-06-29 18:20:06 刚读了一遍,并复写在了 03_js/076_结束.html
.
Vue 再学习 2021-01-11
2023-06-29 18:19:22 刚把它简要读了一遍,感谢我之前做的好笔记啊。
.
微信读书
操作笔记 09_vue/书籍:Vue.js设计与实现
2023-06-29 16:46:49(开始)
2023-07-28 10:49:31(结束) 一个月了,拖拖拉拉,真的很想是放弃了,慢慢还是坚持住了。
感悟
1 | 2023-07-03 09:48:45 |
前言
1 | # Vue.js 3.0 |
第1章 权衡的艺术
“框架设计里到处都体现了权衡的艺术。”
1.1 命令式和声明式
1 | 命令式框架 => 关注过程 |
1.2 性能与可维护性的权衡
1 | [命令式, 声明式] 各有好坏,框架需权衡 [性能, 可维护性]。 |
1.3 虚拟 DOM 的性能到底如何
1 | 所谓的虚拟 DOM,就是为了最小化找出差异这一步的性能消耗而出现的。 |
1.4 运行时和编译时
1 | # 框架的三种选择 |
1.5 总结
1 | # 命令式、声明式 |
第2章 框架设计的核心要素
2.1 提升用户的开发体验
1 | # 开发体验 |
2.2 控制框架代码的体积
1 | # 代码体积 |
2.3 框架要做到良好的 Tree-Shaking
1 | # Tree-Shaking |
2.4 框架应该输出怎样的构建产物
1 | # iife |
2.6 错误处理
1 | # 统一定义 utils.js |
2.7 良好的 TypeScript 类型支持
1 | # TS 优点 |
2.8 总结
1 | 开发体验是衡量一个框架的重要指标之一。 |
第3章 Vue.js 3 的设计思路
3.1 声明式地描述 UI
1 | # Vue.js 3 => 声明式 UI 框架 |
3.2 初识渲染器
1 | 1.虚拟DOM => 2.渲染器 => 3.真实DOM |
3.3 组件的本质
1 | 组件就是一组 DOM 元素的封装,这组 DOM 元素就是组件要渲染的内容。 |
3.4 模板的工作原理
1 | 模板 => 编译器 => 渲染函数 |
3.5 Vue.js 是各个模块组成的有机整体
1 | # 模板 |
3.6 总结
1 | Vue.js 是一个声明式的框架。声明式的好处在于,它直接描述结果,用户不需要关注过程。 |
第4章 响应系统的作用与实现
4.1 响应式数据与副作用函数
1 | # 副作用 |
4.2 响应式数据的基本实现
1 | 在 ES2015之前,只能通过 Object.defineProperty 函数实现,这也是 Vue.js 2 所采用的方式。 |
4.3 设计一个完善的响应系统
1 | 上面,我们硬编码了副作用函数的名字(effect),导致一旦副作用函数的名字不叫 effect,那么这段代码就不能正确地工作了。 |
1 | # 三个角色 |
1 | // 存储副作用函数的桶(WeakMap 代替 Set ) |
1 | # 在 get 拦截函数内调用 track 函数追踪变化 |
4.4 分支切换与 cleanup
1 | # 触发3次 |
4.5 嵌套的 effect 与 effect 栈
1 | # effect 可嵌套 |
1 | # 009_4.5_响应式 v09 嵌套 错误演示.html |
1 | # 010_4.5_响应式 v10 嵌套 栈.html |
4.6 避免无限递归循环
1 | # 011_4.6_响应式 v11 无限递归 错误演示.html |
1 | # 012_4.6_响应式 v12 无限递归 守卫条件.html |
4.7 调度执行
1 | # 013_4.7_响应式 v13 调度 顺序问题 正常.html |
1 | # 014_4.7_响应式 v14 调度 顺序问题 改变.html |
1 | # 015_4.7_响应式 v15 调度 123 有中间状态.html |
1 | # 016_4.7_响应式 v16 调度 13 无中间状态.html |
4.8 计算属性 computed 与 lazy
1 | # 017_4.8_响应式 v17 lazy 无返回值.html |
1 | # 018_4.8_响应式 v18 lazy 有返回值.html |
1 | # 019_4.8_响应式 v19 computed 重新计算问题.html |
1 | # 020_4.8_响应式 v20 computed dirty解决重新计算.html |
1 | # 021_4.8_响应式 v21 computed 更新不变问题.html |
1 | # 022_4.8_响应式 v22 computed scheduler解决更新不变.html |
1 | # 023_4.8_响应式 v23 computed 内层变化不影响外层.html |
1 | # 024_4.8_响应式 v24 computed 内层变化影响外层.html |
4.9 watch 的实现原理
所谓 watch,其本质就是观测一个响应式数据,当数据发生变化时通知并执行相应的回调函数。
实际上,watch 的实现本质上就是利用了 effect 以及 options.scheduler 选项。
1 | # 025_4.9_响应式 v25 watch 固定属性.html |
1 | # 026_4.9_响应式 v26 watch 任意属性.html |
1 | # 027_4.9_响应式 v27 watch getter函数.html |
1 | # 028_4.9_响应式 v28 watch 新旧值.html |
4.10 立即执行的 watch 与回调执行时机
1 | # 029_4.10_响应式 v29 watch immediate立即执行.html |
1 | # 030_4.10_响应式 v30 watch flush执行时机.html |
4.12 总结
1 | # 响应式数据的拦截机制 |
第5章 非原始值的响应式方案
1 | 什么。实际上,实现响应式数据要比想象中难很多,并不是像上一章讲述的那样,单纯地拦截get/set 操作即可。 |
5.1 理解 Proxy 和 Reflect
Proxy
1 | # 基本语义 |
Reflect
1 | # Reflect |
访问器属性的 this 问题
1 | # 031_5.1_响应式 v31 Proxy 属性访问器this.html |
1 | # 032_5.1_响应式 v32 Proxy 属性访问器this receiver.html |
5.2 JavaScript 对象及 Proxy 的工作原理
1 | 如何区分一个对象是普通对象还是函数呢? |
5.3 如何代理 Object
1 | 一个普通对象的所有可能的读取操作。 |
1 | # 035_5.3_响应式 v35 ownKeys for...in 新旧属性都触发.html |
1 | # 036_5.3_响应式 v36 ownKeys for...in 仅新增属性触发.html |
1 | # 037_5.3_响应式 v37 ownKeys for...in 删除属性触发.html |
5.4 合理地触发响应
1 | # 038_5.4_响应式 v38 值未变也触发.html |
1 | # 039_5.4_响应式 v39 值变了才触发.html |
1 | # 040_5.4_响应式 v40 NaN问题.html |
1 | # 041_5.4_响应式 v41 解决NaN问题.html |
1 | # 042_5.4_响应式 v42 代理原型问题.html |
1 | # 043_5.4_响应式 v43 解决代理原型问题.html |
5.5 浅响应与深响应
1 | # 044_5.5_响应式 v44 浅响应.html |
1 | # 045_5.5_响应式 v45 深响应.html |
1 | # 046_5.5_响应式 v46 浅深响应.html |
5.6 只读和浅只读
1 | # 047_5.5_响应式 v47 readonly只读.html |
5.7 代理数组
1 | 在 JavaScript 中,数组只是一个特殊的对象而已。 |
5.7.1 数组的索引与 length
1 | # 048_5.7.1_响应式 v48 数组 index设置可触发.html |
1 | # 049_5.7.1_响应式 v49 数组 length不会重新触发.html |
1 | # 050_5.7.1_响应式 v50 数组 解决length不会重新触发.html |
1 | # 051_5.7.1_响应式 v51 数组 length属性设置问题.html |
1 | # 052_5.7.1_响应式 v52 数组 解决length属性设置问题.html |
5.7.2 遍历数组
1 | # 053_5.7.2_响应式 v53 数组 for...in.html |
1 | # 054_5.7.2_响应式 v54 数组 for...in length属性问题.html |
1 | # 055_5.7.2_响应式 v55 数组 for...of.html |
1 | # 056_5.7.2_响应式 v56 数组 for...of length属性问题.html |
5.7.3 数组的查找方法
1 | # 057_5.7.3_响应式 v57 数组 代理对象includes.html |
1 | # 058_5.7.3_响应式 v58 数组 原始对象includes.html |
1 | # 059_5.7.3_响应式 v59 数组 重写includes.html |
1 | # 060_5.7.3_响应式 v60 数组 重写includes indexOf lastIndexOf.html |
5.7.4 隐式修改数组长度的原型方法
1 | # 061_5.7.4_响应式 v61 数组 push oom.html |
1 | # 062_5.7.4_响应式 v62 数组 重写push.html |
1 | # 063_5.7.4_响应式 v63 数组 重写push pop shift unshift splice.html |
5.8 代理 Set 和 Map
5.8.1 如何代理 Set 和 Map
1 | # 064_5.8.1_响应式 v64 set 代理问题.html |
1 | # 065_5.8.1_响应式 v65 set 封装new Proxy.html |
5.8.2 建立响应联系
1 | # 066_5.8.2_响应式 v66 set add未触发响应.html |
1 | # 067_5.8.2_响应式 v67 set add delete.html |
5.8.3 避免污染原始数据
1 | # 068_5.8.3_响应式 v68 map set触发响应.html |
1 | # 069_5.8.3_响应式 v69 map 污染问题.html |
1 | # 070_5.8.3_响应式 v70 map 解决污染问题.html |
5.8.4 处理 forEach
1 | # 071_5.8.4_响应式 v71 map foreach.html |
1 | # 072_5.8.4_响应式 v72 map foreach 改造.html |
1 | # 073_5.8.4_响应式 v73 map set修改问题.html |
5.8.5 迭代器方法
1 | # 074_5.8.5_响应式 v74 map 正常迭代.html |
1 | # 075_5.8.5_响应式 v75 map 代理迭代.html |
1 | # 076_5.8.5_响应式 v76 map 解决代理迭代.html |
1 | # 077_5.8.5_响应式 v77 map k v响应式数据.html |
1 | # 078_5.8.5_响应式 v78 map entries问题.html |
1 | # 079_05.8.5_响应式 v79 map 解决entries问题.html |
5.8.6 values 与 keys 方法
1 | # 080_5.8.5_响应式 v80 map values.html |
1 | # 081_5.8.5_响应式 v81 map keys修改问题.html |
1 | # 082_5.8.5_响应式 v82 map 解决keys修改问题.html |
5.9 总结
1 | ## 对象和 Proxy |
第6章 原始值的响应式方案
1 | 第5章 非原始值的响应式方案 |
6.1 引入 ref 的概念
1 | # 083_6.1_响应式 v83 基本值不能代理.html |
1 | # 084_6.1_响应式 v84 ref包裹基本值.html |
1 | # 085_6.1_响应式 v85 ref标识.html |
6.2 响应丢失问题
1 | # 数据暴露 |
1 | # 086_6.2_响应式 v86 ...展开符 响应丢失问题.html |
1 | # 087_6.2_响应式 v87 重写属性 toRef toRefs.html |
1 | # 088_6.2_响应式 v88 完善toRef set __v_isRef.html |
6.3 自动脱 ref
1 | # 089_6.3_响应式 v89 属性值访问对比.html |
1 | # 090_6.3_响应式 v90 字动脱 ref get.html |
6.4 总结
1 | # ref 的概念 |
第7章 渲染器的设计
1 | 渲染器的代码量非常庞大,需要合理的架构设计来保证可维护性,不过它的实现思路并不复杂。 |
7.1 渲染器与响应系统的结合
1 | # 渲染 |
1 | 它暴露的全局 API 名叫 VueReactivity |
7.2 渲染器的基本概念
1 | # 渲染器的作用:虚拟 DOM 渲染为 真实 DOM |
1 | # 简单的 createRenderer函数 |
1 | # 渲染一次 |
1 | # 定义函数 createRenderer |
7.3 自定义渲染器
1 | # ============= 大致代码结构 |
7.4 总结
1 | # 渲染器与响应系统 |
第8章 挂载与更新
8.1 挂载子节点和元素的属性
children 数组:挂载多个节点
1 | # children => 对象 改为 数组 |
节点属性
1 | # 新增属性 vnode.props |
8.2 HTML Attributes 与 DOM Properties
1 | # HTML Attributes 与 DOM Properties |
8.3 正确地设置元素属性
浏览器解析、Vue.js解析
1 | # 浏览器 |
disabled 属性存在,就会禁用
1 |
|
1 | # 布尔值 矫正问题 |
1 | # 问题 => 只读属性 |
1 | # 抽取 patchProps |
8.4 class 的处理
1 | <p class="foo bar"></p> |
8.5 卸载操作
1 | // 初次挂载 |
1 | # vnode.el 引用 |
1 | # 抽取方法 unmount ===> 为了后续在卸载时,扩展更多的功能 |
8.6 区分 vnode 的类型
type不同:卸载 old_node,挂载 new_node
1 | # 测试代码 |
不同类型的 vnode 处理
1 | 一个 vnode 可以用来描述普通标签,也可以用来描述组件,还可以用来描述 Fragment 等。 |
8.7 事件的处理
v1 添加事件监听
1 | # 测试对象 |
v2 invoker 包装单个事件处理
1 | # 不用每次都移除监听,添加监听 |
v3 invoker 包装多个事件处理
1 | # 测试对象 |
v4 invoker 单个事件, 多个处理
1 | # addEventListener 相同事件,支持多个处理函数 |
8.8 事件冒泡与更新时机问题
1 | # 测试数据 |
8.9 更新子节点
回顾一下 元素的子节点是如何被挂载的
1 | function mountElement(vnode, container) { |
子节点:三种类型、九种更新
1 | # 子节点:三种类型 |
处理子节点
1 | function patchElement(n1, n2) { |
8.10 文本节点和注释节点
1 | # Symbol 标识 => Text Comment |
跨平台处理
1 | patch 函数依赖浏览器平台特有的API,即 createTextNode 和 el.nodeValue。 |
8.11 Fragment
什么是 Fragment?
1 | Fragment(片断)是 Vue.js 3 中新增 vnode 类型。 |
Fragment 挂载、卸载
1 | # 挂载 Fragment |
8.12 总结
1 | # 节点挂载、属性和卸载 |
第9章 简单 Diff 算法
1 | 当新旧 vnode 的子节点都是一组节点时,为了性能开销更低,需比较两组子节点的算法就叫 Diff 算法。 |
9.1 减少 DOM 操作的性能开销
相同节点
1 | 按照之前的算法,需要 6次 DOM操作:3次DOM卸载,3次DOM挂载。 |
不相同节点:挂载、卸载
1 | function patchChildren(n1, n2, container) { |
9.2 DOM 复用与 key 的作用
1 | 关于这个也需要 6次DOM操作,3次循环(卸载旧的,挂载新的)。 |
1 | # 复用 DOM(key相同) |
9.3 找到需要移动的元素
1 | function patchChildren(n1, n2, container) { |
9.4 如何移动元素
1 | # 指向同一个真实 DOM |
9.5 添加新元素
1 | function patchChildren(n1, n2, container) { |
patch 第四个参数:锚点元素
1 | // patch 函数需要接收第四个参数,即锚点元素 |
9.6 移除不存在的元素
1 | function patchChildren(n1, n2, container) { |
9.7 总结
1 | # Diff算法与DOM操作优化 |
第10章 双端 Diff 算法
10.1 双端比较的原理
1 | # 分析 => 对照着图片,想想就知道了。 |
10.2 双端比较的优势
1 | 简单 Diff 算法 ===> 两次 DOM 移动 |
10.3 非理想状况的处理方式
1 | # 分析 => 对照着图片,想想就知道了。 |
10.4 添加新元素
1 | # 分析 |
10.5 移除不存在的元素
1 | # 分析 |
10.6 总结
1 | # 双端 Diff 算法 |
第11章 快速 Diff 算法
1 | 速度很快,最早应用于 ivi 和 inferno 这两个框架,Vue.js 3 借鉴并扩展了它。 |
11.1 相同的前置元素和后置元素
纯文本 Diff 算法
1 | 借鉴了纯文本 Diff 算法的思路。 |
1 | function patchKeyedChildren(n1, n2, container) { |
11.2 判断是否需要进行 DOM 移动操作
1 | function patchKeyedChildren(n1, n2, container) { |
11.3 如何移动元素
递增子序列
1 | # 分析 |
移动元素的操作代码位置
1 | # 移动元素的操作代码位置 |
快速 diff 算法 => 完整代码
1 | function patchKeyedChildren(n1, n2, container) { |
11.4 总结
1 | 快速 Diff 算法在实测中性能最优。 |
第12章 组件的实现原理
1 | 有了组件,我们就可以将一个大的页面拆分为多个部分, |
12.1 渲染组件
回顾 vnode.type
1 | # 不同类型的 vnode.type |
组件的 vnode.type表示
1 | # 组件的处理 vnode.type === 'object' |
12.2 组件状态与自更新
1 | # 组件渲染 data 数据 |
12.3 组件实例与组件的生命周期
1 | # 分析 |
12.4 props 与组件的被动更新
1 | # 模版 |
1 | # 父组件模版 |
渲染上下文对象 renderContext
1 | # 代码实现 |
12.5 setup 函数的作用与实现
setup 简介
1 | # Vue 3 新增 setup |
setup 最小实现
1 | function mountComponent(vnode, container, anchor) { |
12.6 组件事件与 emit 的实现
1 | # 组件的事件处理 |
emit 代码实现
1 | # setupContext.emit |
12.7 插槽的工作原理与实现
插槽 slot
1 | 顾名思义,组件的插槽指组件会预留一个槽位,该槽位具体要渲染的内容由用户插入, |
setupContext.slots 代码实现
1 | # 分析 |
12.8 注册生命周期
1 | Vue.js 3 生命周期钩子函数,例如 onMounted、onUpdated 等。 |
12.9 总结
1 | # 组件和组件实例 |
第13章 异步组件与函数式组件
1 | 异步组件:以异步的方式加载并渲染一个组件。 |
13.1 异步组件要解决的问题
异步示例
1 | # 同步渲染 |
异步加载组件
1 | # 考虑点 |
13.2 异步组件的实现原理
13.2.1 封装 defineAsyncComponent 函数
1 | # defineAsyncComponent 使用 |
13.2.2 超时与 Error 组件
1 | # 定义参数 timeout errorComponent |
13.2.3 延迟与 Loading 组件
1 | # 定义参数 delay loadingComponent |
Loading 组件的卸载
1 | // 当异步组件加载成功后,会卸载 Loading 组件并渲染异步加载的组件。 |
13.2.4 重试机制
模拟重试
1 | 封装一个 fetch 函数,用来模拟接口请求: |
defineAsyncComponent 处理重试
1 | function defineAsyncComponent(options) { |
13.3 函数式组件
1 | 一个函数式组件本质上就是一个普通函数,该函数的返回值是虚拟 DOM。 |
函数式组件的支持
1 | # patch |
13.4 总结
1 | # 异步组件 |
第14章 内建组件和模块
14.1 KeepAlive 组件的实现原理
14.1.1 组件的激活与失活
KeepAlive 组件是什么?
1 | # KeepAlive 组件作用 |
KeepAlive 组件的实现原理
1 | # KeepAlive 组件的实现原理 |
KeepAlive 具体实现
1 | const KeepAlive = { |
参数解释
1 | KeepAlive 组件本身并不会渲染额外的内容,它的渲染函数最终只返回需要被 KeepAlive 的组件,我们把这个需要被 KeepAlive 的组件称为“内部组件”。 |
1 | # 回顾 [失活 / 激活] |
14.1.2 include 和 exclude
1 | 在默认情况下,KeepAlive 组件会对所有“内部组件”进行缓存。 |
14.1.3 缓存管理
1 | # 回顾之前代码 |
14.2 Teleport 组件的实现原理
14.2.1 Teleport 组件要解决的问题
1 | # DOM层级结构一致问题 |
14.2.2 实现 Teleport 组件
1 | # Teleport 组件 抽取代码的好处: |
1 | # patch => Teleport组件 |
14.3 Transition 组件的实现原理
1 | # Transition 组件的实现原理 |
14.3.1 原生 DOM 的过渡
1 | # 过渡 |
1 | 从创建 DOM 元素完成后,到把 DOM 元素添加到 body 前,整个过程可以视作 beforeEnter 阶段。 |
14.3.2 实现 Transition 组件
1 |
|
14.4 总结
1 | # KeepAlive 组件 |
第五篇 编译器
第15章 编译器核心技术概览
1 |
|
15.1 模板 DSL 的编译器
- 完整的编译过程
1
2
3
4
5
6
7
8
9
10
11# 源代码 => 编译器 => 目标代码
编译器是一段程序,把源代码(source code)翻译为目标代码(target code),这个翻译的过程叫做编译(compile)。
# 整个编译过程:编译前端 + 编译后端
编译前端:词法分析 + 语法分析 + 语义分析
- 与目标平台无关,仅分析源代码。
编译后端:中间代码生成 + 优化 + 目标代码生成
- 与目标平台有关
- 不一定会包含 [中间代码生成+优化],这取决于场景和实现
- [中间代码生成+优化] 也叫“中端”
- 模版 DSL 编译器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69# Vue.js模版编译器
# 源代码
<div>
<h1 :id="dynamicId">Vue Template</h1>
</div>
↓↓↓ Vue.js 模版编译器 ↓↓↓
# 目标代码(js)
function render() {
return h('div', [
h('h1', { id: dynamicId }, 'Vue Template')
])
}
# 编译完整流程
AST 是 abstract syntax tree 的首字母缩写,即抽象语法树。
const template = `
<div>
<h1 v-if="ok">Vue Template</h1>
</div>
`
// 三部分:解析器,转换器,代码生成器
const templateAST = parse(template)
const jsAST = transform(templateAST)
const code = generate(jsAST)// 渲染函数的代码(字符串形式返回)
# 模版AST => 语义分析
有了模板 AST 后,我们就可以对其进行语义分析,并对模板 AST 进行转换了。
什么是语义分析呢?举几个例子。
● 检查 v-else 指令是否存在相符的 v-if 指令。
● 分析属性值是否是静态的,是否是常量等。
● 插槽是否会引用上层作用域的变量。
● ……
# 模版AST 样例
const templateAST = {
// 逻辑根节点
type: 'Root',
children: [
// div 标签节点
{
type: 'Element',
tag: 'div',
children: [
// h1 标签节点
{
type: 'Element',
tag: 'h1',
props: [
// v-if 指令节点
{
type: 'Directive', // 类型为 Directive 代表指令
name: 'if', // 指令名称为 if,不带有前缀 v-
exp: {
// 表达式节点
type: 'Expression',
content: 'ok'
}
}
]
}
]
}
]
}
15.2 parser 的实现原理与状态机
解析器 parser
1 | # 三部分 |
有限状态机 初步实现
1 | // 定义状态机的状态 |
测试与分析
1 | # 测试 |
15.3 构造 AST
1 | 通用用途语言(GPL) |
1 | # 回顾tokenize的使用 |
15.4 AST 的转换与插件化架构
1 | # 再回顾 |
15.4.1 节点的访问
dump 打印节点信息
1 | # dump函数 打印节点信息 |
p变为h1、文本内容重复两次
1 | # traverseNode函数 => 深度优先 |
解耦:抽取函数
1 | # traverseNode => nodeTransforms使用 |
15.4.2 转换上下文与节点操作
1 |
|
1 | # 测试1:replaceNode 替换节点 |
15.4.3 进入与退出
1 | 上面,我们处理完子节点,就不能再处理父节点了。 |
15.5 将模板 AST 转为 JavaScript AST
FunctionDeclNode 函数声明节点的表示(基础版)
1 | # 模版 |
h函数的调用表示,与参数表示
1 | const CallExp = { |
创建辅助函数 createStringLiteral createIdentifier createArrayExpression createCallExpression
1 | 编写转换函数,将模板 AST 转换为上述 JavaScript AST。 |
转换函数 transformElement transformText
1 | 模板 AST 转换为 JavaScript AST,还需两个转换函数:transformElement(处理标签节点) 和 transformText(处理文本节点)。 |
根节点处理 transformRoot
1 | 最后一步,补全 JavaScript AST,即把用来描述render 函数本身的函数声明语句节点附加到 JavaScript AST 中。 |
15.6 代码生成
1 | 代码生成本质上是字符串拼接的艺术。 |
genNode 代码实现
1 | # genNode |
15.7 总结
1 | # Vue.js 模板编译器工作流程 |
第16章 解析器
1 | 解析器(parser)本质上是一个状态机。 |
16.1 文本模式及其对解析器的影响
1 | 解析器遇到特殊标签,会切换模式,从而影响解析行为。 |
16.2 递归下降算法构造模板 AST
解析器 基本模型
1 | 从现在开始,我们将着手实现一个更加完善的模板解析器。 |
parseChildren 函数在解析模板过程中的状态迁移过程
1 | # 元素节点 |
举个栗子
1 | # 模版 |
16.3 状态机的开启与停止
初步认识 isEnd
1 | # 标签压栈 与 状态机停止 |
错误的标签处理
1 | 例如:<div><span></div></span> |
16.4 解析标签节点
1 | # 回顾 |
16.5 解析属性
1 | # 带属性的标签 |
Attribute 与 Directive
1 | # case 1 |
16.6 解析文本与解码 HTML 实体
16.6.1 解析文本
1 | 解析完标签,下面就是解析文本 |
16.6.2 解码命名字符引用
1 | # < 变 < |
代码
1 | # parseText |
16.6.3 解码数字字符引用
1 | function decodeHtml(rawText, asAttr = false) { |
16.7 解析插值与注释
花括号
1 | # 花括号的使用 |
注释
1 | # parseComment |
16.8 总结
1 | # 解析器的文本模式及影响 |
第17章 编译优化
17.1 动态节点收集与补丁标志
17.1.1 传统 Diff 算法的问题
1 | # 传统 Diff 算法 |
17.1.2 Block 与 PatchFlags
1 | # dynamicChildren 属性 |
17.4 预字符串化
1 | # 字符串优化 |
17.5 缓存内联事件处理函数
1 | # 模版 |
17.6 v-once
1 | # 模版 |
17.7 总结
1 | # Vue.js 3 编译优化 |
第六篇 服务端渲染
第18章 同构渲染
1 | Vue.js 可以用于构建客户端应用程序,组件的代码在浏览器中运行,并输出 DOM 元素。 |
18.1 CSR、SSR 以及同构渲染
1 | # 早期 SSR |
18.5.3 只在某一端引入模块
1 | # 环境不同,引入模版不同 |
18.6 总结
1 | # CSR、SSR 和同构渲染工作机制及优缺点 |