vue的整个架构的流程图
compiler目的实现解析html里面的vue指令,例如:v-test,v-html,v-model @click等等
//compile工具库
const compileUtil = {
getValue(expr,vm)
{
//根据 字符串 test.name.key 来获取实际对象的 test[name][key] = 123
return expr.split(".").reduce((data,currentVal)=>{
//不断递归循环直到 expr.split(".")的数组完毕
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执行
node.addEventListener(detailStr,fn.bind(vm),false)
},
bind(node,expr,vm,detailStr)
{
node.setAttribute(detailStr,expr)
},
//视图更新函数
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. 将预编译的元素节点放入文档碎片对象中,避免DOM频繁的回流与重绘,提高渲染性能
const fragments = this.node2fragments(this.el)
//2.编译模板
this.compile(fragments)
//3. 追加子元素到根元素
this.el.appendChild(fragments);
}
compile(fragments)
{
// 1.获取子节点
let childNodes = fragments.childNodes
//2.递归循环编译
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;
//(.+)默认是贪婪匹配
//(.+?)为惰性匹配
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(":");
//根据 属性来处理对应的指令
compileUtil[compileKey](node,value,this.vm,detailStr);
// 删除有指令的标签属性 v-text v-html等,普通的value等原生html标签不必删除
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)
{
//判断是否@开头 事件绑定
return attrName.startsWith('@')
}
isDirector(attrName)
{
//判断是否vue特性标签
return attrName.startsWith('v-')
}
isElementNode(node)
{
// nodeType 属性返回节点类型。
// 如果节点是一个元素节点,nodeType 属性返回 1。
// 如果节点是属性节点, nodeType 属性返回 2。
// 如果节点是一个文本节点,nodeType 属性返回 3。
// 如果节点是一个注释节点,nodeType 属性返回 8。
//元素节点的nodeType属性等于 1
return node.nodeType === 1
}
node2fragments(el)
{
const f = document.createDocumentFragment()
let firstChild
while(firstChild = el.firstChild)
{
//这里的虚拟节点 append之后 就会把被添加过的节点从el中删除掉
f.appendChild(firstChild)
}
return f;
}
}