指令是 模板解析
的續章,本文會嘗試從源碼的角度來理解 指令
是如何被提取和應用的。正則表達式
指令的提取過程是在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