/**
* 实现简易版Vue2数据响应
* 思路:
* 通过发布-订阅模式来实现对数据的监听
* 三个重要概念: observer(数据劫持) dep(订阅器) watcher(订阅者)
* observer:
* 通过 Object.defineProperty 来实现对数据的劫持,为数据中的属性添加订阅器,每个属性都会对应一个订阅器,实际就是一个存放订阅者的数组,并在数据改变时通知订阅器内的订阅者们完成响应的回调
* dep:
* 内部实际就是:一个数组结构,一个添加订阅者的方法addSub,一个通知订阅者更新的方法notify
* watcher:
* 每个订阅者代表对数据监听的一个回调
*
*/
/********************劫持数据*********************/
function observer(data) {
if (!data || typeof data !== "object") return data;
Object.keys(data).forEach((key) => defineReactiv(data, key, data[key]));
}
// 数据劫持具体实现
function defineReactiv(data, key, val) {
// 递归调用 对多层数据实现监听
observer(val);
// 一个数据的属性对应一个订阅器实例
var dep = new Dep();
// 数据监听核心 其中的get和set与defineReactiv构成了闭包环境
Object.defineProperty(data, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target); // 在这里添加一个订阅者
}
return val;
},
set(newVal) {
if (val === newVal) return;
val = newVal;
dep.notify();
},
enumerable: true,
configurable: true,
});
}
/********************订阅器*********************/
class Dep {
// 缓存当前订阅者
static target = null;
// 存放订阅者数组
subs = [];
// 构造器
constructor() {}
// 添者订阅者
addSub(sub) {
this.subs.push(sub);
}
// 通知订阅者更新数据
notify() {
this.subs.forEach((sub) => sub.update());
}
}
/********************订阅者*********************/
class Watcher {
callback = null;
vm = null;
key = undefined;
value = undefined;
constructor({ vm, key, callback }) {
this.callback = callback;
this.vm = vm;
this.key = key;
this.value = this.get();
}
// 订阅者执行更新函数
update() {
this.run();
}
// 更新函数具体逻辑
run() {
let value = this.vm.$data[this.key];
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.callback.call(this.vm, value, oldVal);
}
}
// 实例化订阅者时,获取一次数据值
get() {
Dep.target = this;
var value = this.vm.$data[this.key];
Dep.target = null;
return value;
}
}
/********************Vue实现*********************/
class Vue {
// 实例数据
$data = undefined;
// 实例参数
$options = undefined;
// 构造器
constructor(options) {
const { data } = options;
// 数据挂载
this.$data = data;
// 参数挂载
this.$options = options;
// 监听数据
observer(data);
}
$watch(key, callback) {
new Watcher({ vm: this, key, callback });
}
}
/********************测试一下*********************/
// 1. 创建Vue实例
var vm = new Vue({
data: {
name: "小明",
},
});
vm.$watch("name", (newValue, oldValue) => {
// 通过改变 vm.$data.name 来触发此监听
console.log(newValue, oldValue);
});
// 2. 设置实例上的数据
vm.$data.name = "小张"; // 小张 小明
实现简易版Vue
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- HTMl代码结构 js调用代码 vue结构 Watcher 渲染视图的依赖 (局部更新) Vue constru...
- 1、实现插件2、routes选项解析:生成一个Map,把path和component映射起来3、监控url上的ha...
- 完整源代码请移步GitHub: https://github.com/webxing/element_form 要...
- 前言 全局注册组件的优势在于,当某个弹框之类的组件在全局中注册后使用方便,可以自定义组件显示条件,全局任何地方使用...