模仿vue 渲染数据、双向绑定
创建html页面
<div id="app">
{{ name }}
<div>{{age}}</div>
<span>{{message}}</span>
<div>
<input v-model="model" />
{{ model }}
</div>
js 部分
class Vue extends EventTarget {
constructor(option) {
super();
this.option = option;
// 赋值data值
this.$data = this.option.data;
// 获取当前 app
this.el = document.querySelector(this.option.el);
this.observe(this.$data)
this.compilNode(this.el)
}
observe(data) {
const that = this;
this.$data = new Proxy(data, {
get(target, prop) {
// 返回调用值
return target[prop];
},
set(target, prop, newValue) {
// 创建一个事件对象,名字为newEvent,类型为 prop传递名称
let event = new CustomEvent(prop, {
detail: newValue
})
// 触发自定义事件
that.dispatchEvent(event);
// target[prop] = newValue
// return newValue
return Reflect.set(target, prop, newValue)
}
})
}
compilNode(el) {
let child = el.childNodes;
Array.from(child).forEach((node) => {
if(node.nodeType === 3) {
let text = node.textContent;
let reg = /{{s*([^s{}]+)s*}}/g; //匹配当前页面{{name}}值
if(reg.test(text)){
let $1 = RegExp.$1;
// 查看当前data 是否有值 && 替换当前内容
this.$data[$1] && (node.textContent = text.replace(reg, this.$data[$1]));
// 将自定义事件绑定在 EventTarget 对象上
this.addEventListener($1, e=>{
// 监听当前 name名称 替换DOM内容
node.textContent = text.replace(reg, e.detail);
})
}
} else if(node.nodeType === 1){
let attr = node.attributes;
// 获取当前自定义属性 查看是否为v-model
if(attr.hasOwnProperty('v-model')) {
// 获取v-model 值
let keyName = attr['v-model'].nodeValue;
// 将仓库值赋值给当前文本框
node.value = this.$data[keyName];
// 监听文本框内容是否改变
node.addEventListener('input', e => {
// 修改仓库值
this.$data[keyName] = node.value
})
}
// 传递当前元素 处理元素标签
this.compilNode(node);
}
})
}
}
// 引用
var vm = new Vue({
el: '#app',
data: {
name: '张三',
age: 15,
model: '这是双向数据绑定值',
message: '这是一个说明文件'
}
})