什么是php的ast结构,什么是AST?Vue源码中AST语法树的解析

这篇文章给大家介绍的内容是关于什么是AST?Vue源码中AST语法树的解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

什么是AST

AST是指抽象语法树(abstract syntax tree),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式。Vue在mount过程中,template会被编译成AST语法树。

然后,经过generate(将AST语法树转化成render function字符串的过程)得到render函数,返回VNode。VNode是Vue的虚拟DOM节点,里面包含标签名、子节点、文本等信息,关于VNode的学习来自:https://blog.csdn.net/qq_3626...

请输入:

parse()var stack = [];

var preserveWhitespace = options.preserveWhitespace !== false;

var root;

var currentParent;

var inVPre = false;

var inPre = false;

var warned = false;

function warnOnce (msg){

}

function closeElement (element){

}

//调用parseHTML,这里对options的内容省略

parseHTML(template,options);

定义一些变量,root用于存放AST树根节点,currentParent存放当前父元素,stack用来辅助树建立的栈。接着调用parseHTML函数进行转化,传入template和options。

options的结构如下:

7e2bbddd283cb79812cc00823f0c20e8.png

parseHTML()

parseHTML内容大纲

9876490c3e43b0981b406aaec6564a04.pnglast = html;

//确认html不是类似

if (!lastTag || !isPlainTextElement(lastTag)) {

var textEnd = html.indexOf('<');//判断html字符串是否以<开头

if (textEnd === 0) {

// 这里的Comment是Vue定义的正则表达式,判断html是不是注释

//var comment = /^

if (comment.test(html)) {

var commentEnd = html.indexOf('-->');

if (commentEnd >= 0) {

if (options.shouldKeepComment) {

options.comment(html.substring(4, commentEnd));

}

advance(commentEnd + 3);

continue

}

}

//判断是否处理向下兼容的注释,类似

//var conditionalComment = /^

if (conditionalComment.test(html)) {

var conditionalEnd = html.indexOf(']>');

if (conditionalEnd >= 0) {

advance(conditionalEnd + 2);

continue

}

}

//获取/p>

// var doctype = /^^>]+>/i;

var doctypeMatch = html.match(doctype);

if (doctypeMatch) {

advance(doctypeMatch[0].length);

continue

}

//判断此段html是否结束标签

// var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>"));

// var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")";

// var ncname = '[a-zA-Z_][\\w\\-\\.]*';

var endTagMatch = html.match(endTag);

if (endTagMatch) {

var curIndex = index;

advance(endTagMatch[0].length);

parseEndTag(endTagMatch[1], curIndex, index);

continue

}

// 匹配开始标签,获取match对象

var startTagMatch = parseStartTag();

if (startTagMatch) {

handleStartTag(startTagMatch);

if (shouldIgnoreFirstNewline(lastTag, html)) {

advance(1);

}

continue

}

var text = (void 0), rest = (void 0), next = (void 0);

if (textEnd >= 0) {

rest = html.slice(textEnd);

while (

!endTag.test(rest) &&

!startTagOpen.test(rest) &&

!comment.test(rest) &&

!conditionalComment.test(rest)

) {

// 处理文本中的<字符

next = rest.indexOf('<', 1);

if (next < 0) { break }

textEnd += next;

rest = html.slice(textEnd);

}

text = html.substring(0, textEnd);

advance(textEnd);

}

if (textEnd < 0) {

text = html;

html = '';

}

if (options.chars && text) {

options.chars(text);

}

} else {

//代码省略

}

if (html === last) {

//代码省略

}

}

1533181211113691.png

parseHTML使用while循环对传进来的html进行解析。首先获取<标签索引var textEnd = html.indexOf('<');如果textEnd为0 说明是标签或者,再用正则匹配是否为注释标签,如果不是,再判断是否为向下兼容放入注释,如(详情见),如果不是,再判断是否已如果不是,再判断当前是否结束标签。var endTagMatch = html.match(endTag); 匹配不到那么就是开始标签,调用parseStartTag()函数解析。

parseStartTag()function parseStartTag () {

var start = html.match(startTagOpen);

if (start) {

var match = {

tagName: start[1],//标签名,本文的例子p

attrs: [],

start: index//0

};//定义match对象

advance(start[0].length);//index=4,html=" id="test">...

var end, attr;

//match对象的attrs

//index=14

while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {

advance(attr[0].length);

match.attrs.push(attr);

}

// 在第二次while循环后 end匹配到结束标签 => ['>','']

if (end) {

match.unarySlash = end[1];

advance(end[0].length);

match.end = index;

return match

}

}

}

parseStartTag()构建一个match对象,对象里面包含标签名(tagName),标签属性(attrs),<左开始标签的位置(start),>右开始标签的位置(end)。本文的例子,程序第一次进入该函数,所以tagName:p,start:0,end:14 matchfunction advance (n) {

index += n;

html = html.substring(n);

}

advance函数将局部变量index往后推 并切割字符串。

