Vue3 编译原理知识总结

之前读过《Vue3设计与实现》时渲染编译相关的篇章让我印象特别深刻,填补了对编程渲染和编译相关的知识盲区。今天发现相关知识已经开始忘记了,随写此博客作为记录,方便后面随时查阅。

强烈建议所有前端开发者阅读此书《Vue3设计与实现》,补充编译和渲染相关知识。

第一篇 框架设计

1、命名式与声明式

1、命名式关注过程,声明式关注结果。
2、声明式代码的性能小于等于命令式代码的性能。

2、框架种类

1、纯运行时框架
    用户手写浏览器可以直接执行代码;
    缺点:开发效率低;

2、纯编译时框架
    编译后浏览器可以直接执行代码;
    缺点:页面更新效率可能差;

3、运行时+编译时

3、框架设计的核心要素

1、提升开发体验

2、控制框架体积

3、支持 Tree-Shaking
Tree-Shaking 指的就是消除那些永远不会被执行的代码。想要实现 Tree-Shaking,必须满足一个条件,即模块必须是 ESM(ES Module),因为 Tree-Shaking 依赖 ESM 的静态结构。

4、良好的 TypeScript 类型支持

4、Vue.js 3 的设计思路

1)一个声明式的 UI 框架

1、使用 JavaScript 对象来描述 UI 的方式,其实就是所谓的虚拟 DOM。

2、h 函数就是一个辅助创建虚拟 DOM 的工具函数,仅此而已。Vue.js 会根据组件的 render 函数的返回值拿到虚拟 DOM,然后就可以把组件的内容渲染出来了。

3、组件就是一组 DOM 元素的封装,这组 DOM 元素就是组件要渲染的内容。

2)模板的工作原理

无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 DOM 渲染为真实 DOM,这就是模板的工作原理,也是 Vue.js 渲染页面的流程。

3)渲染器的作用

渲染器的作用是,把虚拟 DOM 对象渲染为真实 DOM 元素。它的工作原理是,递归地遍历虚拟 DOM 对象,并调用原生 DOM API 来完成真实 DOM 的创建。渲染器的精髓在于后续的更新,它会通过 Diff 算法找出变更点,并且只会更新需要更新的内容。

第三篇渲染器

1、响应系统和渲染器的关系

利用响应系统的能力,自动调用渲染器完成页面的渲染和更新。

2、渲染器的作用

渲染器的作用是把虚拟 DOM 渲染为特定平台上的真实 DOM元素。渲染器会执行挂载和打补丁操作,对于新的元素,渲染器会将它挂载到容器内;对于新旧 vnode 都存在的情况,渲染器则会执行打补丁操作,即对比新旧 vnode,只更新变化的内容。

3、渲染器的关键

都在更新节点的阶段。

4、渲染器基本概念

1)renderer
名词,渲染器;

2)render
动词,渲染;

3)虚拟DOM

虚拟 DOM 通常用英文 virtual DOM 来表达,有时会简写成 vdom。虚拟 DOM 和真实 DOM 的结构一样,都是由一个个节点组成的树型结构。所以,我们经常能听到“虚拟节点”这样的词,即 virtual node,有时会简写成 vnode。虚拟 DOM 是树型结构,这棵树中的任何一个 vnode 节点都可以是一棵子树,因此 vnode 和 vdom 有时可以替换使用。为了避免造成困惑,在本书中将统一使用 vnode。**

4)挂载

渲染器把虚拟 DOM 节点渲染为真实 DOM 节点的过程叫作挂载,通常用英文 mount 来表达。

5)#app

渲染器通常需要接收一个挂载点作为参数,用来指定具体的挂载位置。这里的“挂载点”其实就是一个 DOM 元素,渲染器会把该 DOM 元素作为容器元素,并把内容渲染到其中。我们通常用英文 container 来表达容器。

第四篇 组件化

1、原理

1)渲染组件

用户的角度来看,一个有状态组件就是一个选项对象。

从渲染器的内部实现来看,一个组件则是一个特殊类型的虚拟 DOM 节点。

2)组件本质

组件实例本质上就是一个状态集合(或一个对象),它维护着组件运行过程中的所有信息,例如注册到组件的生命周期函数、组件渲染的子树(subTree)、组件是否已经被挂载、组件自身的状态(data),等等。

3)setup 函数的作用与实现

组件的整个生命周期中,setup 函数只会在被挂载时执行一次,它的返回值可以有两种情况。

(1) 返回一个函数,该函数将作为组件的 render 函数
```
01 const Comp = {
02   setup() {
03     // setup 函数可以返回一个函数,该函数将作为组件的渲染函数
04     return () => {
05       return { type: 'div', children: 'hello' }
06     }
07   }
08 }
```

