返回顶部
首页 > 资讯 > 前端开发 > VUE >vue parseHTML 函数源码解析AST基本形成
  • 773
分享到

vue parseHTML 函数源码解析AST基本形成

2024-04-02 19:04:59 773人浏览 独家记忆
摘要

目录AST(抽象语法树)?子节点Vue中是如何把html(template)字符串编译解析成AST解析html代码重新改造接着解析 html (template)字符串解析divAS

目录
  • AST(抽象语法树)?
    • 子节点
  • Vue中是如何把html(template)字符串编译解析成AST
    • 解析html
    • 代码重新改造
    • 接着解析 html (template)字符串
    • 解析div

AST(抽象语法树)?

vue parseHTML函数解析器遇到结束标签

在上篇文章中我们已经把整个词法分析的解析过程分析完毕了。

例如有html(template)字符串:

<div id="app">
  <p>{{ message }}</p>
</div>

产出如下:

{
attrs: [" id="app"", "id", "=", "app", undefined, undefined]
end: 14
start: 0
tagName: "div"
unarySlash: ""
}
{
attrs: []
end: 21
start: 18
tagName: "p"
unarySlash: ""
}

看到这不禁就有疑问? 这难道就是AST(抽象语法树)??

非常明确的告诉你答案:No 这不是我们想要的AST,parse 阶段最终生成的这棵树应该是与如上html(template)字符串的结构一一对应的:

├── div
│   ├── p
│   │   ├── 文本

如果每一个节点我们都用一个 javascript 对象来表示的话,那么 div 标签可以表示为如下对象:

{
  type: 1,
  tag: "div"
}

子节点

由于每个节点都存在一个父节点和若干子节点,所以我们为如上对象添加两个属性:parent 和 children ,分别用来表示当前节点的父节点和它所包含的子节点:

{
  type: 1,
  tag:"div",
  parent: null,
  children: []
}

同时每个元素节点还可能包含很多属性 (attributes),所以我们可以为每个节点添加attrsList属性,用来存储当前节点所拥有的属性:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

按照以上思路去描述之前定义的 html 字符串,那么这棵抽象语法树应该长成如下这个样子:

{
  type: 1,
  tag: "div",
  parent: null,
  attrsList: [],
  children: [{
      type: 1,
      tag: "p",
      parent: div,
      attrsList: [],
      children:[
         {
          type: 3,
          tag:"",
          parent: p,
          attrsList: [],
          text:"{{ message }}"
         }
       ]
  }],
}

实际上构建抽象语法树的工作就是创建一个类似如上所示的一个能够描述节点关系的对象树,节点与节点之间通过 parent 和 children 建立联系,每个节点的 type 属性用来标识该节点的类别,比如 type 为 1 代表该节点为元素节点,type 为 3 代表该节点为文本节点。

这里可参考nodeType:https://www.w3school.com.cn/jsref/prop_node_nodetype.asp

回顾我们所学的 parseHTML 函数可以看出,他只是在生成 AST 中的一个重要环节并不是全部。 那在Vue中是如何把html(template)字符串编译解析成AST的呢?

Vue中是如何把html(template)字符串编译解析成AST

源码中:

