[前端] vue parseHTML函数源码解析 AST预备知识

1985 0
王子 2022-10-21 15:36:01 | 显示全部楼层 |阅读模式
目录

    正文createASTElement函数
      解析指令所用正则parse 函数中的变量



正文

接上章节:parseHTML 函数源码解析AST 基本形成
在正式扎进Vue parse源码之前,我们先了解下他周边的工具函数, 这能帮我们快速的去理解阅读。
还记得我们在上章节讲的element元素节点的描述对象吗?
  1. var element = {
  2.         type: 1,
  3.         tag: tag,
  4.         parent: null,
  5.         attrsList: attrs,
  6.         children: []
  7. }
复制代码
在源码中定义了一个createASTElement函数,用来创建一个元素的描述对象。

createASTElement函数
  1. function createASTElement(tag, attrs, parent) {
  2.                 return {
  3.                         type: 1,
  4.                         tag: tag,
  5.                         attrsList: attrs,
  6.                         attrsMap: makeAttrsMap(attrs),
  7.                         parent: parent,
  8.                         children: []
  9.                 }
  10.         }
复制代码
解析指令所用正则
  1. var onRE = /^@|^v-on:/;
  2. var dirRE = /^v-|^@|^:/;
  3. var forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
  4. var forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
  5. var stripParensRE = /^\(|\)$/g;
  6. var argRE = /:(.*)$/;
  7. var bindRE = /^:|^v-bind:/;
  8. var modifierRE = /\.[^.]+/g;
复制代码
在解析开始标签的时候你遇到的不仅有属性,还有一些Vue 自定义的指令。下面一起来分析下解析指令会有用哪些正则。
onRE
  1. var onRE = /^@|^v-on:/;
复制代码
匹配以字符 @ 或 v-on: 开头的字符串,主要作用是检测标签属性名是否是监听事件的指令。
dirRE
  1. var const dirRE = /^v-|^@|^:/
复制代码
匹配以字符 v- 或 @ 或 : 开头的字符串,主要作用是检测标签属性名是否是指令。在Vue中所有以 v- 开头的属性都被认为是指令,另外@字符是 v-on 的缩写,: 字符是 v-bind 的缩写。
forAliasRE
  1. var forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
复制代码
匹配 v-for 属性的值,并捕获 in 或 of 前后的字符串。都是正则大神就不解释怎么捕获的了。
forIteratorRE
  1. var forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
