.6-Vue源碼之AST(2)
上一節獲取到了DOM樹的字符串,準備進入compile階段:
// Line-9326 function compileToFunctions(template,options,vm) { // 獲取配置參數 options = options || {}; // ... // Go! var compiled = compile(template, options); // ... }
該函數接受兩個參數,DOM樹字符串、配置參數,如圖:,進函數:
// Line-9287
function compile(template, options) {
var finalOptions = Object.create(baseOptions);
var errors = [];
var tips = [];
finalOptions.warn = function(msg, tip$$1) {
(tip$$1 ? tips : errors).push(msg);
};
// 合並參數
if (options) {
if (options.modules) {
finalOptions.modules = (baseOptions.modules || []).concat(options.modules);
}
if (options.directives) {
finalOptions.directives = extend(
Object.create(baseOptions.directives),
options.directives
);
}
for (var key in options) {
if (key !== ‘modules‘ && key !== ‘directives‘) {
finalOptions[key] = options[key];
}
}
}
// 核心編譯函數
var compiled = baseCompile(template, finalOptions);
errors.push.apply(errors, detectErrors(compiled.ast));
// 提示與報錯屬性添加
compiled.errors = errors;
compiled.tips = tips;
return compiled
}
compile主要做了3件事:
1、參數合並
這裏涉及到baseOptions與傳進來的options,baseOptions是內置對象,與options合並後得到finalOptions作為參數傳進第二步的函數。
// Line-9529 var baseOptions = { expectHTML: true, modules: modules$1, directives: directives$1, isPreTag: isPreTag, isUnaryTag: isUnaryTag, mustUseProp: mustUseProp, canBeLeftOpenTag: canBeLeftOpenTag, isReservedTag: isReservedTag, getTagNamespace: getTagNamespace, staticKeys: genStaticKeys(modules$1) };
這個對象十分復雜,涉及的時候再做講解。
2、調用baseCompile函數
該函數接受2個參數:字符串、參數對象。
// Line-9261 function baseCompile(template, options) { // 解析字符串為AST var ast = parse(template.trim(), options); // 優化 optimize(ast, options); // var code = generate(ast, options); return { ast: ast, render: code.render, staticRenderFns: code.staticRenderFns } }
簡單看一眼這個函數,3個調用都很簡單暴力,後面一個一個講解。
3、添加提示、報錯屬性並返回compiled值
將過程中出現的error與tips作為屬性添加到compiled對象上,以便一次性輸出。
跑流程的話,主要還是看第二步,理一理DOM樹字符串是如何被解析和編譯的,來看parse這個函數吧!
這函數太長了,分兩部分來:
// Line-7982 function parse(template, options) { // 參數修正 warn$2 = options.warn || baseWarn; // 這幾個屬性都是原型鏈上面的 platformGetTagNamespace = options.getTagNamespace || no; platformMustUseProp = options.mustUseProp || no; platformIsPreTag = options.isPreTag || no; preTransforms = pluckModuleFunction(options.modules, ‘preTransformNode‘); transforms = pluckModuleFunction(options.modules, ‘transformNode‘); postTransforms = pluckModuleFunction(options.modules, ‘postTransformNode‘); // 這是自家的 值為undefined… delimiters = options.delimiters; // 變量聲明 var stack = []; var preserveWhitespace = options.preserveWhitespace !== false; var root; var currentParent; var inVPre = false; var inPre = false; var warned = false; // 功能函數 function warnOnce(msg) { if (!warned) { warned = true; warn$2(msg); } } function endPre(element) { if (element.pre) { inVPre = false; } if (platformIsPreTag(element.tag)) { inPre = false; } } parseHTML( /*...*/ ); return root }
首先是獲取options參數的屬性,其中大部分都是定義在原型上,即baseOptions,所以沒什麽講的。唯一涉及的函數就是pluckModuleFunction,比較簡單,看一下內容:
// Line-5684 function pluckModuleFunction(modules, key) { // 返回modules[key]組成的數組 return modules ? modules.map(function(m) { return m[key]; }).filter(function(_) { return _; }) : [] }
簡而言之,就是返回一個數組,內容是modules[key],這裏返回空數組。
第一部分沒什麽講的,主要是聲明一些變量,第二部分才是核心:
// Line-7982 function parse(template, options) { // ...part-1 parseHTML(template, { warn: warn$2, expectHTML: options.expectHTML, isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, start: function start(tag, attrs, unary) { /* code... */ }, end: function end() { /* code... */ }, chars: function chars(text) { /* code... */ } }); return root }
這部分就是一個函數的調用,第一個參數E為字符串,第二個參數是一個對象,包含多個屬性與方法,屬性內容如下:
// Line-7578 // 自閉合標簽 var isUnaryTag = makeMap( ‘area,base,br,col,embed,frame,hr,img,input,isindex,keygen,‘ + ‘link,meta,param,source,track,wbr‘ ); // 可以省略閉合標簽 var canBeLeftOpenTag = makeMap( ‘colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source‘ ); // 當前瀏覽器是否會對換行轉義 var shouldDecodeNewlines = inBrowser ? shouldDecode(‘\n‘, ‘ ‘) : false; function shouldDecode(content, encoded) { var div = document.createElement(‘div‘); div.innerHTML = "<div a=\"" + content + "\">"; return div.innerHTML.indexOf(encoded) > 0 }
第一個expectHTML在baseOptions中是默認true的,其余幾個是標簽名數組與換行轉義判斷的集合。
剩余三個方法包括start、end、chars,單獨講不好講,在parseHTML方法邊跑邊說。
暫時結束,parseHTML方法非常長,單獨講。
.6-Vue源碼之AST(2)