真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么在Vue中將Template轉(zhuǎn)化為AST-創(chuàng)新互聯(lián)

本文章向大家介紹怎么在Vue中將Template轉(zhuǎn)化為AST,主要包括怎么在Vue中將Template轉(zhuǎn)化為AST的使用實(shí)例、應(yīng)用技巧、基本知識點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:國際域名空間、虛擬空間、營銷軟件、網(wǎng)站建設(shè)、新華網(wǎng)站維護(hù)、網(wǎng)站推廣。Vue的優(yōu)點(diǎn)

Vue具體輕量級框架、簡單易學(xué)、雙向數(shù)據(jù)綁定、組件化、數(shù)據(jù)和結(jié)構(gòu)的分離、虛擬DOM、運(yùn)行速度快等優(yōu)勢,Vue中頁面使用的是局部刷新,不用每次跳轉(zhuǎn)頁面都要請求所有數(shù)據(jù)和dom,可以大大提升訪問速度和用戶體驗(yàn)。

什么是AST

在Vue的mount過程中,template會(huì)被編譯成AST語法樹,AST是指抽象語法樹(abstract syntax tree或者縮寫為AST),或者語法樹(syntax tree),是源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式。

Virtual Dom

Vue的一個(gè)厲害之處就是利用Virtual DOM模擬DOM對象樹來優(yōu)化DOM操作的一種技術(shù)或思路。

Vue源碼中虛擬DOM構(gòu)建經(jīng)歷 template編譯成AST語法樹 -> 再轉(zhuǎn)換為render函數(shù) 最終返回一個(gè)VNode(VNode就是Vue的虛擬DOM節(jié)點(diǎn))

本文通過對源碼中AST轉(zhuǎn)化部分進(jìn)行簡單提取,因?yàn)樵创a中轉(zhuǎn)化過程還需要進(jìn)行各種兼容判斷,非常復(fù)雜,所以筆者對主要功能代碼進(jìn)行提取,用了300-400行代碼完成對template轉(zhuǎn)化為AST這個(gè)功能。下面用具體代碼進(jìn)行分析。

 function parse(template) {
    var currentParent;  //當(dāng)前父節(jié)點(diǎn)
    var root;      //最終返回出去的AST樹根節(jié)點(diǎn)
    var stack = [];
    parseHTML(template, {
      start: function start(tag, attrs, unary) {
        ......
      },
      end: function end() {
       ......
      },
      chars: function chars(text) {
        ......
      }
    })
    return root
  }

第一步就是調(diào)用parse這個(gè)方法,把template傳進(jìn)來,這里假設(shè)template為

{{message}}

然后聲明3個(gè)變量

currentParent -> 存放當(dāng)前父元素,root -> 最終返回出去的AST樹根節(jié)點(diǎn),stack -> 一個(gè)棧用來輔助樹的建立

接著調(diào)用parseHTML函數(shù)進(jìn)行轉(zhuǎn)化,傳入template和options(包含3個(gè)方法 start,end,chars 等下用到這3個(gè)函數(shù)再進(jìn)行解釋)接下來先看parseHTML這個(gè)方法

