页面端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
订阅发布1:<span class="box1"></span>
订阅发布2:<span class="box2"></span>
<script src="index.js"></script>
<script>
let dataObj = {};
dataHijack({
data: dataObj,
tag: "view1",
dataKey: "one",
selector: ".box1"
});
dataHijack({
data: dataObj,
tag: "view2",
dataKey: "two",
selector: ".box2"
});
dataObj.one = "一笑置之";
dataObj.two = "爱丽丝";
</script>
</body>
</html>
JS代码
//当数据改变时,我们要进行通知
//只有订阅这个数据 才会通知
//订阅器模型
let Dep = {
clientList: {}, //定义List列表用来存放消息 干货:为什么用对象来存储 → 深拷贝,浅拷贝
//如果用数组,去数组里取值就必须用key去取值,相当于深拷贝,深拷贝 复杂度与消耗性能比浅拷贝多
//如果用对象 就避免了这层 查找更快
listen: function (key, fn) {
//key是唯一的 可以有多个函数
// if(!this.clientList[key]){//判断key是否存在 若不存在 给其预留空间
// this.clientList[key]=[]
// }
// this.clientList[key].push(fn);
//vue源码写法 短路运算符
(this.clientList[key] || (this.clientList[key] = [])).push(fn); //判断key是否存在 不存在 预留空间 且用push方法将函数fn推入
},
//发布消息
trigger: function () {
//类数组转化未数组
let key = Array.prototype.shift.call(arguments), //需要理解
fns = this.clientList[key];
if (!fns || fns.length === 0) {
//判断是否存在fns这个值
return false;
}
//vue源码写法 不会有终止条件
for (let i = 0,fn; fn = fns[i++];) {
fn.apply(this, arguments); //改变指针
}
},
};
//发布订阅模式+数据劫持
//劫持方法
let dataHijack = function ({ data, tag, dataKey, selector }) {
//data:需要劫持的数据 tag:具体的对象 datakey:唯一的值 ,selector:选择的元素
let value = "",
el = document.querySelector(selector);
//数据劫持
Object.defineProperty(data, dataKey, {
get: function () {
console.log("我获取到了值");
return value;
},
set: function (newValue) {
console.log("我设置了值"); //当设置了新的值 我们要进行通知 订阅方 更改
//通知订阅者
value = newValue; //新旧值的替换
Dep.trigger(tag, newValue); //关键点
},
});
//添加订阅者
Dep.listen(tag, function (text) {
//关键点
el.innerHTML = text;
});
};