vue作為最受歡迎的前端開發(fā)框架磺芭。非常值得我們傾心研究一番。
讀源碼的動(dòng)力
- 源碼閱讀可以看到作者(前端技術(shù)最頂端的人)對(duì)js的理解
- 可以看到作者優(yōu)秀的設(shè)計(jì)思想
- 可以更加快速的處理和理解我們?cè)谌粘9ぷ鞒霈F(xiàn)的問題
- 提高自己的技術(shù)深度和廣度
Vue響應(yīng)式原理
- 使用Object.defineProperty將data數(shù)據(jù)變成響應(yīng)式對(duì)象醉箕,通過Observer給對(duì)象添加get 和 set屬性
- 調(diào)用對(duì)象數(shù)據(jù)時(shí)會(huì)觸發(fā)getter钾腺,改變對(duì)象數(shù)據(jù)時(shí)會(huì)觸發(fā)setter
- 在getter中進(jìn)行依賴收集(使用到當(dāng)前數(shù)據(jù)的地方會(huì)被作為一個(gè)Watcher對(duì)象處理)
- 在setter中通過Dep進(jìn)行派發(fā)更新(通過處理watcher對(duì)象Dep.target = watcher從而調(diào)用nextTick進(jìn)行數(shù)據(jù)更新)
通過Observer處理響應(yīng)式對(duì)象
- 初始化Vue實(shí)例時(shí)調(diào)用initState(this)
- 在initState(this)函數(shù)中調(diào)用new Observer(vm.$options.data)處理data數(shù)據(jù)
- 在Observer(val)中通過調(diào)用walk()循環(huán)data數(shù)據(jù)并調(diào)用defineReactive(obj, key, val)實(shí)現(xiàn)響應(yīng)式綁定
Vue.prototype._init = function() {
initState(this)
}
function initState(vm) {
new Observer(vm.$options.data)
}
class Observer {
constructor (value) {
this.walk(value)
}
walk (obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
function defineReactive (obj, key, val) {
val = obj[key]
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
return value
},
set: function reactiveSetter (newVal) {
val = newVal
}
})
}
給對(duì)象添加getter和setter屬性
- 利用Object.defineProperty函數(shù)的特性給對(duì)象添加get和set屬性
- 監(jiān)測(cè)getter和setter是否存在,不存在就添加get/set屬性
function defineReactive (obj, key, val) {
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
const getter = property && property.get
const setter = property && property.set
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
return value
},
set: function reactiveSetter (newVal) {
val = newVal
}
})
}
利用Dep在get中進(jìn)行依賴收集
- Dep是Watcher的管理器
- Dep.target就是當(dāng)前的Watcher
- depend()中會(huì)調(diào)用執(zhí)行Dep.target.addDep(this)
- 通過一連串的函數(shù)調(diào)用最終將當(dāng)前的watcher存儲(chǔ)在subs[]中
let childOb = !shallow && observe(val)
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
}
return value
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
addDep (dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
在set中進(jìn)行派發(fā)更新
- set函數(shù)會(huì)在修改當(dāng)前data數(shù)據(jù)時(shí)調(diào)用
- set中會(huì)執(zhí)行dep.notify()函數(shù) -- 派發(fā)更新的重點(diǎn)在notify()中
- notify會(huì)循環(huán)subs得到之前在get中存儲(chǔ)的Watcher并調(diào)用update()
- update中會(huì)執(zhí)行Watcher的run函數(shù)
- run函數(shù)中執(zhí)行this.cb.call(this.vm, value, oldValue)讥裤,最終會(huì)觸發(fā)updateComponent函數(shù)進(jìn)行Dom更新
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()
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
update () {
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
run () {
this.cb.call(this.vm, value, oldValue)
}
Watcher是什么時(shí)候創(chuàng)建的放棒?Watcher是如何觸發(fā)Dom更新的
- 在初始化虛擬Dom時(shí)initVirtualComponent()函數(shù)中會(huì)執(zhí)行 new Watcher(vm, updateComponent, noop, null, true)創(chuàng)建Watcher
- Watcher對(duì)象的cb數(shù)據(jù)updateComponent函數(shù),執(zhí)行this.cb.call(this.vm, value, oldValue)也就是執(zhí)行了updateComponent
- updateComponent()中調(diào)用_update()己英,進(jìn)而更新Dom