1、myvue.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue 发布订阅模式Dep-Watcher、Proxy 实现数据响应式</title>
<script src="./myvue.js"></script>
</head>
<body>
<div id="app">
<div>
{{name}}
</div>
fafafa{{message}}fafafa
</div>
<script>
// 一:初次渲染你
// 二: 响应式: 1、数据被修改(数据观察,数据劫持); 2、视图更新(观察者模式)
let vm = new MyVue({
el:'#app',
data:{
message:'数据',
name:'vue'
}
});
console.log(vm);
setTimeout(() => {
vm._data.message = "修改的值";
vm._data.name = 'react';
},1000);
</script>
</body>
</html>
2、myvue.js
class MyVue{
constructor(options){
this.$options = options;
this._data = options.data;
this.observe(this._data);
this.compile();
}
// observer-proxy
observe(data){
let temp = {};
this._data = new Proxy(data, {
get(target, key){
console.log('get');
temp[key] = new Dep();
if(Dep.target){
temp[key].addDep(Dep.target);
}
return Reflect.get(target, key);
},
set(target, key, newValue){
console.log('set');
temp[key] && temp[key].notify(newValue);
return Reflect.set(target, key, newValue);
}
})
}
compile(){
let ele = document.querySelector(this.$options.el);
this.compileNodes(ele);
}
compileNodes(ele){
let childNodes = ele.childNodes;
childNodes.forEach(node => {
if(node.nodeType === 1){
// 元素节点0
if(node.childNodes.length > 0){
this.compileNodes(node);
}
}else{
// 文本节点
let reg = /\{\{\s*([^{}\s]+)\s*\}\}/g;
let textContent = node.textContent;
if(reg.test(textContent)){
let $1 = RegExp.$1;
node.textContent = node.textContent.replace(reg, this._data[$1]);
console.log('11111')
new Watcher(this._data, $1, (newValue) => {
console.log('视图更新!');
let oldValue = this._data[$1];
node.textContent = node.textContent.replace(oldValue, newValue);
});
}
}
})
}
}
class Dep{
constructor(){
this.subs = [];
}
addDep(sub){
this.subs.push(sub);
}
notify(value){
this.subs.forEach(sub => {
sub.update && sub.update(value);
})
}
}
class Watcher{
constructor(data, key, cb){
Dep.target = this;
data[key]; // 触发get 收集watcher
this.cb = cb;
Dep.target = null;
}
update(value){
this.cb(value);
}
}
|