这篇文章给大家介绍的内容是关于什么是AST?Vue源码中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...
以
<p> 请输入:<input><br> </p>
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的结构如下:
parseHTML内容大纲
last = html; //确认html不是类似<script>,<style>这样的纯文本标签 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 } } //判断是否处理向下兼容的注释,类似<![if !IE]> //var conditionalComment = /^<!\[/; if (conditionalComment.test(html)) { var conditionalEnd = html.indexOf(']>'); if (conditionalEnd >= 0) { advance(conditionalEnd + 2); continue } } //获取<!DOCTYPE开头的标签内容 // var doctype = /^<!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) { //代码省略 } }</script>
parseHTML使用while循环对传进来的html进行解析。首先获取var textEnd = html.indexOf('如果textEnd为0 说明是标签var endTagMatch = html.match(endTag);
匹配不到那么就是开始标签,调用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),右开始标签的位置(end)。本文的例子,程序第一次进入该函数,所以tagName:p,start:0,end:14 match
function advance (n) { index += n; html = html.substring(n); }
advance函数将局部变量index往后推 并切割字符串。
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 <p>在该函数中,对match进行了二次处理,根据标签名、属性生成一个新对象,push到最开始的stack数组中。<br>由于匹配的是起始标签,所以也会以这个标签名结束,此处的lastTag就是p。<br>函数最后调用了parse内部声明的方法start</p><pre class="brush:php;toolbar:false">function 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 ' + "" + ', as they will not be parsed.' ); } // apply pre-transforms for (var i = 0; i <p>对标签名进行校验,同时对属性进行更细致的处理,如v-pre,v-for,v-if等。最后调用processElement(element, options)对当前的树节点元素进行处理,具体如下:</p><pre class="brush:php;toolbar:false"> 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 <p>start()生成element对象,再连接元素的parent和children节点,最后push到栈中,此时栈中第一个元素生成。结构如下:</p><p style="text-align: center;"><span class="img-wrap"><img src="https://img.php.cn//upload/image/187/717/423/1533181233204676.png" title="1533181233204676.png" alt="什麼是AST? Vue源碼中AST語法樹的解析"></span></p><p>接下来开始第二次循环,html变成了 请输入:<input type="text" v-model="message"></p><p>,因此这次解析的是文字:'请输入',具体代码分析在下一次~~~</p><p class="post-topheader custom- pt0">相关文章推荐:</p><p class="post-topheader custom- pt0"><a href="//m.sbmmt.com/php-weizijiaocheng-256904.html" target="_blank" class="title">使用PHP-Parser生成<span class="course-color">AST</span>抽象语法树</a></p>
以上是什麼是AST? Vue源碼中AST語法樹的解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!