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

2023-11-02

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 函数对模板字符串进行解析,如果在解析过程中遇到文本或过滤器信息则再调用相应的解析器进行解析,最终完成对整个模板字符串的解析。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 东方卫视演得泰坦机器人_报名丨清北上交都认可的“机器人技术等级考试”开启报名!...

    点击上方 蓝字 关注我们吧 众所周知 学习少儿编程有利于提高孩子的综合能力 助力升学 但是 关于编程有那么多的比赛和证书 家长该怎么选择 有没有什么考试像钢琴考级一样 通过不同级别认证评估孩子的学习成果呢 答案是 有的 今天 我们要介绍的就
  • 人工智能—问题规约法(Reduction)[一]

    问题规约表示 问题规约 Problem reduction 是另一种基于状态空间的问题描述与求解方法 已知问题的描述 通过一系列变换把此问题最终变成另一个本原问题 事实 定理 集合 这些本原问题的解可以直接得到 从而解决了初始问题 问题规约
  • java中 io和nio的区别

    一 概念 1 基本概念 1 i o inputstream outputstream 整个Java IO体系都是基于字符流 InputStream OutputStream 和 字节流 Reader Writer 作为基类 根据不同的数据载
  • 通俗易懂的 OpenGL ES 3.0(一)入门必备知识!!

    前言 opengl的入门是个难点 因为在绘制出图形之前 你必须要学习一大堆的概念 以及部分图形知识 以至于在代码中不迷路 v 接下来就通俗的讲解下部分概念知识 希望大家取其精华去其糟粕 温馨提示 blog阅读体验会好点 https lais
  • C++ STL中哈希表 hash_map从头到尾详细介绍

    目录 0 为什么需要hash map 用过map吧 map提供一个很常用的功能 那就是提供key value的存储和查找功能 例如 我要记录一个人名和相应的存储 而且随时增加 要快速查找和修改 岳不群 华山派掌门人 人称君子剑 张三丰 武当
  • 前端艺术之毛玻璃-倾斜-日历

    前端艺术之毛玻璃 倾斜 日历 描述 项目 效果 index html index css 描述 项目 描述 开发语言 HTML JavaScript CSS 库 dyCalendarJS vanilla tilt Edge 108 0 14
  • 反反调试:app 检测 data/local/tmp 目录绕过手段

    最近在逆向某app时遇到一个非常恶心的反调试手段 只要是maps和fd中存在 data local tmp 甚至只有tmp的字段 app就给kill掉 因为这个目录对于安卓逆向工作来说 是一个比较敏感的目录 hluda server和fri
  • 【DL】第6章 问题匹配

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • SpringBoot打成可执行的jar包

    当你使用springBoot进行打包的时候 这篇经验会帮助到你的 操作 1 spring boot maven plugin 插件 首先要明白一个spring boot maven plugin 插件的作用 pom文件中添加了 org sp
  • [管理与领导-67]:IT基层管理者 - 辅助技能 - 4- 职业发展规划 - 评估你与公司的八字是否相合

    目录 前言 一 概述 二 八字相合的步骤 2 1 企业文化是否相合 2 2 1 企业文化对职业选择的意义 2 2 2 个人与企业三观不合的结果 2 2 3 什么样的企业文化的公司不能加入 2 2 公司的发展前景 2 3 公司所处行业发展 2
  • ChatGPT对我们的影响-ChatGPT能给我们带来什么

    ChatGPT日常应用 ChatGPT是一种应用广泛的自然语言处理算法 其可以应用于多种日常场景 以下是一些ChatGPT的日常应用 聊天机器人 ChatGPT可用于构建聊天机器人 通过与用户进行自然语言交互来提供个性化的服务和支持 新闻稿
  • 基于Spark 的电影推荐系统

    来源 我是码农 转载请保留出处和链接 本文链接 http www 54manong com id 1215 基于大数据的电影推荐系统主要分为两部分 基于历史数据的离线处理和基于实时流的实时处理 离线处理是基于历史数据 实时处理是结合历史数据
  • 翻译:miceforest:使用Python中的随机森林进行快速插补

    原文 miceforest Fast Imputation with Random Forests in Python miceforest 包实现随机森林的链式方程式 MICE 多重插补 具有快速 内存利用率高的特征 无需太多设置即可插入
  • kettle中判断输入的数据是不是 偶然错误

    参照表一个常见的用途就是做数据的查询和检验 提供一个输入字段 如果输入字段里的值没有匹配上 就给对应的数据行做一个错误标志 下面使用城市和邮政编码查询做个例子 演示如何使用计算器步骤和查询步骤来判断地址和邮政编码是否匹配 完整的转换如下图
  • iOS AVPlayer使用总结

    目录 1 AVPlayer的创建 2 AVPlayer的配置 AVPlayerLayer 3 AVPlayerItem 加载视频资源 4 播放控制 5 监听播放状态 1 AVPlayer的创建 AVPlayer player AVPlaye
  • [Anaconda] Jupyter Notebook中使用Python虚拟环境

    进入虚拟环境后安装ipykernel conda install ipykernel 运行ipykernel安装指令 python m ipykernel install name tf365 python m ipykernel inst
  • 【虚拟机】vmware虚拟机创建 centos7系统配置

    目录 前言 创建虚拟机 进入虚拟机 前言 vmware虚拟机网上可以下载 本节主要记录一下 如何创建虚拟机 并安装centos操作系统 然后运行虚拟机 创建虚拟机 打开VMware Workstation 新建虚拟机 我们选择典型 推荐 之
  • Error: Cannot find module 'chalk'

    问题描述 Vue文件打包 npm install npm run build 然后问题如下 然后网上搜索答案 有说删除掉node modules 再重新执行npm install 也有说执行npm install chalk的 都试了 结果
  • gtest中TEST和TEST_F使用的区别

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

    文章目录 1 前言 2 什么是模板编译 3 整体渲染流程 4 模板编译内部流程 4 1 抽象语法树AST 4 2 具体流程 5 小总结 6 整体流程 7 回到源码 8 总结 1 前言 在前几篇文章中 我们介绍了Vue中的虚拟DOM以及虚拟D