vue的整個(gè)架構(gòu)的流程圖
image.png
compiler目的實(shí)現(xiàn)解析html里面的vue指令,例如:v-test,v-html,v-model @click等等
//compile工具庫(kù)
const compileUtil = {
getValue(expr,vm)
{
//根據(jù) 字符串 test.name.key 來(lái)獲取實(shí)際對(duì)象的 test[name][key] = 123
return expr.split(".").reduce((data,currentVal)=>{
//不斷遞歸循環(huán)直到 expr.split(".")的數(shù)組完畢
return data[currentVal];
},vm.$data)
//vm.$data 是reduce的初始值
},
text(node,expr,vm)
{
let value;
console.log(expr)
if(expr.indexOf('{{')!==-1)
{
//replace args[0] = "{{xxx}}" args[1] = xxx
value = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
console.log(args[1])
return this.getValue(args[1],vm)
})
}
else
{
this.getValue(expr,vm)
}
this.updater.textUpdater(node,value)
},
html(node,expr,vm)
{
const value = this.getValue(expr,vm)
this.updater.htmlUpdater(node,value)
},
model(node,expr,vm)
{
const value = this.getValue(expr,vm)
this.updater.modelUpdater(node,value)
},
on(node,expr,vm,detailStr)
{
let fn = vm.$options.methods && vm.$options.methods[expr]
//把功能綁定到 vm執(zhí)行
node.addEventListener(detailStr,fn.bind(vm),false)
},
bind(node,expr,vm,detailStr)
{
node.setAttribute(detailStr,expr)
},
//視圖更新函數(shù)
updater:{
textUpdater(node,value)
{
node.textContent = value
},
htmlUpdater(node,value)
{
node.innerHTML = value;
},
modelUpdater(node,value)
{
//input 里面的value值
node.value = value;
}
}
}
class Compiler
{
constructor(el,vm)
{
this.el = this.isElementNode(el) ? el:document.querySelector(el);
this.vm = vm;
// 1. 將預(yù)編譯的元素節(jié)點(diǎn)放入文檔碎片對(duì)象中萍诱,避免DOM頻繁的回流與重繪悬嗓,提高渲染性能
const fragments = this.node2fragments(this.el)
//2.編譯模板
this.compile(fragments)
//3. 追加子元素到根元素
this.el.appendChild(fragments);
}
compile(fragments)
{
// 1.獲取子節(jié)點(diǎn)
let childNodes = fragments.childNodes
//2.遞歸循環(huán)編譯
for(var i = 0;i<childNodes.length;i++)
{
let child = childNodes[i]
if(this.isElementNode(child))
{
this.compileElement(child)
}
else
{
this.compileText(child)
}
if(child.childNodes && child.childNodes.length)
{
this.compile(child)
}
}
}
compileText(node)
{
//解析溫飽中的 {{ xxx}}
const content = node.textContent;
//(.+)默認(rèn)是貪婪匹配
//(.+?)為惰性匹配
if(/\{\{(.+?)\}\}/.test(content)){
console.log("testtt")
console.log(content)
compileUtil['text'](node,content,this.vm);
}
}
compileElement(node)
{
//v-html v-test v-mode v-bind v-on:click
let attributes = node.attributes
for(var i =0;i<attributes.length;i++)
{
//object
let attr = attributes[i]
//-text="msg" v-html=htmlStr type="text" v-model="msg"
let {name,value} = attr
if(this.isDirector(name))
{
let [,directive] = name.split("-")
let [compileKey,detailStr] = directive.split(":");
//根據(jù) 屬性來(lái)處理對(duì)應(yīng)的指令
compileUtil[compileKey](node,value,this.vm,detailStr);
// 刪除有指令的標(biāo)簽屬性 v-text v-html等,普通的value等原生html標(biāo)簽不必刪除
node.removeAttribute('v-' + directive);
}
else if(this.isEventName(name))
{
let [,detailStr] = name.split("@")
compileUtil['on'](node,value,this.vm,detailStr)
node.removeAttribute('@'+detailStr)
}
else;
}
}
isEventName(attrName)
{
//判斷是否@開(kāi)頭 事件綁定
return attrName.startsWith('@')
}
isDirector(attrName)
{
//判斷是否vue特性標(biāo)簽
return attrName.startsWith('v-')
}
isElementNode(node)
{
// nodeType 屬性返回節(jié)點(diǎn)類(lèi)型裕坊。
// 如果節(jié)點(diǎn)是一個(gè)元素節(jié)點(diǎn)包竹,nodeType 屬性返回 1。
// 如果節(jié)點(diǎn)是屬性節(jié)點(diǎn), nodeType 屬性返回 2籍凝。
// 如果節(jié)點(diǎn)是一個(gè)文本節(jié)點(diǎn)周瞎,nodeType 屬性返回 3。
// 如果節(jié)點(diǎn)是一個(gè)注釋節(jié)點(diǎn)饵蒂,nodeType 屬性返回 8声诸。
//元素節(jié)點(diǎn)的nodeType屬性等于 1
return node.nodeType === 1
}
node2fragments(el)
{
const f = document.createDocumentFragment()
let firstChild
while(firstChild = el.firstChild)
{
//這里的虛擬節(jié)點(diǎn) append之后 就會(huì)把被添加過(guò)的節(jié)點(diǎn)從el中刪除掉
f.appendChild(firstChild)
}
return f;
}
}