编译过程首先就是对模板做解析,生成 AST,它是一种抽象语法树,是对源代码的抽象语法结构的树状表现形式。
Vue的实现方式是从头开始解析,运用正则表达式查找,逐部分字符分析、处理、删减,直到处理完整个template字符,最终得到一颗完整的AST Tree。代码在src/compiler/parser/index.js
中的parseHTML()
。
if (inVPre) { processRawAttrs(element) } else if (!element.processed) { // structural directives processFor(element) processIf(element) processOnce(element) processElement(element, options) // processKey/processRef/processSlot/processComponent/processAttrs }
Copied!
export function parseHTML (html, options) { let lastTag while (html) { if (!lastTag || !isPlainTextElement(lastTag)){ let textEnd = html.indexOf('<') // 如果是<开头 if (textEnd === 0) { // 注释/Doctype都是匹配整个区域 if(matchComment) { advance(commentLength) continue } if(matchDoctype) { advance(doctypeLength) continue } // 匹配结尾,如</h1>。new RegExp(`^<\\/${qnameCapture}[^>]*>`) if(matchEndTag) { advance(endTagLength) parseEndTag() // 1. stack弹出标签 2. options.end continue } // 匹配开始 if(matchStartTag) { parseStartTag() // 将<h1 name="123">分别处理为:<h1、name="123"、> handleStartTag() // stack压入 2. options.start continue } } // 开闭标签之间的纯文本 handleText() advance(textLength) } else { // 处理特殊的script,style,textarea handlePlainTextElement() parseEndTag() } } } function advance (n) { index += n html = html.substring(n) // 每次都截取文字 }
Copied!
// 处理时html在变化(游标作用) " v-for="branch in branches"> <input type="radio" :id="branch" :value="branch" name="branch" v-model="currentBranch"> <label :for="branch">{{ branch }}</label> </template> <p>vuejs/vue@{{ currentBranch }}</p> </div>"
Copied!
<ul :class="bindCls" class="list" v-if="isShow"> <li v-for="(item,index) in data" @click="clickItem(index)">{{item}}:{{index}}</li> </ul>
Copied!
ast = { 'type': 1, 'tag': 'ul', 'attrsList': [], 'attrsMap': { ':class': 'bindCls', 'class': 'list', 'v-if': 'isShow' }, 'if': 'isShow', 'ifConditions': [{ 'exp': 'isShow', 'block': // ul ast element }], 'parent': undefined, 'plain': false, 'staticClass': 'list', 'classBinding': 'bindCls', 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [{ 'name': '@click', 'value': 'clickItem(index)' }], 'attrsMap': { '@click': 'clickItem(index)', 'v-for': '(item,index) in data' }, 'parent': // ul ast element 'plain': false, 'events': { 'click': { 'value': 'clickItem(index)' } }, 'hasBindings': true, 'for': 'data', 'alias': 'item', 'iterator1': 'index', 'children': [ 'type': 2, 'expression': '_s(item)+":"+_s(index)' 'text': '{{item}}:{{index}}', 'tokens': [ {'@binding':'item'}, ':', {'@binding':'index'} ] ] }] }
Copied!