1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        vue模板編譯原理

        共 7877字,需瀏覽 16分鐘

         ·

        2021-09-11 11:32


        我們都知道 vue 寫(xiě)代碼是需要按照固定的格式,比如下面這樣:


        <template>  <div>{{msg}}</div></template>
        <script>export default { data () { return { msg: '' } }}</script>


        把 html 的內(nèi)容寫(xiě)在 template 里面,經(jīng)過(guò) vue 的處理,首先會(huì)把模板轉(zhuǎn)換為 AST 抽象語(yǔ)法樹(shù),然后把 AST 轉(zhuǎn)換為可執(zhí)行的 render 函數(shù),最后才可以生成真是 DOM,那么今天要分析的內(nèi)容是如何將模板生成 AST 的。



        首先要生成的 AST 結(jié)構(gòu)如下:


        function createASTElement (  tag,  attrs,  parent) {  return {    type: 1,    tag: tag,    attrsList: attrs,    attrsMap: attrs,    rawAttrsMap: {},    parent: parent,    children: []  }}


        然后就是把模板的內(nèi)容都遍歷一遍,通過(guò)正則去匹配標(biāo)簽的內(nèi)容。



        var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;// Regular Expressions for parsing tags and attributesvar attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;var dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;var ncname = "[a-zA-Z_][\\-\\.0-9_a-zA-Z" + (unicodeRegExp.source) + "]*";var qnameCapture = "((?:" + ncname + "\\:)?" + ncname + ")";var startTagOpen = new RegExp(("^<" + qnameCapture));var startTagClose = /^\s*(\/?)>/;var endTag = new RegExp(("^<\\/" + qnameCapture + "[^>]*>"));var doctype = /^<!DOCTYPE [^>]+>/i;// #7298: escape - to avoid being passed as HTML comment when inlined in pagevar reCache = {};var comment = /^<!\--/;var conditionalComment = /^<!\[/;


        這些正則都是源碼里面粘貼過(guò)來(lái)的,包括匹配開(kāi)始標(biāo)簽,結(jié)束標(biāo)簽,注釋等等。


        function parseHTML(html, options) {  var stack = [];  var expectHTML = options.expectHTML;  var isUnaryTag$$1 = options.isUnaryTag || no;  var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no;  var index = 0;  var last, lastTag;  while (html) {    last = html;    // 確保不是 script 或者 style 者 textarea 標(biāo)簽    if (!lastTag || !isPlainTextElement(lastTag)) {      var textEnd = html.indexOf('<');      if (textEnd === 0) {        // End tag: 匹配結(jié)束標(biāo)簽        var endTagMatch = html.match(endTag);        if (endTagMatch) {          var curIndex = index;          advance(endTagMatch[0].length);          parseEndTag(endTagMatch[1], curIndex, index);          continue        }
        // Start tag: 匹配開(kāi)始標(biāo)簽 var startTagMatch = parseStartTag(); if (startTagMatch) { handleStartTag(startTagMatch); 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) ) { // 把標(biāo)簽里面的文字提取出來(lái) hello</div> 提取 hello next = rest.indexOf('<', 1); if (next < 0) { break } textEnd += next; rest = html.slice(textEnd); } text = html.substring(0, textEnd); }
        if (textEnd < 0) { text = html; } if (text) { advance(text.length); // 去除文字 hello 剩下 </div> }
        if (options.chars && text) { options.chars(text, index - text.length, index); } } else { var endTagLength = 0; var stackedTag = lastTag.toLowerCase(); var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')); var rest$1 = html.replace(reStackedTag, function (all, text, endTag) { endTagLength = endTag.length; return '' }); index += html.length - rest$1.length; html = rest$1; parseEndTag(stackedTag, index - endTagLength, index); } }
        // Clean up any remaining tags parseEndTag();
        function advance(n) { index += n; html = html.substring(n); }
        function parseStartTag() { var start = html.match(startTagOpen); // [ '<div', 'div', index: 0, input: '<div id="1" bg="2">hello</div>', groups: undefined ] if (start) { var match = { tagName: start[1], attrs: [], start: index }; advance(start[0].length); // 去除 <div,剩下 id="1" bg="2">hello</div> var end, attr; // 把屬性都存儲(chǔ)起來(lái) id="1" bg="2" 去除屬性之后剩下 >hello</div> while (!(end = html.match(startTagClose)) && (attr = html.match(dynamicArgAttribute) || html.match(attribute))) { attr.start = index; advance(attr[0].length); attr.end = index; match.attrs.push(attr); } if (end) { match.unarySlash = end[1]; advance(end[0].length); // 把>或者/>去除剩下 hello</div> match.end = index; return match } } }
        function handleStartTag(match) { var tagName = match.tagName; var unarySlash = match.unarySlash; // 自閉合標(biāo)簽為/ 雙標(biāo)簽為‘’空
        var unary = !!unarySlash;
        var l = match.attrs.length; var attrs = new Array(l); for (var i = 0; i < l; i++) { var args = match.attrs[i]; var value = args[3] || args[4] || args[5] || ''; attrs[i] = { name: args[1], value: value }; // 按照{(diào) name: value } 的形式保存起來(lái),{ id: 1 } if (options.outputSourceRange) { attrs[i].start = args.start + args[0].match(/^\s*/).length; attrs[i].end = args.end; } } // 雙標(biāo)簽 if (!unary) { stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs, start: match.start, end: match.end }); lastTag = tagName; } if (options.start) { options.start(tagName, attrs, unary, match.start, match.end); } } // stackedTag, index - endTagLength, index function parseEndTag(tagName, start, end) { var pos, lowerCasedTagName; if (start == null) { start = index; } if (end == null) { end = index; }
        // Find the closest opened tag of the same type if (tagName) { lowerCasedTagName = tagName.toLowerCase(); for (pos = stack.length - 1; pos >= 0; pos--) { if (stack[pos].lowerCasedTag === lowerCasedTagName) { break } } } else { // If no tag name is provided, clean shop pos = 0; } if (pos >= 0) { // Close all the open elements, up the stack for (var i = stack.length - 1; i >= pos; i--) { if (options.end) { options.end(stack[i].tag, start, end); } } // Remove the open elements from the stack stack.length = pos; lastTag = pos && stack[pos - 1].tag; } }}


        上面是把所有的標(biāo)簽進(jìn)行處理,按照順序遍歷之后就可以生成 AST 了。



        var currentParent;var stack = [];var root;
        function end(tag, start) { var element = stack[stack.length - 1]; // pop stack 出棧 stack.length -= 1; currentParent = stack[stack.length - 1]; closeElement(element);}
        function start(tag, attrs, unary, start, end) { var element = createASTElement(tag, attrs, currentParent); if (!root) { root = element; } if (!unary) { currentParent = element; // push stack 入棧 stack.push(element); }}
        function chars(text, start, end) { var children = currentParent.children; if (text) { var child; child = { type: 2, expression: '', tokens: '', text: text }; if (child) { children.push(child); } }}
        function closeElement (element) { if (currentParent && !element.forbidden) { currentParent.children.push(element); element.parent = currentParent; } element.children = element.children.filter(function (c) { return !(c).slotScope; });}
        function createASTElement ( tag, attrs, parent) { return { type: 1, tag: tag, attrsList: attrs, attrsMap: attrs, rawAttrsMap: {}, parent: parent, children: [] }}
        const content = '<template><div bg="1">hello<span>11</span></div></template>'
        parseHTML(content, { start: start, end: end, chars: chars});
        console.log(root) // ast


        源碼在 vue-template-compiler

        瀏覽 60
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            啊日出水了用力小说 | 精品成人Av一区二区三区 | 出差的丰满新婚少妇 | 美女扒开粉嫩尿囗桶爽免费网站 | 国内一级黄色 | 国产真实伦在线视频 | 日本公妇乱婬免费视频一区三区 | 老子要把你cao烂h调教 | 国产精品久久久在线 | 国产亚洲综合AV婷婷 |