ALEX CODE PARK

vuePress-theme-reco ALEX CODE PARK    2020
ALEX CODE PARK

Choose mode

  • dark
  • auto
  • light
阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
author-avatar

ALEX CODE PARK

19

文章

10

标签

阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
  • 《深入浅出Vue.js》

    • ⭕️ 第二篇 虚拟 DOM
      • 什么是虚拟 DOM
      • 为什么要引入虚拟 DOM
      • Vue.js 中的虚拟 DOM
      • ----------
      • 什么是 VNode
      • VNode 的作用
      • VNode 类型
      • ----------
      • Patch 介绍
      • 创建节点
      • 删除节点
      • 更新节点
      • 更新子节点【详解】
    • ⭕️ 第三篇 模板编译原理
      • 模板编译介绍
      • 解析器
      • 优化器
      • 代码生成器
      • ----------
      • 解析器内部运行原理
      • HTML 解析器

《深入浅出Vue.js》

vuePress-theme-reco ALEX CODE PARK    2020

《深入浅出Vue.js》


AlexChiang 2020-03-07 JavaScript Vue

# ⭕️ 第二篇 虚拟 DOM

# 什么是虚拟 DOM

🔥🔥虚拟 DOM 是将状态映射成视图的众多解决方案之一

虚拟 DOM 的解决方式是通过状态生成一个虚拟节点树 🌲,然后使用虚拟节点树进行渲染。 在渲染之前,会使用新生成的寻你节点树和上一次生成的虚拟节点树进行对比,只渲染不同的部分。

# 为什么要引入虚拟 DOM

Angular 和 react 的变换侦测都不知道哪些状态变了,因此需要进行暴力比对。

  • Angular 使用脏检查
  • React 使用虚拟 DOM

Vue 的变化侦测和上两者不同,因为 vue 知道具体哪些状态发生了变化,这样就可以通过更细的粒度来绑定更新视图。

  • Vue.js 1.0 无需比对直接对更新节点操作 (无虚拟 DOM 操作)
    • 因为粒度太细,每一个绑定的状态都有一个相应的 watcher 来观察
    • 当状态被越多的节点使用时开销会变得极大
  • Vue.js 2.0 选择中等粒度的解决方案 —— 引入虚拟 DOM
    • 一个 watcher 实例仅观察一个组件
    • 🔥状态变化时 watcher 通知组件,组件内通过虚拟 DOM 去进行比对与渲染

# Vue.js 中的虚拟 DOM

模板 ⏩ 渲染函数 ⏩ vnode(patch) ⏩ 视图

  • 模板:我们使用模板来描述状态与 DOM 之间的映射关系 如{{}}语法
  • Vue.js 通过编译将模板转换成渲染函数 render
  • 执行 render 即可得到虚拟节点树 🌲
  • 使用虚拟节点树进行Patch 算法后即可更新视图
    • 🔥Patch 算法详见下文

# ----------

# 什么是 VNode

Vue.js 中存在一个 vnode 类,使用它可以实例化不同类型的 vnode 实例,而不同类型的 vnode 实例各自表示不同类型的 DOM 元素

export default class VNode{
	constructor (tag,data,children,text,elm...){
		this.tag = tag
		...
	}
}
1
2
3
4
5
6

🔥简单地说,vnode 可以理解成节点描述对象,它描述了怎样去创建真实的 DOM 节点。

# VNode 的作用

🔥将上一次渲染视图时创建的 vnode 缓存起来,重新渲染时进行比对

# VNode 类型

  • 注释节点<!-- 注释节点 -->
  • 文本节点
  • 元素节点
    • tag:p,ul,li,div...
    • data:class,id...
  • 组件节点
    • componentOptions
    • componentInstance
  • 函数式组件
    • functionalContext
    • functionalOptions
  • 克隆节点:优化静态节点和插槽节点
    • 将现有节点全部属性复制到新节点即可
    • isCloned = true

# ----------

# Patch 介绍

🔥🔥创建 ➕ 删除 ➕ 更新节点