function parseHTML(html, options) {
    var stack = [];  //這里和上面的parse函數(shù)一樣用到stack這個(gè)數(shù)組 不過這里的stack只是為了簡單存放標(biāo)簽名 為了和結(jié)束標(biāo)簽進(jìn)行匹配的作用
    var isUnaryTag$$1 = isUnaryTag;  //判斷是否為自閉合標(biāo)簽
    var index = 0;
    var last;
    while (html) {
      //  第一次進(jìn)入while循環(huán)時(shí),由于字符串以<開頭,所以進(jìn)入startTag條件,并進(jìn)行AST轉(zhuǎn)換,最后將對象彈入stack數(shù)組中
      last = html;
      var textEnd = html.indexOf('<');
      if (textEnd === 0) {   // 此時(shí)字符串是不是以<開頭
        // End tag:
        var endTagMatch = html.match(endTag);
        if (endTagMatch) {
          var curIndex = index;
          advance(endTagMatch[0].length);
          parseEndTag(endTagMatch[1], curIndex, index);
          continue
        }

        // Start tag:  // 匹配起始標(biāo)簽
        var startTagMatch = parseStartTag();  //處理后得到match
        if (startTagMatch) {
          handleStartTag(startTagMatch);
          continue
        }
      }

      // 初始化為undefined 這樣安全且字符數(shù)少一點(diǎn)
      var text = (void 0), rest = (void 0), next = (void 0);
      if (textEnd >= 0) {   // 截取<字符索引 => 
 這里截取到閉合的<         rest = html.slice(textEnd); //截取閉合標(biāo)簽         // 處理文本中的<字符         // 獲取中間的字符串 => {{message}}         text = html.substring(0, textEnd); //截取到閉合標(biāo)簽前面部分         advance(textEnd);        //切除閉合標(biāo)簽前面部分       }       // 當(dāng)字符串沒有<時(shí)       if (textEnd < 0) {         text = html;         html = '';       }       // // 處理文本       if (options.chars && text) {         options.chars(text);       }     }   }

函數(shù)進(jìn)入while循環(huán)對html進(jìn)行獲取<標(biāo)簽索引 var textEnd = html.indexOf('<');如果textEnd === 0 說明當(dāng)前是標(biāo)簽或者 再用正則匹配是否當(dāng)前是結(jié)束標(biāo)簽。var endTagMatch = html.match(endTag); 匹配不到那么就是開始標(biāo)簽,調(diào)用parseStartTag()函數(shù)解析。

function parseStartTag() {   //返回匹配對象
  var start = html.match(startTagOpen);     // 正則匹配
  if (start) {
    var match = {
      tagName: start[1],    // 標(biāo)簽名(div)
      attrs: [],        // 屬性
      start: index       // 游標(biāo)索引(初始為0)
    };
    advance(start[0].length);
    var end, attr;
    while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { 
      advance(attr[0].length); 
      match.attrs.push(attr);
    }
    if (end) {
      advance(end[0].length);   // 標(biāo)記結(jié)束位置
      match.end = index;   //這里的index 是在 parseHTML就定義 在advance里面相加
      return match     // 返回匹配對象 起始位置 結(jié)束位置 tagName attrs
    }
  }
}

該函數(shù)主要是為了構(gòu)建一個(gè)match對象,對象里面包含tagName(標(biāo)簽名),attrs(標(biāo)簽的屬性),start(<左開始標(biāo)簽在template中的位置),end(>右開始標(biāo)簽在template中的位置) 如template =

{{message}}
程序第一次進(jìn)入該函數(shù) 匹配的是div標(biāo)簽 所以tagName就是div
start:0 end:14 如圖:

怎么在Vue中將Template轉(zhuǎn)化為AST

接著把match返回出去 作為調(diào)用handleStartTag的參數(shù)

var startTagMatch = parseStartTag();  //處理后得到match
if (startTagMatch) {
  handleStartTag(startTagMatch);
  continue
}

接下來看handleStartTag這個(gè)函數(shù):

 function handleStartTag(match) {
  var tagName = match.tagName;
  var unary = isUnaryTag$$1(tagName) //判斷是否為閉合標(biāo)簽 
  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
    };
  }
  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);
  }
  }

函數(shù)中分為3部分 第一部分是for循環(huán)是對attrs進(jìn)行轉(zhuǎn)化,我們從上一步的parseStartTag()得到的match對象中的attrs屬性如圖

怎么在Vue中將Template轉(zhuǎn)化為AST

當(dāng)時(shí)attrs是上面圖這樣子滴 我們通過這個(gè)循環(huán)把它轉(zhuǎn)化為只帶name 和 value這2個(gè)屬性的對象 如圖:

怎么在Vue中將Template轉(zhuǎn)化為AST

接著判斷如果不是自閉合標(biāo)簽,把標(biāo)簽名和屬性推入棧中(注意 這里的stack這個(gè)變量在parseHTML中定義,作用是為了存放標(biāo)簽名 為了和結(jié)束標(biāo)簽進(jìn)行匹配的作用。)接著調(diào)用最后一步 options.start 這里的options就是我們在parse函數(shù)中 調(diào)用parseHTML是傳進(jìn)來第二個(gè)參數(shù)的那個(gè)對象(包含start end chars 3個(gè)方法函數(shù)) 這里開始看options.start這個(gè)函數(shù)的作用:

start: function start(tag, attrs, unary) {
  var element = {
    type: 1,
    tag: tag,
    attrsList: attrs,
    attrsMap: makeAttrsMap(attrs),
    parent: currentParent,
    children: []
  };
  processAttrs(element);
  if (!root) {
    root = element;
  } 
  if(currentParent){
    currentParent.children.push(element);
    element.parent = currentParent;
  }
  if (!unary) {
    currentParent = element;
    stack.push(element);
  }
}

這個(gè)函數(shù)中 生成element對象 再連接元素的parent 和 children節(jié)點(diǎn) 最終push到棧中

此時(shí)棧中第一個(gè)元素生成 如圖:

怎么在Vue中將Template轉(zhuǎn)化為AST

