Vue.js 是一款 MVVM 框架,数据模型仅仅是普通的 JavaScript 对象,但是对这些对象进行操作时,却能影响对应视图,它的核心实现就是「响应式系统」。
在 Vue 中,数据模型下的所有属性,会被 Vue 使用Object.defineProperty(Vue3.0 使用 Proxy)进行数据劫持代理。
响应式的核心机制是观察者模式,数据是被观察的一方,一旦发生变化,通知所有观察者,这样观察者可以做出响应,比如当观察者为视图时,视图可以做出视图的更新。
Vue.js 的响应式系统以来三个重要的概念,Observer、Dep、Watcher.
Object.defineProperty()
我觉得先要从认识这个函数开始,何为Object.defineProperty() ? 这个方法它会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
它有三个参数
obj: 要在其上定义的属性的对象,说白了就是你要操作对象
prop: 对象上的你需要修改的属性名称。
descriptor: 将被定义或修改的属性描述符。
例子:
let obj = {}
obj.singer = '周杰伦'
// obj: 目标对象
// prop: 需要操作的目标对象的属性名
// descriptor: 描述符
Object.defineProperty(obj, 'music', {
// value: '刘德华',
// writable : false,
configurable: true, // 可以配置对象,删除属性
enumerable: true, // 是否可以枚举
// get,set设置时不能设置writable和value,它们代替了二者且是互斥的
get () {
return '青花瓷'
},
set (val) {
console.log('视图更新');
}
})
运行后,到浏览器的控制台中输入
obj
/* 打印
music: "青花瓷"
singer: "周杰伦"
get music: ƒ ()
set music: ƒ (val)
__proto__: Object
*/
当我们尝试更改数据,可以看到调用到了 set() 方法.
obj.music = '稻香'
/*
视图更新
"稻香"
*/
热身结束。我们开始下个阶段。
响应式系统
思路: -> 获取数据 -> 劫持数据 -> 绑定监听方法
// 调用它即代表更新视图
function cb(val) {
console.log('视图更新了');
}
// 我们的 obj 的 key 属性在「读」的时候会触发 reactiveGetter 方法,
// 而在该属性被「写」的时候则会触发 reactiveSetter 方法。
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true, /* 属性可枚举 */
configurable: true, /* 属性可被修改或删除 */
get: function reactiveGetter() {
return val; /* 实际上会依赖收集 */
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return
};
cb(newVal);
}
})
}
Observe 扮演的角色是发布者,他的主要作用是在组件初始化的时,调用defineReactive函数,使用Object.defineProperty方法对对象的每一个子属性进行数据劫持/监听,即为每个属性添加getter和setter,将对应的属性值变成响应式。
/*
函数传入一个 value(需要「响应式」化的对象),
通过遍历所有属性的方式对该对象的每一个属性都通过 defineReactive 处理。
*/
function observer(value) {
// 当 val 为空的时候或者 val 不等于一个对象的时候,结束这次操作
if (!value || typeof value !== 'object') {
return
}
Object.keys(value).forEach((key) => {
defineReactive(value, key, value[key]);
})
}
<!--模拟数据-->
constructor(options) {
this._data = options.data;
observer(this._data);
}
最后在页面中调用
let o = new Vue({
data: {
name: 'Hello Word',
age: '18',
hobby: 'PC Game'
}
})