handleStartTag()function handleStartTag (match) {

var tagName = match.tagName;

var unarySlash = match.unarySlash;

if (expectHTML) {

//段落式元素

if (lastTag === 'p' && isNonPhrasingTag(tagName)) {

parseEndTag(lastTag);

}

// 可以省略闭合标签

if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) {

parseEndTag(tagName);

}

}

var unary = isUnaryTag$$1(tagName) || !!unarySlash;

var l = match.attrs.length;

var attrs = new Array(l);

//解析html属性值{name:'id',value:'test'}的格式

for (var i = 0; i < l; i++) {

var args = match.attrs[i];

if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {

if (args[3] === '') { delete args[3]; }

if (args[4] === '') { delete args[4]; }

if (args[5] === '') { delete args[5]; }

}

var value = args[3] || args[4] || args[5] || '';

var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href'

? options.shouldDecodeNewlinesForHref

: options.shouldDecodeNewlines;

attrs[i] = {

name: args[1],

// 处理转义字符

value: decodeAttr(value, shouldDecodeNewlines)

};

}

// 将切割出来的字符串转换为AST

if (!unary) {

stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs });

//设置结束标签

lastTag = tagName;

}

if (options.start) {

options.start(tagName, attrs, unary, match.start, match.end);

}

}

在该函数中,对match进行了二次处理,根据标签名、属性生成一个新对象,push到最开始的stack数组中。

由于匹配的是起始标签,所以也会以这个标签名结束,此处的lastTag就是p。

函数最后调用了parse内部声明的方法startfunction start (tag, attrs, unary) {

//检查命名空间是否是svg或者math

var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag);

// handle IE svg bug

/* istanbul ignore if */

if (isIE && ns === 'svg') {

attrs = guardIESVGBug(attrs);

}

//创建element元素,element其实就是{type: 1,

//tag: "p",

//attrsList: [{name: "id", value: "test"}]],

//attrsMap: makeAttrsMap(attrs), //parent:undefined

//children: []}的一个对象

var element = createASTElement(tag, attrs, currentParent);

if (ns) {

element.ns = ns;

}

//排除script,style标签

if (isForbiddenTag(element) && !isServerRendering()) {

element.forbidden = true;

"development" !== 'production' && warn$2(

'Templates should only be responsible for mapping the state to the ' +

'UI. Avoid placing tags with side-effects in your templates, such as ' +

"<" + tag + ">" + ', as they will not be parsed.'

);

}

// apply pre-transforms

for (var i = 0; i < preTransforms.length; i++) {

//若html里面有v-model等指令,通过preTransforms进行转换

element = preTransforms[i](element, options) || element;

}

if (!inVPre) {

// 判断是否有v-pre属性

processPre(element);

if (element.pre) {

inVPre = true;

}

}

//判断标签名是不是pre

if (platformIsPreTag(element.tag)) {

inPre = true;

}

if (inVPre) {

processRawAttrs(element);

} else if (!element.processed) {

// 处理v-for

processFor(element);

// 处理v-if

processIf(element);

// 处理v-once

processOnce(element);

// element-scope stuff

processElement(element, options);

}

// 树结构的root节点处理

if (!root) {

root = element;

checkRootConstraints(root);

} else if (!stack.length) {

// allow root elements with v-if, v-else-if and v-else

if (root.if && (element.elseif || element.else)) {

checkRootConstraints(element);

addIfCondition(root, {

exp: element.elseif,

block: element

});

} else {

warnOnce(

"Component template should contain exactly one root element. " +

"If you are using v-if on multiple elements, " +

"use v-else-if to chain them instead."

);

}

}

if (currentParent && !element.forbidden) {

if (element.elseif || element.else) {

processIfConditions(element, currentParent);

} else if (element.slotScope) { // scoped slot

currentParent.plain = false;

var name = element.slotTarget || '"default"';(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;

} else {

currentParent.children.push(element);

element.parent = currentParent;

}

}

if (!unary) {

currentParent = element;

stack.push(element);

} else {

closeElement(element);

}

}

对标签名进行校验,同时对属性进行更细致的处理,如v-pre,v-for,v-if等。最后调用processElement(element, options)对当前的树节点元素进行处理,具体如下:processKey(element);

// 检测是否是空属性节点

element.plain = !element.key && !element.attrsList.length;

// 处理:ref或v-bind:ref属性

processRef(element);

//处理标签名为slot的情况

processSlot(element);

// 处理is或v-bind:is属性

processComponent(element);

for (var i = 0; i < transforms.length; i++) {

element = transforms[i](element, options) || element;

}

//处理属性

processAttrs(element);

start()生成element对象,再连接元素的parent和children节点,最后push到栈中,此时栈中第一个元素生成。结构如下:

1533181233204676.png

接下来开始第二次循环,html变成了 请输入:

,因此这次解析的是文字:'请输入',具体代码分析在下一次~~~

相关文章推荐:

以上就是什么是AST?Vue源码中AST语法树的解析的详细内容,更多请关注php中文网其它相关文章!

article_wechat2021.jpg?1111

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值