html部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模拟vue双向绑定</title>
</head>
<body>
<div id="root">
<ul>
<li>123</li>
<li>456</li>
<li>789</li>
</ul>
<p>{{nickname}}</p>
<p>{{age}}</p>
</div>
</body>
</html>
js部分:
class MyVue{
static sign = /\{\{(.+?)\}\}/g; // {{ }}正则匹配
constructor(options){
const _this = this;
// _ 标识内部属性 , $表示 只读私有属性
this._el = options.el;
this._data = options.data;
// 监听数据的变化
for(let i in this._data){
let d = _this[i] = this._data[i] //将this._data的数据绑定到MyVue实例中
Object.defineProperty(_this,i,{
get(){
return d
},
set(newValue){
console.log(newValue)
d = newValue;
_this.render()
}
})
}
this._templateDOM = document.getElementById(this._el)
this._parent = this._templateDOM.parentNode;
this.render()
options.mounted.bind(_this)()
}
// 渲染
render(){
// 1.获取每个dom及其子节点,正则匹配到每个节点
let copyTemplate = this._templateDOM.cloneNode(true); //深拷贝dom,含子节点
this.compile(copyTemplate)
this.update(copyTemplate)
}
// 编译
compile( template ){
let _this = this;
let childNodes = template.childNodes;
for(let i = 0;i<childNodes.length;i++){
let type = childNodes[i].nodeType;
if(type === 3){
// 文本节点
let txt = childNodes[i].nodeValue;
txt = txt.replace(MyVue.sign,function(_,group){
//js中的replace函数 是支持回调函数的,回调中的第一个参数是匹配到的参数,return可以返回一个希望匹配替换成的值
let key = group.trim()
let value = _this[key]
return value;
})
childNodes[i].nodeValue = txt;
}else if(type === 1){
// node节点 递归调用
_this.compile(childNodes[i])
}
}
}
// 更新替换
update(realDom){
let replacdChild = document.getElementById(this._el);
this._parent.replaceChild(realDom,replacdChild)
}
}
let VueDom = new MyVue({
el:'root',
data:{
nickname:'我的名字',
age:20
},
mounted(){
setTimeout(() => {
console.log('mounted',this)
this.nickname = '你的你的'
}, 2000);
}
})
console.log(VueDom)