“ 自己动手实践,就会更加深刻的理解
前面的几篇文章,大致解决了从模板与数据到虚拟节点,再到渲染至页面上这一过程,接下来的几篇,着手解决响应式,即数据发生修改,页面随即更新的功能。
我们都知道在 Vue 2.x 版本中,数据的双向绑定是靠 Object.defineProperty()来实现的。
在 3 的版本中使用的 Proxy来代理。毫无疑问,proxy实现起来更简单。本着学习的目的,我们还是使用前者来学习实现吧。至于后者,以后有时间总会尝试的!
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
/** * 对与响应式数组,我们需要重写其一些方法,例如 push、pop * 保证响应式数组使用这些方法之后依然是响应式的。 */
const arrProto = Array.prototype;
const arrMethods = Object.create(arrProto);
function Observer(value) {
this.value = value; def(value, '__ob__', this); // 用来标志响应式
if (Array.isArray(value)) {
value.__proto__ = arrMethods;
this.observeArray(value);
} else {
this.walk(value);
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
Observer.prototype.walk = function (obj) {
for (const key in obj) {
defineReactive(obj, key, obj[key]);
}
}
/**
* Observe a list of Array items.
*/
Observer.prototype.observeArray = function (items) {
for (const item of items) {
observe(item);
}
}
对于一个对象,遍历其键值对,并对每个键值对调用 defineReactive 方法:
/**
* Define a reactive property on an Object.
* 这里使用到了闭包
*/
function defineReactive(obj, key, val) {
observe(val);
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get: function reactiveGetter() {
console.log(`get ${key}: ${val}`);
return val
},
set: function reactiveSetter(newValue) {
console.log(`set ${key} from ${val} to ${newValue}`);
val = newValue;
observe(val); // 对于直接给数组复制的情况需要添加响应式
},
})
}
observe:将数据转换为响应式的入口函数,判断是否已经为响应式,若不是,则新创建一个 Observer 对象,如果是,则直接返回响应式。
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function observe(obj) {
if (!isObject(obj)) {
return
}
if (obj.hasOwnProperty('__ob__')) {
// 如果已经是响应式
return obj.__ob__;
} else {
return new Observer(obj);
}
}
原始数据:
const data = {
name: 'romeo',
message: 'wants to be rich',
deep: {
firstLevel: {
secondLevel: 'here!'
}
},
arr: [
{ name: 'jack1' },
{ name: 'jack2' },
{ name: 'jack3' },
]
}
下期预告:添加数组的一系列方法~