虛擬 DOM
Vue 通過建立一個(gè)虛擬 DOM 來追蹤自己要如何改變真實(shí) DOM
在Vue中定義虛擬節(jié)點(diǎn)(VNode)描述節(jié)點(diǎn)信息
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
這里描述節(jié)點(diǎn)文本间校,標(biāo)簽信息(tag)谤专,真實(shí)Dom節(jié)點(diǎn)(elm)揍魂,節(jié)點(diǎn)的data信息物独,子節(jié)點(diǎn)重荠,父節(jié)點(diǎn)等信息
“虛擬 DOM”是我們對(duì)由 Vue 組件樹建立起來的整個(gè) VNode 樹的稱呼
從結(jié)構(gòu)可以看到根節(jié)點(diǎn)(parent為空)就可以表示整個(gè)樹
有了虛擬 DOM 栋豫,Vue就會(huì)比較差異挤安,更新真實(shí)DOM
比較差異是在patch.js里面的patch方法(補(bǔ)丁)
響應(yīng)式原理
- 使用 Object.defineProperty 把屬性全部轉(zhuǎn)為getter/setter
- 屬性變更時(shí)通知觀察者(watcher)變更
- watcher觸發(fā)重新渲染生成虛擬 DOM
- Vue框架遍歷計(jì)算新舊虛擬 DOM差異
4.1 由于 JavaScript 的限制笼才,Vue 不能檢測(cè)數(shù)組和對(duì)象的變化 - 加載操作漱受,將差異局部修改到真實(shí) DOM
Vue源碼解讀
//截取部分代碼
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
setter最后一句dep.notify(),dep是什么類型骡送,這里看都猜到是通知昂羡,具體定義
const dep = new Dep()
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
可以看到,Dep類 提供一個(gè)訂閱摔踱,通知的功能
最后我們看一下訂閱的目標(biāo)Watcher是做什么
Watcher最重要的一個(gè)方法update
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}