🔁运行流程:

  • oldVnode 是否存在 ➡️ ❎ 不存在则使用 vnode 创建节点插入视图
    • ✅ 存在 检查 OldVnode 和 Vnode 是否为同一节点 ➡️ ✅ 是则使用 patchVnode 进行更新
      • ❎ 不是则使用 vnode 创建真实节点插入视图 并删除旧节点

# 创建节点

⚠️ 只有三种节点会被插入 DOM 中:元素节点、注释节点、文本节点

判断顺序:

  • 是否为元素节点
    • 依据vnode.tag
    • 🔥如何创建真实元素节点:
      • 1️⃣ 调用createElement创建元素节点
      • 2️⃣ 循环 vnode 的 children 属性
      • 3️⃣ 调用appendChild插入父节点
  • 是否为注释节点
    • 依据vnode.isComment
    • 调用 createComment
  • 文本节点
    • 调用 createTextNode

# 删除节点

逻辑较为简单

# 更新节点

更新并不是暴力的用新节点覆盖旧节点,而是通过比对找出不一样的地方进行更新。

# 静态节点

⚠️ 更新节点时首先判断是否为静态节点,如果是则不需要进行操作。

例如<p>12345</p>没有绑定状态的节点即为静态节点,一旦被渲染后不会发生变化。

更新节点算法将跳过类似节点。

# 🔥 具体流程图

upadte vnode

# 更新子节点【详解】

# 更新策略

  • 创建子节点:插入到所有未处理之前
  • 更新子节点
  • 移动子节点:Node.insertBefore()插入所有未处理之前
  • 删除子节点:循环完 new 后 old 中还存在未处理的节点即可删除

# 优化策略

针对位置不变或者说位置可以预测的节点,我们不需要循环来查找,因为我们有一个更快捷的查找方式。

⚠️4 种快捷查找方式

  • 新前 🆚 旧前 : 更新
  • 新后 🆚 旧后 : 更新
  • 新后 🆚 旧前 : 更新+移动
  • 新前 🆚 旧后 : 更新+移动

# 🔥 哪些节点是未处理过的

设置四个变量oldStartIdx oldEndIdx newStartIdx newEndIdx每处理一个节点则向相应方向移动一个位置。

结束循环条件:old 或 new 中任意一个的 idx 开始位置大于结束位置即停止

  • old 提前结束:new 中剩下的节点都为需要新增的节点
  • new 提前结束:old 中剩下的都是舍弃的节点
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
  //循环体
}
1
2
3

# ⭕️ 第三篇 模板编译原理

# 模板编译介绍

模板编译的主要目标是生成渲染函数,每次执行渲染函数就会使用当前最新的状态生成一份新 node

🔥逻辑上分为三步:

  • 将模板解析为 AST(解析器)
  • 遍历 AST 标记静态节点(优化器)
  • 使用 AST 生成渲染函数(代码生成器)

# 解析器

在解析器内部,分成了很多小解析器,其中包括过滤器解析器、文本解析器和 HTML 解析器,然后通过一条主线将这些解析器组装在一起

  • 过滤器解析器
  • 文本解析器 hello { {name}}
  • HTML 解析器

如何解析详解见下文

# 优化器

优化器的目标是遍历 AST,检测出所有静态子树并给其打标记

# 代码生成器

模板编译的最后一步,将 AST 转换成渲染函数中的内容,该内容称为“代码字符串”

# ----------

# 解析器内部运行原理

# 钩子函数

parseHTML(template, {
  start(tag, attrs, unary) {
    // 标签开始 标签名 属性 是否自闭合
  },
  end() {
    // 标签结束
  },
  chars(text) {
    // 解析文本
  },
  comment(text) {
    // 解析注释
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 栈管理 AST 层级关系

🔥 一个 AST 节点具有父节点和子节点,我们维护一个栈用来记录 DOM 的深度,每次触发钩子函数 start,把当前节点推入栈,触发 end 时弹出一个节点。

# HTML 解析器