【Vue2.0源码学习】模板编译篇-模板解析阶段(整体运行流程)

简介: 【Vue2.0源码学习】模板编译篇-模板解析阶段(整体运行流程)

1. 前言

在前几篇文章中,我们介绍了Vue中的虚拟DOM以及虚拟DOMpatch(DOM-Diff)过程,而虚拟DOM存在的必要条件是得先有VNode,那么VNode又是从哪儿来的呢?这就是接下来几篇文章要说的模板编译。你可以这么理解:把用户写的模板进行编译,就会产生VNode

2. 什么是模板编译

我们知道,在日常开发中,我们把写在<template></template>标签中的类似于原生HTML的内容称之为模板。这时你可能会问了,为什么说是“类似于原生HTML的内容”而不是“就是HTML的内容”?因为我们在开发中,在<template></template>标签中除了写一些原生HTML的标签,我们还会写一些变量插值,如,或者写一些Vue指令,如v-onv-if等。而这些东西都是在原生HTML语法中不存在的,不被接受的。但是事实上我们确实这么写了,也被正确识别了,页面也正常显示了,这又是为什么呢?

这就归功于Vue的模板编译了,Vue会把用户在<template></template>标签中写的类似于原生HTML的内容进行编译,把原生HTML的内容找出来,再把非原生HTML找出来,经过一系列的逻辑处理生成渲染函数,也就是render函数,而render函数会将模板内容生成对应的VNode,而VNode再经过前几篇文章介绍的patch过程从而得到将要渲染的视图中的VNode,最后根据VNode创建真实的DOM节点并插入到视图中, 最终完成视图的渲染更新。

而把用户在<template></template>标签中写的类似于原生HTML的内容进行编译,把原生HTML的内容找出来,再把非原生HTML找出来,经过一系列的逻辑处理生成渲染函数,也就是render函数的这一段过程称之为模板编译过程。

3. 整体渲染流程

所谓渲染流程,就是把用户写的类似于原生HTML的模板经过一系列处理最终反应到视图中称之为整个渲染流程。这个流程在上文中其实已经说到了,下面我们以流程图的形式宏观的了解一下,流程图如下:

从图中我们也可以看到,模板编译过程就是把用户写的模板经过一系列处理最终生成render函数的过程。

4. 模板编译内部流程

那么模板编译内部是怎么把用户写的模板经过处理最终生成render函数的呢?这内部的过程是怎样的呢?

4.1 抽象语法树AST

我们知道,用户在<template></template>标签中写的模板对Vue来说就是一堆字符串,那么如何解析这一堆字符串并且从中提取出元素的标签、属性、变量插值等有效信息呢?这就需要借助一个叫做抽象语法树的东西。

所谓抽象语法树,在计算机科学中,抽象语法树AbstractSyntaxTree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。——来自百度百科

我就知道,这段话贴出来也是白贴,因为看了也看不懂,哈哈。那么我们就以最直观的例子来理解什么是抽象语法树。请看下图:

从图中我们可以看到,一个简单的HTML标签的代码被转换成了一个JS对象,而这个对象中的属性代表了这个标签中一些关键有效信息。如图中标识。 有兴趣的同学可以在这个网站在线转换试试:https://astexplorer.net/

4.2 具体流程

将一堆字符串模板解析成抽象语法树AST后,我们就可以对其进行各种操作处理了,处理完后用处理后的AST来生成render函数。其具体流程可大致分为三个阶段:

  1. 模板解析阶段:将一堆模板字符串用正则等方式解析成抽象语法树AST
  2. 优化阶段:遍历AST,找出其中的静态节点,并打上标记;
  3. 代码生成阶段:将AST转换成渲染函数;

这三个阶段在源码中分别对应三个模块,下面给出三个模块的源代码在源码中的路径:

  1. 模板解析阶段——解析器——源码路径:src/compiler/parser/index.js;
  2. 优化阶段——优化器——源码路径:src/compiler/optimizer.js;
  3. 代码生成阶段——代码生成器——源码路径:src/compiler/codegen/index.js; 其对应的源码如下:
// 源码位置: /src/complier/index.js
export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  // 模板解析阶段:用正则等方式解析 template 模板中的指令、class、style等数据,形成AST
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    // 优化阶段:遍历AST,找出其中的静态节点,并打上标记;
    optimize(ast, options)
  }
  // 代码生成阶段:将AST转换成渲染函数;
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

可以看到 baseCompile 的代码非常的简短主要核心代码。

  • const ast =parse(template.trim(), options):parse 会用正则等方式解析 template 模板中的指令、classstyle等数据,形成AST
  • optimize(ast, options): optimize 的主要作用是标记静态节点,这是 Vue 在编译过程中的一处优化,挡在进行patch 的过程中, DOM-Diff 算法会直接跳过静态节点,从而减少了比较的过程,优化了 patch 的性能。
  • const code =generate(ast, options): 将 AST 转化成 render函数字符串的过程,得到结果是 render函数 的字符串以及 staticRenderFns 字符串。

