Vue虛擬DOM是實現響應式的核心,弄清楚虛擬DOM和Vue響應式原理,對寫代碼有參考作用 ...
虛擬 DOM
Vue 通過建立一個虛擬 DOM 來追蹤自己要如何改變真實 DOM
在Vue中定義虛擬節點(VNode)描述節點信息
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
這裡描述節點文本,標簽信息(tag),真實Dom節點(elm),節點的data信息,子節點,父節點等信息
“虛擬 DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼
從結構可以看到根節點(parent為空)就可以表示整個樹
有了虛擬 DOM ,Vue就會比較差異,更新真實DOM
比較差異是在patch.js裡面的patch方法(補丁)
響應式原理
Vue的響應式大概會經過下麵幾個階段
1. 使用 Object.defineProperty 把屬性全部轉為getter/setter
2. 屬性變更時通知觀察者(watcher)變更
3. watcher觸發重新渲染生成虛擬 DOM
4. Vue框架遍歷計算新舊虛擬 DOM差異
4.1 由於 JavaScript 的限制,Vue 不能檢測數組和對象的變化
5. 載入操作,將差異局部修改到真實 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前面的都是賦值的判斷,
1. 值是否相等,
2. 是否自定義setter函數,
3. 是否只讀
4. 最後一句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類 提供一個訂閱,通知的功能
最後我們看一下訂閱的目標Watcher是做什麼
Watcher最重要的一個方法update
update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } }
轉發請標明出處:https://www.cnblogs.com/WilsonPan/p/12744695.html