(2) 返回一个对象,该对象中包含的数据将暴露给模板使用
```
01 const Comp = {
02   setup() {
03     const count = ref(0)
04     // 返回一个对象,对象中的数据会暴露到渲染函数中
05     return {
06       count
07     }
08   },
09   render() {
10     // 通过 this 可以访问 setup 暴露出来的响应式数据
11     return { type: 'div', children: `count is: ${this.count}` }
12   }
13 }
```

第五篇 编译器

1、解析器

编译器其是一段程序,它将“语言 A”翻译成“语言 B”。其中,语言 A 通常叫作源代码(source code),语言 B 通常叫作目标代码(object code 或 target code)。编译器将源代码翻译为目标代码的过程叫作编译(compile)。完整的编译过程通常包含词法分析、语法分析、语义分析、中间代码生成、优化、目标代码生成等步骤。

1)整个编译过程

源代码-词法分析-语法分析-语义分析-中间代码生成-优化-目标代码生成-目标代码

2)编译前端

编译前端包含词法分析、语法分析和语义分析,它通常与目标平台无关,仅负责分析源代码。

3)编译后端

编译后端则通常与目标平台有关,编译后端涉及中间代码生成和优化以及目标代码生成。但是,编译后端并不一定会包含中间代码生成和优化这两个环节,这取决于具体的场景和实现。中间代码生成和优化这两个环节有时也叫“中端”。

4)AST

(1)、AST 是 abstract syntax tree 的首字母缩写,抽象语法树。所谓模板 AST,其实就是用来描述模板的抽象语法树。

(2)、AST 是一个具有层级结构的对象。模板 AST 具有与模板同构的嵌套结构。每一棵 AST 都有一个逻辑上的根节点,其类型为 Root。模板中真正的根节点则作为 Root 节点的 children 存在。

(3)、其他

1、不同类型的节点是通过节点的 type 属性进行区分的。例如标签节点的 type 值为 ‘Element’。

2、标签节点的子节点存储在其 children 数组中。

3、标签节点的属性节点和指令节点会存储在 props 数组中。

4、不同类型的节点会使用不同的对象属性进行描述。例如指令节点拥有 name 属性,用来表达指令的名称,而表达式节点拥有 content 属性,用来描述表达式的内容。

示例

01  const ast = {
02   // 逻辑根节点
03   type: 'Root',
04   children: [
05     // div 标签节点
06     {
07       type: 'Element',
08       tag: 'div',
09       children: [
10         // h1 标签节点
11         {
12           type: 'Element',
13           tag: 'h1',
14           props: [
15             // v-if 指令节点
16             {
17               type: 'Directive', // 类型为 Directive 代表指令
18               name: 'if'// 指令名称为 if,不带有前缀 v-
19               exp: {
20                 // 表达式节点
21                 type: 'Expression',
22                 content: 'ok'
23               }
24             }
25           ]
26         }
27       ]
28     }
29   ]
30 }

5)编译优化

编译优化指的是通过编译的手段提取关键信息,并以此指导生成最优代码的过程。具体来说,Vue.js 3 的编译器会充分分析模板,提取关键信息并将其附着到对应的虚拟节点上。在运行时阶段,渲染器通过这些关键信息执行“快捷路径”,从而提升性能。

编译优化的核心在于区分动态节点与静态节点。Vue.js 3 会为动态节点打上补丁标志,即 patchFlag。同时,Vue.js 3 还提出了 Block 的概念,一个 Block 本质上也是一个虚拟节点,但与普通虚拟节点相比,会多出一个 dynamicChildren 数组。

1、静态提升

能够减少更新时创建虚拟 DOM 带来的性能开销和内存占用。

2、预字符串化

在静态提升的基础上,对静态节点进行字符串化。这样做能够减少创建虚拟节点产生的性能开销以及内存占用。

3、缓存内联事件处理函数

避免造成不必要的组件更新。

4、v-once 指令

缓存全部或部分虚拟节点,能够避免组件更新时重新创建虚拟 DOM 带来的性能开销,也可以避免无用的 Diff 操作。

6)其他

对于 Vue.js 模板编译器来说,源代码就是组件的模板,而目标代码是能够在浏览器平台上运行的 JavaScript 代码,或其他拥有 JavaScript 运行时的平台代码。

Vue.js 模板编译器的目标代码是渲染函数。

Vue.js 模板编译器首先对模板进行词法分析和语法分析,得到模板 AST。接着,将模板 AST 转换(transform)成 JavaScript AST。最后,根据 JavaScript AST 生成 JavaScript 代码,即渲染函数代码。

Github:Vue3设计与实现

原文链接:https://juejin.cn/post/7232973034067820603 作者:SoaringHeart

(0)
上一篇 2023年5月15日 上午11:10
下一篇 2023年5月16日 上午10:01

相关推荐

发表回复

登录后才能评论