Vue2.0原理-指令

指令是 模板解析 的續章,本文會嘗試從源碼的角度來理解 指令 是如何被提取和應用的。正則表達式

指令的提取

指令的提取過程是在parse階段進行的,在 parseHTML 方法中,會解析字符串模板爲以下的單個ast對象express

<div class="app">
    hello {{ a }}
    <span v-for='i in 5'>{{i}}</span>
  </div>
  
被解析爲以下文本

{
  "type": 1,
  "tag": "div",
  "attrsList": [
    {
      "name": "class",
      "value": "app"
    }
  ],
  "attrsMap": {
    "class": "app"
  },
  "children": []
}

其中的 <span v-for='i in 5'>{{i}}</span> 被解析爲以下文本

{
  "type": 1,
  "tag": "span",
  "attrsList": [
    {
      "name": "v-for",
      "value": "i in 5"
    }
  ],
  "attrsMap": {
    "v-for": "i in 5"
  },
  "parent": {
    "type": 1,
    "tag": "div",
    "attrsList": [],
    "attrsMap": {
      "class": "app"
    },
    "children": [
      {
        "type": 2,
        "expression": "\"\\n    hello \"+_s(a)+\"\\n    \"",
        "tokens": [
          "\n    hello ",
          {
            "@binding": "a"
          },
          "\n    "
        ],
        "text": "\n    hello {{ a }}\n    "
      }
    ],
    "plain": false,
    "staticClass": "\"app\""
  },
  "children": []
}

經過提取爲格式化的對象,就能夠對單個的節點進行解析了app

指令的解析

首先對 v-for , v-if , v-once 三個指令進行解析dom

// processFor 調用 parseFor,
// 經過 /([^]*?)\s+(?:in|of)\s+([^]*)/ 正則表達式match後,
// 將 v-for='i in 5' 解析爲 {for: "5", alias: "i"} 對象
// 再把 {for: "5", alias: "i"} 與 element 對象合併
processFor(element)

processIf(element) // 同上 

processOnce(element) // 同上 

// processElement 是一個比較大的方法,
// 裏面對 key ref slot component attrs進行了提取和解析
// 其中的 processAttrs 方法,會提取attrs裏面的 v-bind(:) v-on(@)
// 構形成props, attrsMap, events, directives等對象
// 而後與 element 對象合併
// 值得一提的是 v-model 會被當作props,而後再次進入directives中處理
processElement(element)

指令的處理

經過上面的步驟,Vue 提取出了 props, events, attrsMap , for 等指令對象,那麼 Vue 又是如何去處理這些指令的呢? codegen/index.js 中有這樣一些代碼:函數

...
  if (el.key) {
    data += `key:${el.key},`
  }
  // ref
  if (el.ref) {
    data += `ref:${el.ref},`
  }
  ...
  if (el.attrs) {
    data += `attrs:{${genProps(el.attrs)}},`
  }
  if (el.props) {
    data += `domProps:{${genProps(el.props)}},`
  }
  if (el.events) {
    data += `${genHandlers(el.events, false, state.warn)},`
  }
  ...

上面這些代碼會對每一個屬性進行處理,而後拼接爲字符串spa

好比這段代碼
 <div class="app">
   hello {{ a }}
   <span v-for='i in 5' @click='a'>{{i}}</span>
   <input type="text"  v-model='a'>
 </div>
 
 會被處理爲
 _c('div',{staticClass:"app"},[_v("\n    hello "+_s(a)+"\n    "),_l((5),function(i){return _c('span',{on:{"click":a}},[_v(_s(i))])}),_v(" "),_c('input',{directives:[{name:"model",rawName:"v-model",value:(a),expression:"a"}],attrs:{"type":"text"},domProps:{"value":(a)},on:{"input":function($event){if($event.target.composing)return;a=$event.target.value}}})],2)
 
 這段代碼在 to-function.js 中,會被 createFunction 處理爲一個匿名函數

剩下的事情,就交給 vm.$createElement 去生成vNode了。code

相關文章
相關標籤/搜索