您好,欢迎访问一九零五行业门户网

什么是AST?Vue源码中AST语法树的解析

这篇文章给大家介绍的内容是关于什么是ast?vue源码中ast语法树的解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
什么是astast是指抽象语法树(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 id="test">    请输入:<input type="text" v-model="message"><br/></p>
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的结构如下:
parsehtml()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) { //代码省略 } }
parsehtml使用while循环对传进来的html进行解析。首先获取<标签索引var textend = html.indexof('<');如果textend为0 说明是标签<xx>或者</xx>,再用正则匹配是否为<!---->注释标签,如果不是,再判断是否为向下兼容放入注释,如<![if !ie]>(详情见),如果不是,再判断是否已...      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 match
 function 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内部声明的方法start
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 ' + "<" + 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到栈中,此时栈中第一个元素生成。结构如下:
接下来开始第二次循环,html变成了 请输入:<input type="text" v-model="message"></div>,因此这次解析的是文字:'请输入',具体代码分析在下一次~~~
相关文章推荐:
使用php-parser生成ast抽象语法树
以上就是什么是ast?vue源码中ast语法树的解析的详细内容。
其它类似信息

推荐信息