最终 baseCompile 的返回值

{
  ast: ast,
  render: code.render,
  staticRenderFns: code.staticRenderFns
 }

最终返回了抽象语法树( ast ),渲染函数( render ),静态渲染函数( staticRenderFns ),且render 的值为code.renderstaticRenderFns 的值为code.staticRenderFns,也就是说通过 generate处理 ast之后得到的返回值 code 是一个对象。

下面再给出模板编译内部具体流程图,便于理解。流程图如下:

5. 小总结

本篇文章首先引出了为什么会有模板编译,因为有了模板编译,才有了虚拟DOM,才有了后续的视图更新。接着介绍了什么是模板编译,以及介绍了把用户所写的模板经过层层处理直到最终渲染的视图中这个整体的渲染流程;最后介绍了模板编译过程中所需要使用的抽象语法树的概念以及分析了模板编译的具体实施流程,其流程大致分为三个阶段,分别是模板解析阶段、优化阶段和代码生成阶段。那么接下来的几篇文章将会把这三个阶段逐一进行分析介绍。

6. 整体流程

前言中我们说了,在模板解析阶段主要做的工作是把用户在<template></template>标签内写的模板使用正则等方式解析成抽象语法树(AST)。而这一阶段在源码中对应解析器(parser)模块。

解析器,顾名思义,就是把用户所写的模板根据一定的解析规则解析出有效的信息,最后用这些信息形成AST。我们知道在<template></template>模板内,除了有常规的HTML标签外,用户还会一些文本信息以及在文本信息中包含过滤器。而这些不同的内容在解析起来肯定需要不同的解析规则,所以解析器不可能只有一个,它应该除了有解析常规HTML的HTML解析器,还应该有解析文本的文本解析器以及解析文本中如果包含过滤器的过滤器解析器。

另外,文本信息和标签属性信息却又是存在于HTML标签之内的,所以在解析整个模板的时候它的流程应该是这样子的:HTML解析器是主线,先用HTML解析器进行解析整个模板,在解析过程中如果碰到文本内容,那就调用文本解析器来解析文本,如果碰到文本中包含过滤器那就调用过滤器解析器来解析。如下图所示:

7. 回到源码

解析器的源码位于/src/complier/parser文件夹下,其主线代码如下:

// 代码位置:/src/complier/parser/index.js
/**
 * Convert HTML string to AST.
 */
export function parse(template, options) {
   // ...
  parseHTML(template, {
    warn,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
    shouldKeepComment: options.comments,
    start (tag, attrs, unary) {
    },
    end () {
    },
    chars (text: string) {
    },
    comment (text: string) {
    }
  })
  return root
}

从上面代码中可以看到,parse 函数就是解析器的主函数,在parse 函数内调用了parseHTML 函数对模板字符串进行解析,在parseHTML 函数解析模板字符串的过程中,如果遇到文本信息,就会调用文本解析器parseText函数进行文本解析;如果遇到文本中包含过滤器,就会调用过滤器解析器parseFilters函数进行解析。

8. 总结

本篇文章主要梳理了模板解析的整体运行流程,模板解析其实就是根据被解析内容的特点使用正则等方式将有效信息解析提取出来,根据解析内容的不同分为HTML解析器,文本解析器和过滤器解析器。而文本信息与过滤器信息又存在于HTML标签中,所以在解析器主线函数parse中先调用HTML解析器parseHTML 函数对模板字符串进行解析,如果在解析过程中遇到文本或过滤器信息则再调用相应的解析器进行解析,最终完成对整个模板字符串的解析。

了解了模板解析阶段的整体运行流程后,接下来,我们就对流程中所涉及到的三种解析器分别深入分析,逐个击破。

目录
相关文章
|
4月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
645 0
|
5月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
621 77
|
4月前
|
JavaScript 前端开发 UED
Vue 手风琴实现的三种常用方式及长尾关键词解析
手风琴效果是Vue开发中常见的交互组件,可节省页面空间、提升用户体验。本文介绍三种实现方式:1) 原生Vue结合数据绑定与CSS动画;2) 使用Element UI等组件库快速构建;3) 自定义指令操作DOM实现独特效果。每种方式适用于不同场景,可根据项目需求选择。示例包括产品特性页、后台菜单及FAQ展示,灵活满足多样需求。附代码示例与资源链接,助你高效实现手风琴功能。
200 10
|
4月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
228 1
|
7月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
692 29
|
7月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
208 4
|
7月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
7月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
7月前
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。

推荐镜像

更多
  • DNS