Vue相應式道理
看過vue官方文檔的同硯,對這張圖應當已然相稱熟習了。
vue的相應式是怎樣完成的?
聽過太多回覆,經由歷程Object.defineProperty
,然則再細緻的問時,對方渾然不知。
先擼為敬
const Observer = function(data) {
for (let key in data) {
defineReactive(data, key);
}
}
const defineReactive = function(obj, key) {
const dep = new Dep();
let val = obj[key];
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log('in get');
dep.depend();
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
dep.notify();
}
});
}
const observe = function(data) {
return new Observer(data);
}
const Vue = function(options) {
const self = this;
if (options && typeof options.data === 'function') {
this._data = options.data.apply(this);
}
this.mount = function() {
new Watcher(self, self.render);
}
this.render = function() {
with(self) {
_data.text;
}
}
observe(this._data);
}
const Watcher = function(vm, fn) {
const self = this;
this.vm = vm;
Dep.target = this;
this.addDep = function(dep) {
dep.addSub(self);
}
this.update = function() {
console.log('in watcher update');
fn();
}
this.value = fn();
Dep.target = null;
}
const Dep = function() {
const self = this;
this.target = null;
this.subs = [];
this.depend = function() {
if (Dep.target) {
Dep.target.addDep(self);
}
}
this.addSub = function(watcher) {
self.subs.push(watcher);
}
this.notify = function() {
for (let i = 0; i < self.subs.length; i += 1) {
self.subs[i].update();
}
}
}
const vue = new Vue({
data() {
return {
text: 'hello world'
};
}
})
vue.mount(); // in get
vue._data.text = '123'; // in watcher update /n in get
這裏我們用不到100行的代碼,完成了一個淺易的vue相應式。固然,這裏假如不斟酌時期的歷程,我置信,40行代碼以內能夠搞定。然則我這裏不想省略,為何呢?我怕你把个中的歷程自動疏忽掉,怕他人問你相干東西的時刻,明顯本身看過了,卻被懟的張口結舌。總之,我是為了你好,多喝熱水。
Dep的作用是什麼?
依靠收集器,這不是官方的名字蛤,我本身起的,為了好記。
用兩個例子來看看依靠收集器的作用吧。
例子1,毫無意義的襯着是否是沒必要?
const vm = new Vue({ data() { return { text: 'hello world', text2: 'hey', } } })
當
vm.text2
的值發作變化時,會再次挪用render
,而template
中卻沒有運用text2
,所以這裏處置懲罰render
是否是毫無意義?針對這個例子還記得我們上面模仿完成的沒,在
Vue
的render
函數中,我們挪用了本次襯着相干的值,所以,與襯着無關的值,並不會觸發get
,也就不會在依靠收集器中添加到監聽(addSub
要領不會觸發),縱然挪用set
賦值,notify
中的subs
也是空的。OK,繼承回歸demo,來一小波測試去印證下我說的吧。const vue = new Vue({ data() { return { text: 'hello world', text2: 'hey' }; } }) vue.mount(); // in get vue._data.text = '456'; // nothing vue._data.text2 = '123'; // in watcher update /n in get
例子2,多個Vue實例援用同一個data時,關照誰?是否是應當倆都關照?
let commonData = { text: 'hello world' }; const vm1 = new Vue({ data() { return commonData; } }) const vm2 = new Vue({ data() { return commonData; } }) vm1.mount(); // in get vm2.mount(); // in get commonData.text = 'hey' // 輸出了兩次 in watcher update /n in get
老例子,本身代入進去嘗嘗。
願望經由歷程這兩個例子,你已也許清晰了Dep
的作用,有無原來就那麼回事的覺得?有就對了。總結一下吧(以下依靠收集器實為Dep
):
-
vue
將data
初始化為一個Observer
並對對象中的每一個值,重寫了个中的get
、set
,data
中的每一個key
,都有一個自力的依靠收集器。 - 在
get
中,向依靠收集器添加了監聽 - 在mount時,實例了一個
Wathcer
,將收集器的目的指向了當前Watcher
- 在
data
值發作變動時,觸發set
,觸發了依靠收集器中的一切監聽的更新,來觸發Watcher.update