1.简介
何为响应式?简单来讲,就是在数据发生变化时,界面会重新渲染。
2.基本原理
发布订阅模式+数据劫持。当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
3.代码实现
首先通过发布订阅模式,先订阅好改变视图的方法;然后通过数据劫持,当数据发生改变时,会触发setter方法,在setter中发布(触发)之前订阅的方法,就会去调用该方法修改视图内容。
<div class="box"></div>
<script>
let obj = {}; // 数据对象
// 订阅器
let Dep = {
eventList: {}, // 事件容器
// 添加订阅
on: function (key, fn) {
(this.eventList[key] || (this.eventList[key] = [])).push(fn);
},
// 发布
emit: function () {
let key = Array.prototype.shift.call(arguments);
let fns = this.eventList[key];
if (!fns || fns.length === 0) {
return false;
}
for (let i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
}
}
// 数据劫持
let dataWatch = function({data, tag, dataKey, selector}) {
let value = '';
let el = document.querySelector(selector);
Object.defineProperty(data, dataKey , {
get: function () {
return value;
},
set: function (val) {
this.value = val;
// 发布
Dep.emit(tag, val);
}
})
// 订阅
Dep.on(tag, function(text) {
el.innerHTML = text;
})
}
dataWatch({
data: obj,
tag: 'view',
dataKey: 'one',
selector: '.box',
})
// 初始化赋值
obj.one = '视图';
</script>
初始状态
值改变时,界面重新渲染