function parse (html) {
  var root;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      // 省略...
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

可以看到Vue在进行模板编译词法分析阶段调用了parse函数,parse函数返回root,其中root 所代表的就是整个模板解析过后的 AST,这中间还有两个非常重要的钩子函数,之前我们没有讲到的,options.start 、options.end。

接下来重点就来看看他们做了什么。

解析html

假设解析的html字符串如下:

<div></div>

这是一个没有任何子节点的div 标签。如果要解析它,我们来简单写下代码。

function parse (html) {
  var root;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root) root = element
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

如上: 在start 钩子函数中首先定义了 element 变量,它就是元素节点的描述对象,接着判断root 是否存在,如果不存在则直接将 element 赋值给 root 。当解析这段 html 字符串时首先会遇到 div 元素的开始标签,此时 start 钩子函数将被调用,最终 root 变量将被设置为:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

html 字符串复杂度升级: 比之前的 div 标签多了一个子节点,span 标签。

<div>
  <span></span>
</div>

代码重新改造

此时需要把代码重新改造。

function parse (html) {
  var root;
  var currentParent;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root){
        root = element;
       }else if(currentParent){
        currentParent.children.push(element)
      }
      if (!unary) currentParent = element
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

我们知道当解析如上 html 字符串时首先会遇到 div 元素的开始标签,此时 start 钩子函数被调用,root变量被设置为:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

还没完可以看到在 start 钩子函数的末尾有一个 if 条件语句,当一个元素为非一元标签时,会设置 currentParent 为该元素的描述对象,所以此时currentParent也是:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

接着解析 html (template)字符串

接着解析 html (template)字符串,会遇到 span 元素的开始标签,此时root已经存在,currentParent 也存在,所以会将 span 元素的描述对象添加到 currentParent 的 children 数组中作为子节点,所以最终生成的 root 描述对象为:

{
  type: 1,
  tag:"div",
  parent: null,
  attrsList: []
  children: [{
     type: 1,
     tag:"span",
     parent: div,
     attrsList: [],
     children:[]
  }], 
}

到目前为止好像没有问题,但是当html(template)字符串复杂度在升级,问题就体现出来了。

<div>
 <span></span>
 <p></p>
</div>

在之前的基础上 div 元素的子节点多了一个 p 标签,到解析span标签的逻辑都是一样的,但是解析 p 标签时候就有问题了。

注意这个代码:

if (!unary) currentParent = element

在解析 p 元素的开始标签时,由于 currentParent 变量引用的是 span 元素的描述对象,所以p 元素的描述对象将被添加到 span 元素描述对象的 children 数组中,被误认为是 span 元素的子节点。而事实上 p 标签是 div 元素的子节点,这就是问题所在。

为了解决这个问题,就需要我们额外设计一个回退的操作,这个回退的操作就在end钩子函数里面实现。

解析div

这是一个什么思路呢?举个例子在解析div 的开始标签时:

stack = [{tag:"div"...}]

在解析span 的开始标签时:

stack = [{tag:"div"...},{tag:"span"...}]

在解析span 的结束标签时:

stack = [{tag:"div"...}]

在解析p 的开始标签时:

stack = [{tag:"div"...},{tag:"p"...}]

在解析p 的标签时:

这样的一个回退操作看懂了吗? 这就能保证在解析p开始标签的时候,stack中存储的是p标签父级元素的描述对象。

接下来继续改造我们的代码。

function parse (html) {
  var root;
  var currentParent;
  var stack = [];  
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root){
        root = element;
       }else if(currentParent){
        currentParent.children.push(element)
      }
      if (!unary){
          currentParent = element;
          stack.push(currentParent);
       } 
    },
    end: function (){
      stack.pop();
      currentParent = stack[stack.length - 1]
    }
  }) 
  return root
}

通过上述代码,每当遇到一个非一元标签的结束标签时,都会回退 currentParent 变量的值为之前的值,这样我们就修正了当前正在解析的元素的父级元素。

以上就是根据 parseHTML 函数生成 AST 的基本方式,但实际上还不完美在Vue中还会去处理一元标签,文本节点和注释节点等等。

接下来你是否迫不及待要进入到源码部分去看看了? 但Vue这块代码稍微复杂点,我们还需要有一些前期的预备知识。

parseHTML 函数源码解析 AST 预备知识

以上就是vue parseHTML 函数源码解析AST基本形成的详细内容,更多关于vue parseHTML 函数AST的资料请关注编程网其它相关文章!

--结束END--

本文标题: vue parseHTML 函数源码解析AST基本形成

