Vue Reactive函数实现流程详解
Vue.js 框架的核心就是数据驱动,同时也是以数据为中心来响应视图变化。然而,Vue.js 还支持响应式,因此当数据发生变化时,Vue.js 自动更新视图。
在 Vue.js 中,通过 getter 和 setter 函数来实现数据的响应式。
实现流程
Vue.js 的实现响应式的方式,是通过劫持数据对象的属性来达到的。在对象的 getter 方法中,进行依赖收集;在对象的 setter 方法中,进行依赖触发。
Observer
Vue.js 中通过 Observer 类来实现数据对象属性的劫持,Observer 的作用是将一个正常的 JavaScript 对象转化为一个响应式的对象。
Observer 类的主要工作是深度遍历所有属性,将它们变成响应式属性(即变为 getter 和 setter 函数)。
代码示例:
class Observer {
constructor(value) {
this.value = value;
if (!Array.isArray(value)) {
this.walk(value);
}
}
// 遍历所有属性并进行监测
walk(obj) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
}
function defineReactive(obj, key) {
let val = obj[key];
// 递归实现深度遍历响应式设置
const childOb = observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log(`访问${key}`);
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
console.log(`设置${key}的新值为${newVal}`);
childOb && observe(newVal);
},
});
}
function observe(value) {
if (typeof value !== "object") {
return;
}
return new Observer(value);
}
Dep
Dep 类用于收集依赖和通知更新。在 defineReactive 函数中,每个属性的 getter 函数都会在对应的 dep 实例调用 depend() 方法进行依赖收集,并且每个属性的 setter 函数都会在 dep 派发通知,通知每个依赖进行更新。
在 dep 类中,定义了 dep 实例的 depend() 和 notify() 方法。
代码示例:
let watchId = 0;
// define a watcher class
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm;
this.cb = cb;
this.options = options;
this.id = ++watchId;
// apply options
if (options) {
this.deep = !!options.deep;
this.user = !!options.user;
}
// get the expression
if (typeof expOrFn === "function") {
this.expOrFn = expOrFn;
} else {
this.getter = parsePath(expOrFn);
}
// trigger the watcher's initial update
if (this.options.immediate) {
this.cb();
} else {
this.value = this.get();
}
}
get() {
Dep.target = this;
const value = this.getter
? this.getter(this.vm)
: this.expOrFn(this.vm);
Dep.target = undefined;
return value;
}
update() {
const oldValue = this.value;
const value = this.get();
if (this.deep) {
traverseoldValue(oldValue);
traversevalue;
}
this.value = value;
this.cb.call(this.vm, value, oldValue);
}
}
let uid = 0;
// define dep class
class Dep {
constructor() {
this.id = uid++;
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
removeSub(sub) {
remove(this.subs, sub);
}
depend() {
if (Dep.target) {
Dep.target.addDep(this);
}
}
notify() {
const subs = this.subs.slice();
for (let i = 0; i < subs.length; i++) {
subs[i].update();
}
}
}
Dep.target = null;
function traverse(value) {
_traverse(value, new Set());
}
function _traverse(value, visited) {
const isA = Array.isArray(value);
if (!isA && typeof value !== "object") {
return;
}
if (value.__ob__) {
const depId = value.__ob__.dep.id;
if (visited.has(depId)) {
return;
}
visited.add(depId);
}
if (isA) {
let i = value.length;
while (i--) _traverse(value[i], visited);
} else {
const keys = Object.keys(value);
let i = keys.length;
while (i--) _traverse(value[keys[i]], visited);
}
}
示例说明
示例一
const vm = {
name: "Vue",
author: {
name: "Evan",
age: 26,
},
};
observe(vm);
vm.author.age = 30;
以上示例中,我们定义了一个 data 对象,包含一个嵌套的对象,调用 observe 函数将 vm 对象变成响应式对象。然后修改嵌套对象的属性 age 的值,将值改为 30,此时触发了 setter 函数,并对 watcher 数组中的依赖进行通知更新。
示例二
const vm = {
list: [1, 2, 3],
};
observe(vm);
vm.list.push(4);
以上示例中,我们定义了一个 data 对象,包含数组 list,调用 observe 函数将 vm 变成响应式对象。然后对数组 list 进行 push 操作,并没有调用任何方法来触发依赖更新,此时依赖应当也进行了更新。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue reactive函数实现流程详解 - Python技术站