双向绑定数据的实现(new Proxy 版本)
之前有用 Object.defineProperty 实现了一个双向绑定,过于繁琐,这是 基于 Proxy 实现了一版,也优化了模版渲染的部分,代码更简洁。
调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>myvue</title>
<script src="myvue.js"></script>
</head>
<body>
<div id="app">
姓名:{{name}}
<div>
年龄:{{age}}
<p>
身高:{{height}}
</p>
<p>
体重:{{weight}}
</p>
</div>
<div>
双向数据绑定:<input type="text" v-model="inputData">
{{inputData}}
</div>
</div>
<script>
let vm = new Myvue({
el: '#app',
data: {
name: 'lili',
age: 18,
height: '170cm',
weight: '65kg',
inputData: '双向数据绑定'
}
})
</script>
</body>
</html>
myvue.js 核心实现
// 基础知识点扫盲
// - 自定义事件,和调用自定义事件 new CustomEvent this.dispathEvent
// - 监听拦截数据方法 Proxy, new Proxy(data, set(target, prop, newValue){})
// - EventTarget 是一个 DOM 接口,由可以接收事件
class Myvue extends EventTarget{
constructor(option) {
super();
this.option = option
this._data = this.option.data;
this.el = document.querySelector(option.el);
this.observe(this._data); // 监听观察数据
this.complieNode(this.el) // 编译节点,渲染数据
}
observe(data) {
let _this = this;
// 每次访问、设置等 data 的时候,都会经过 proxy 代理过后的对象,便于拦截,监听等操作
this._data = new Proxy(data, {
set(target, prop, newValue){
// 监听到新值变化
// 如何通知视图修改数据?
console.log(newValue);
// 自定义事件
let event = new CustomEvent(prop, {
detail: newValue
});
_this.dispatchEvent(event);
return Reflect.set(...arguments) // 设置值
}
})
}
complieNode(el) {
let child = el.childNodes;
// 类数组转成数组
[...child].forEach(element => {
if(element.nodeType === 3) {
// console.log('文本节点')
let textContent = element.textContent;
// console.log(textContent) {{name}} {{age}}
let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g;
if(reg.test(textContent)) {
let $1 = RegExp.$1;
// console.log($1); name age
if(this._data[$1]) {
element.textContent = textContent.replace(reg, this._data[$1]);
// 绑定自定义事件,自定义事件名就是 双向数据绑定的值 name age inputData
this.addEventListener($1, e => {
console.log(e)
element.textContent = textContent.replace(reg, e.detail);
})
}
}
} else if(element.nodeType===1){
// console.log('元素节点');
let attr = element.attributes;
if(attr.hasOwnProperty('v-model')) {
let keyName = attr['v-model'].nodeValue; // inputData
element.value = this._data[keyName];
// 绑定input 事件, 修改input内容时候,同时更新掉所有 {{inputData}} 数据, 即监听 inputData
element.addEventListener('input', e => {
this._data[keyName] = element.value ;
// this.observe(this._data);
})
}
this.complieNode(element)
}
});
}
}