Vue2响应式原理
this._init -> initState -> initData -> observe(data) -> defineReactive() -> Object.defineProperty -> get方法调用dep.depend -> set方法调用dep.notify
- initData: 初始化data数据,判断data是否是函数,如果是函数调用getData()
- observe(data): 调用new Observer(data),构造函数中遍历Object.keys(data)判断data[key]是否是数组如果是数组调用observeArray,否则调用defineReactive劫持对象的get和set
- Dep.target: Watcher实例
- dep.notify: 通知Watcher实例更新视图
- 源码部分
Vue.prototype._init = function (options) {
// ...
initState(vm);
// ...
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
}
function initState(vm) {
// ...
if (opts.data) {
initData(vm);
}
}
function initData(vm) {
var data = vm.$options.data;
data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
// ...
// observe data
var ob = observe(data);
}
function observe(value, shallow, ssrMockReactivity) {
// ...
return new Observer(value, shallow, ssrMockReactivity);
}
var Observer = /** @class */ (function () {
function Observer(value, shallow, mock) {
// ...
if (isArray(value)) {
// ...
this.observeArray(value);
} else {
// ...
defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);
}
}
Observer.prototype.observeArray = function (value) {
// ...
observe(value[i], false, this.mock);
}
return Observer
}
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
* @internal
*/
var Dep = /** @class */ (function () {
function Dep() {
// ...
this.subs = [];
}
Dep.prototype.addSub = function (sub) {
this.subs.push(sub);
};
Dep.prototype.removeSub = function (sub) {
}
Dep.prototype.depend = function (info) {
if (Dep.target) {
Dep.target.addDep(this);
// ...
}
}
Dep.prototype.notify = function (info) {
// ...
sub.update();
}
return Dep
}
// expOrFn -> mountComponent
var Watcher = /** @class */ (function () {
function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
// ...
this.getter = expOrFn;
// ...
this.value = this.lazy ? undefined : this.get();
}
Watcher.prototype.get = function() {
// ...
value = this.getter.call(vm, vm);
// ...
return value
}
/**
* Add a dependency to this directive.
*/
Watcher.prototype.addDep = function (dep) {
// ...
dep.addSub(this);
}
/**
* Clean up for dependency collection.
*/
Watcher.prototype.cleanupDeps = function () {
// ...
}
Watcher.prototype.update = function () {
// ...
this.run();
}
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
Watcher.prototype.run = function () {
// ...
var value = this.get(); // 更新视图
}
return Watcher
})
/**
* Define a reactive property on an Object.
*/
function defineReactive(obj, key, val, customSetter, shallow, mock) {
var dep = new Dep();
// ...
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// ...
if (Dep.target) {
dep.depend({
target: obj,
type: "get" /* TrackOpTypes.GET */,
key: key
});
}
},
set: function reactiveSetter(newVal) {
// ...
dep.notify({
type: "set" /* TriggerOpTypes.SET */,
target: obj,
key: key,
newValue: newVal,
oldValue: value
})
}
})
}
function mountComponent(vm, el) {
// ...
const updateComponent = function () {
vm._update(vm._render(), hydrating);
}
// ...
new Watcher(vm, updateComponent, noop, watcherOptions, true /* isRenderWatcher */);
// ...
}
Vue.prototype.$mount = function (el, hydrating) {
// ...
return mountComponent(this, el, hydrating);
};
Observe类(数据劫持重写对象get和set方法)
- 遍历数据调用defineReactive方法通过Object.defineProperty劫持数据获取和更新
Dep类(收集依赖)
- 依赖收集,属性subs用于存放Watcher实例
- notify方法调用sub.update通知Watcher实例更新
Watcher类(更新视图)
- $mount(mountComponent)的时候实例化,参数updateComponent用于更新视图
- updateComponent: vm._update(vm._render()) 模版解析和视图更新
dep.target 指向 Watcher实例
Watcher实例通过addDep方法参数dep,调用addSub将当前实例添加到Dep的subs属性