模板编译
1、所有的DOM操作,都放到内存fragment中执行(创建fragment内存空间,把所有的子元素都移动到fragment中包裹,操作完DOM元素后,在把fragment移动到根节点上)。
function compile(el,data){
var fragment = document.createDocumentFragment();
var first = null;
while(first=el.firstChild){
fragment.appendChild(first);
}
renderDom(fragment);
el.appendChild(fragment);
}
2、HTML中存在v-html和{{}}内容编译成对应的数据(遍历fragment子元素,判断是元素节点还是文本节点,文本节点去掉{{}}找到对应的数据来源,在给相应的元素赋值)
区分DOM节点
function renderDom(fragment){
var _that = this;
Array.from(fragment.childNodes).forEach(function(item){
if(item.nodeType==1){
//元素节点
renderElement(item);
if(item.childNodes.length>0) renderDom(item);
}else{
//文本节点
renderText(item)
};
})
}
遍历元素节点的属性值,找到对应的v-
function renderElement(dom){
Array.from(dom.attributes).forEach(function(attr){
if(attr.name=="v-html"||attr.name=="v-text"||attr.name=="v-model"){
eleRender(attr.value,dom);
}
})
}
元素节点赋值
function eleRender(value,dom){
if(dom.tagName.toLocaleLowerCase()=="input"){
dom.value = compileData(value);
}else{
dom.innerHTML = compileData(value);
}
}
文本节点赋值
function renderText(text){
var reg = /\{\{([^}]+)\}\}/;
text.textContent.replace(reg,function(){
new Watch(arguments[1],function(newValue){
text.textContent = newValue;
})
text.textContent = compileData(arguments[1]);
})
}
数据劫持
当数据data发生改变的时候,重新编译模板。(利用的是Object.defineProperty监听数据变化,watch方法对每个元素进行监听,发布订阅者模式订阅watch对象,发布watch对象中update方法)
1、Object.defineProperty监听数据变化(是对象上的每个属性进行监听,而且要监听到数据的最底层)
function observer($data){
if(!$data || typeof($data)!="object"){
return false;
}
for(var key in $data){
defineWatch($data,key,$data[key])
}
}
function defineWatch($data,key,value){
observer(value);
//监听对象中某个属性的变化
var subScript = new Subscribe();
Object.defineProperty($data, key, {
enumerable: true,
configurable: true,
get:function(){
subScript.order(Subscribe.dep);
return value;
},
set:function(newValue){
value=newValue;
subScript.push();
}
})
}
2、定义watch方法,对每个元素进行绑定,当监听到的数据改变时,触发watch方法,对元素重新赋值
function Watch(explor,callback){
Subscribe.dep = this;
this.oldValue = compileData(explor);
Subscribe.dep = null;
this.update = function(){
var newValue = compileData(explor);
console.log("newValue",newValue)
if(this.oldValue!=newValue){
callback(newValue)
}
}
}
image.png
3、定义发布订阅者模式,当获取数据的时候,表明元素正在进行赋值操作,这时,需要订阅watch对象,当改变数据的时候,表明赋值完成,这时,需要发布watch对象上的update方法,对监听到元素进行赋值操作。
function Subscribe(){
this.store = [];
this.order = function(watch){
this.store.push(watch);
}
this.push = function(){
if(this.store.length==0) return false;
this.store.forEach(function(item){
item && item.update()
})
}
}
网友评论