完成了while循環(huán)的第一次執(zhí)行,進(jìn)入第二次循環(huán)執(zhí)行,這個(gè)時(shí)候html變成{{message}}

接著截取到 處理過程和第一次一致 經(jīng)過這次循環(huán)stack中元素如圖:

怎么在Vue中將Template轉(zhuǎn)化為AST

怎么在Vue中將Template轉(zhuǎn)化為AST

接著繼續(xù)執(zhí)行第三個(gè)循環(huán) 這個(gè)時(shí)候是處理文本節(jié)點(diǎn)了 {{message}}

// 初始化為undefined 這樣安全且字符數(shù)少一點(diǎn)
var text = (void 0), rest = (void 0), next = (void 0);
if (textEnd >= 0) {   // 截取<字符索引 => 
 這里截取到閉合的<   rest = html.slice(textEnd); //截取閉合標(biāo)簽   // 處理文本中的<字符   // 獲取中間的字符串 => {{message}}   text = html.substring(0, textEnd); //截取到閉合標(biāo)簽前面部分   advance(textEnd);        //切除閉合標(biāo)簽前面部分 } // 當(dāng)字符串沒有<時(shí) if (textEnd < 0) {   text = html;   html = ''; } // 另外一個(gè)函數(shù) if (options.chars && text) {   options.chars(text); }

這里的作用就是把文本提取出來 調(diào)用options.chars這個(gè)函數(shù) 接下來看options.chars

chars: function chars(text) {
  if (!currentParent) {  //如果沒有父元素 只是文本
    return
  }

  var children = currentParent.children; //取出children
  // text => {{message}}
  if (text) {
    var expression;
    if (text !== ' ' && (expression = parseText(text))) {
      // 將解析后的text存進(jìn)children數(shù)組
      children.push({
        type: 2,
        expression: expression,
        text: text
      });
    } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
      children.push({
        type: 3,
        text: text
      });
    }
  }
}
})

這里的主要功能是判斷文本是{{xxx}}還是簡單的文本xxx,如果是簡單的文本 push進(jìn)父元素的children里面,type設(shè)置為3,如果是字符模板{{xxx}},調(diào)用parseText轉(zhuǎn)化。如這里的{{message}}轉(zhuǎn)化為 _s(message)(加上_s是為了AST的下一步轉(zhuǎn)為render函數(shù),本文中暫時(shí)不會(huì)用到。) 再把轉(zhuǎn)化后的內(nèi)容push進(jìn)children。

怎么在Vue中將Template轉(zhuǎn)化為AST

又走完一個(gè)循環(huán)了,這個(gè)時(shí)候html = 剩下2個(gè)結(jié)束標(biāo)簽進(jìn)行匹配了

 var endTagMatch = html.match(endTag);
  if (endTagMatch) {
    var curIndex = index;
    advance(endTagMatch[0].length);
    parseEndTag(endTagMatch[1], curIndex, index);
    continue
  }

接下來看parseEndTag這個(gè)函數(shù) 傳進(jìn)來了標(biāo)簽名 開始索引和結(jié)束索引

 function parseEndTag(tagName, start, end) {
  var pos, lowerCasedTagName;
  if (tagName) {
    lowerCasedTagName = tagName.toLowerCase();
  }
  // Find the closest opened tag of the same type
  if (tagName) { // 獲取最近的匹配標(biāo)簽
    for (pos = stack.length - 1; pos >= 0; pos--) {
      // 提示沒有匹配的標(biāo)簽
      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;
}

這里首先找到棧中對應(yīng)的開始標(biāo)簽的索引pos,再從該索引開始到棧頂?shù)乃栽卣{(diào)用options.end這個(gè)函數(shù)

 end: function end() {
  // pop stack
  stack.length -= 1;
  currentParent = stack[stack.length - 1];
},

把棧頂元素出棧,因?yàn)檫@個(gè)元素已經(jīng)匹配到結(jié)束標(biāo)簽了,再把當(dāng)前父元素更改。終于走完了,把html的內(nèi)容循環(huán)完,最終return root 這個(gè)root就是我們所要得到的AST

怎么在Vue中將Template轉(zhuǎn)化為AST

到此這篇關(guān)于怎么在Vue中將Template轉(zhuǎn)化為AST的文章就介紹到這了,更多相關(guān)的內(nèi)容請搜索創(chuàng)新互聯(lián)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持創(chuàng)新互聯(lián)!


分享名稱:怎么在Vue中將Template轉(zhuǎn)化為AST-創(chuàng)新互聯(lián)
網(wǎng)頁路徑:http://weahome.cn/article/ddgdjg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部