复制代码
这个也是匹配v-for的属性值,不过比之前要稍微复杂点:列表渲染 v-for(https://cn.vuejs.org/v2/guide/list.html)需要先了解下这个。
  1. //示例:1
  2. <div v-for="(value, name) in object">
  3.   {{ name }}: {{ value }}
  4. </div>
  5. //示例:2
  6. <div v-for="(value, name, index) in object">
  7.   {{ index }}. {{ name }}: {{ value }}
  8. </div>
复制代码
没错就是用来捕获,示例1中的:'obj , index' 示例2中的:'value, key, index' 。
stripParensRE
  1. var stripParensRE = /^\(|\)$/g;
复制代码
这个捕获组用来捕获要么以字符 ( 开头,要么以字符 ) 结尾的字符串,或者两者都满足。那么这个正则的作用是什么呢?我们在讲解正则 forIteratorRE 时有个细节不知道大家注意到了没有,就是 forIteratorRE 正则所匹配的字符串是 'obj, index' ,而不是 '(obj, index)' ,这两个字符串的区别就在于第二个字符串拥有左右括号,所以在使用 forIteratorRE 正则之前,需要使用 stripParensRE 正则去掉字符串 '(obj, index)' 中的左右括号,实现方式很简单:
  1. "(obj, index)".replace(stripParensRE, "")
复制代码
argRE
  1. var argRE = /:(.*)$/;
复制代码
argRE正则用来匹配指令编写中的参数,并且拥有一个捕获组,用来捕获参数的名字。
示例:
  1. <div v-on:click.item="handle"></div>
复制代码
其中 v-on 为指令,click为传递给 v-on 指令的参数,stop 为修饰符。
bindRE
  1. var bindRE = /^:|^v-bind:/;
复制代码
该正则用来匹配以字符:或字符串 v-bind: 开头的字符串,主要用来检测一个标签的属性是否是绑定(v-bind)。
modifierRE
  1. var modifierRE = /\.[^.]+/g;
复制代码
该正则用来匹配修饰符的,但是并没有捕获任何东西,但你可以用match、exec等方法获取与当前正则匹配成功的信息。

parse 函数中的变量

在讲解 parse 函数直接我们还需要先了解下它内部所定义的一些变量以及用途。
  1. function parse(template, options) {
  2.         warn$2 = options.warn || baseWarn;
  3.         platformIsPreTag = options.isPreTag || no;
  4.         platformMustUseProp = options.mustUseProp || no;
  5.         platformGetTagNamespace = options.getTagNamespace || no;
  6.         transforms = pluckModuleFunction(options.modules, 'transformNode');
  7.         preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
  8.         postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
  9.         delimiters = options.delimiters;
  10.         var stack = [];
  11.         var preserveWhitespace = options.preserveWhitespace !== false;
  12.         var root;
  13.         var currentParent;
  14.         var inVPre = false;
  15.         var inPre = false;
  16.         var warned = false;
  17.         function warnOnce(msg) {
  18.         //...
  19.         }
  20.         function closeElement(element) {
  21.        //...
  22.         }
  23.         parseHTML(template, {
  24.                 warn: warn$2,
  25.                 expectHTML: options.expectHTML,
  26.                 isUnaryTag: options.isUnaryTag,
  27.                 canBeLeftOpenTag: options.canBeLeftOpenTag,
  28.                 shouldDecodeNewlines: options.shouldDecodeNewlines,
  29.                 shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
  30.                 shouldKeepComment: options.comments,
  31.                 start: function start(tag, attrs, unary) {},
  32.                 end: function end() {},
  33.                 chars: function chars(text) {},
  34.                 comment: function comment(text) {},
  35.         });
  36.         return root
  37. }
复制代码
我们先来看下针对web平台初始化的一些变量。
  1. warn$2 = options.warn || baseWarn;
  2. platformIsPreTag = options.isPreTag || no;
  3. platformMustUseProp = options.mustUseProp || no;
  4. platformGetTagNamespace = options.getTagNamespace || no;
  5. transforms = pluckModuleFunction(options.modules, 'transformNode');
  6. preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
  7. postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
  8. delimiters = options.delimiters;
复制代码
    warn$2 函数 毋庸置疑它作用是用来打印警告信息的platformIsPreTag 函数是一个编译器选项,其作用是通过给定的标签名字判断该标签是否是 pre 标签。platformMustUseProp 该函数也是一个编译器选项,其作用是用来检测一个属性在标签中是否要使用元素对象原生的 prop 进行绑定。platformGetTagNamespace 该函数是一个编译器选项,其作用是用来获取元素(标签)的命名空间。transforms 、preTransforms 、postTransforms 还没讲到它们的上下文,暂时不解释它们的作用。delimiters 它的值为 options.delimiters 属性,它的值就是在创建 Vue 实例对象时所传递的 delimiters 选项。
继续往下看:
  1. var stack = [];
  2. var preserveWhitespace = options.preserveWhitespace !== false;
  3. var root;
  4. var currentParent;
  5. var inVPre = false;
  6. var inPre = false;
  7. var warned = false;
复制代码
    stack 初始值是一个空数组,作用在上个章节我们讲到,回退操作为了让子元素描述对象的parent属性能够正确指向其父元素。preserveWhitespace 是一个布尔值并且它的值与编译器选项中的options.preserveWhitespace选项有关,只要 options.preserveWhitespace 的值不为false,那么 preserveWhitespace 的值就为真。其中 options.preserveWhitespace 选项用来告诉编译器在编译 html 字符串时是否放弃标签之间的空格,如果为 true 则代表放弃。root 存储最终生成的AST。currentParent 通过上章节了解到,该变量维护元素描述对象之间的父子关系。inVPre 初始值:false。标识当前解析的标签是否在拥有 v-pre (跳过这个元素和它的子元素的编译过程。)的标签之内。inPre 初始值:false。标识当前正在解析的标签是否在 <pre></pre> 标签之内。warned 初始值:false。用来打印警告信息的函数,只不过 warnOnce 函数就如它的名字一样,只会打印一次警告信息,并且 warnOnce 函数也是通过调用 warn 函数来实现的。
好了一些边边角角的东西就先讲到这,接下来我们一起来分析Vue parse源码看看一颗完整的AST树是如何构建出来的。更多关于vue parseHTML函数AST预备的资料请关注中国红客联盟其它相关文章!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行