vue2.0的双向数据绑定主要实现方式就是: 发布订阅 + 数据劫持
这里来手写一个实现过程,
// 发布订阅 + 数据劫持
// 订阅器模型
let Dep = {
// 容器
clientList: {}, // 为何不是数组? 深拷贝与浅拷贝,使用数组需要key来进行查值
// 添加订阅者
listen: function (key, fn) {
// 短路运算
(this.clientList[key] || (this.clientList[key] = [])).push(fn)
},
// 发布消息
trigger: function () {
// 类数组转换为数组
let key = Array.prototype.shift.call(arguments),
fns = this.clientList[key];
if (!fns || fns.length === 0) {
return false;
}
// vue 源码写法
for (let i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
}
}
// 数据劫持
let dataHijack = function ({
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;
});
}
将js引入html中,
<!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="box-1"></span>
可订阅视图2 <span class="box-2"></span>
<script src="./index.js"></script>
<script>
let dataObj = {};
dataHijack({
data: dataObj,
tag: 'view-1',
datakey: 'one',
selector: '.box-1'
});
dataHijack({
data: dataObj,
tag: 'view-2',
datakey: 'two',
selector: '.box-2'
});
dataObj.one = "小";
dataObj.two = "大";
</script>
</body>
</html>
打开浏览器console面板,键入 dataObj.one = "路飞"; dataObj.two = "索隆"; 就可以看到页面发生的变化了。
这里说一下我遇到的问题,Uncaught TypeError: Object.defineProperty called on non-object
报错原因是,给Object.defineProperty传参的数据类型不对,
最后在html里面,发现dataObj定义的是字符串,而不是对象,这里是需要一个对象类型的。