本文链接: https://lsjlt.com/news/34288.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue parseHTML 函数源码解析AST基本形成
    目录AST(抽象语法树)?子节点Vue中是如何把html(template)字符串编译解析成AST解析html代码重新改造接着解析 html (template)字符串解析divAS...
    99+
    2024-04-02
  • vue parseHTML函数源码分析AST
    这篇“vue parseHTML函数源码分析AST”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue ...
    99+
    2023-07-02
  • vue parseHTML 函数源码解析
    目录正文函数开头定义的一些常量和变量while 循环textEnd ===0parseStartTag 函数解析开始标签总结:正文 接上篇: Vue编译器源码分析AST 抽象语法树 ...
    99+
    2024-04-02
  • vue parseHTML函数源码分析
    本文小编为大家详细介绍“vue parseHTML函数源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue parseHTML函数源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-07-02
  • vue parseHTML函数源码解析start钩子函数
    目录正文platformGetTagNamespace 源码 isForbiddenTag 函数addIfCondition是什么processIfConditions 源...
    99+
    2024-04-02
  • vue parseHTML函数源码分析start钩子函数
    这篇文章主要讲解了“vue parseHTML函数源码分析start钩子函数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue parseHTML函数源码分析start...
    99+
    2023-07-02
  • vue parseHTML源码解析hars end comment钩子函数
    目录引言chars源码:parseTextend 源码closeElement 源码comment 注释节点描述对象引言 接上文  parseHTML 函数源码解析&nbs...
    99+
    2024-04-02
  • vue parseHTML 函数拿到返回值后的处理源码解析
    目录引言parseStartTag函数返回值handleStartTag源码tagName 及unarySlash调用parser钩子函数引言 继上篇文章: parseHTML 函数...
    99+
    2024-04-02
  • vueparseHTML函数源码解析AST预备知识
    目录正文createASTElement函数解析指令所用正则parse 函数中的变量正文 接上章节:parseHTML 函数源码解析AST 基本形成 在正式扎进Vue parse源码...
    99+
    2024-04-02
  • Vue中AST源码解析的示例分析
    这篇文章主要介绍Vue中AST源码解析的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!从这个函数开始的:// Line-3924  Vue.prototy...
    99+
    2024-04-02
  • vue parseHTML函数解析器遇到结束标签
    目录引言match函数匹配正则endTag关键 parseEndTag 函数代码总结parseEndTag 函数作用handleStartTag函数后续最后更新 stack 栈以及 ...
    99+
    2024-04-02
  • vue parseHTML函数解析器遇到结束标签会怎么样
    今天小编给大家分享一下vue parseHTML函数解析器遇到结束标签会怎么样的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了...
    99+
    2023-07-02
  • OpenCV 基本图形绘制函数详解
    用于绘制直线的line函数; 用于绘制椭圆的ellipse函数; 用于绘制矩形的rectangle函数; 用于绘制圆的circle函数; 用...
    99+
    2024-04-02
  • RxJava 触发流基本原理源码解析
    目录正文触发流小结总结正文 本节,我们从Rxjava使用代码入手,去结合自己已有的知识体系,加查阅部分源码验证的方式,来一起探索一下Rxjava实现的基本原理。 为了本文原理分析环...
    99+
    2022-12-30
    RxJava 触发流原理 RxJava 触发流
  • Vue3源码解析watch函数实例
    目录引言一、watch参数类型1. 选项options2. 回调cb3. 数据源source二、watch函数三、watch的核心:doWatch 函数引言 想起上次面试,问了个古老...
    99+
    2022-11-13
    Vue3 watch函数 Vue watch
  • Vue八大生命周期钩子函数源码分析
    本篇内容主要讲解“Vue八大生命周期钩子函数源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue八大生命周期钩子函数源码分析”吧!一.速识概念:我们把一个对象从生成(new)到被销毁(d...
    99+
    2023-07-05
  • Oracle中DECODE函数的基本用法解析
    Oracle中DECODE函数的基本用法解析 在Oracle数据库中,DECODE函数是一种非常常用的函数,用于实现类似于多层if-else语句的逻辑判断和数值替换。DECODE函数的...
    99+
    2024-03-07
    函数 oracle decode
  • useEffect 返回函数执行过程源码解析
    目录引言deletions处理当前节点的 deletions删除的 fiber 没有子节点:删除的 fiber 有子节点:向下遍历和向上遍历总结1. 遍历 deletions 数组:...
    99+
    2023-05-16
    useEffect 返回函数执行流程 useEffect 返回函数
  • 了解numpy函数的基本用法速成指南
    快速入门:numpy函数的基本用法 numpy是Python中一个强大的库,用于科学计算和数据分析。它提供了一个高效的多维数组对象ndarray,以及对该对象进行操作的函数库。numpy的函数可以让我们以更快的速度进行数值计算,...
    99+
    2024-01-26
    函数 Numpy 基本用法
  • Vue源码解析之数据响应系统的示例分析
    这篇文章主要介绍Vue源码解析之数据响应系统的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!数据双向绑定的思路1. 对象先来看元素是对象的情况。假设我们有一个对象和一个监测方...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作