Vue的主要原理中主要用到了定義的這麼幾個函數Dep,Watcher,observer。 咱們來使用這幾個函數簡單的實現一下vue構造函數數據綁定和相互依賴部分,梳理一下它們之間的關係。 省略了編譯部分和proxy代理與其餘的一些複雜邏輯。html
Dep是依賴類,簡要實現爲vue
class Dep {
constructor () {
// 放當時屬性的觀察者
this.subs = []
}
}
// target 用來掛載當時的watcher觀察者
Dep.target = null
複製代碼
作屬性劫持,並作點其餘事情git
function observer (vm, key, val) {
let dep = new Dep()
Object.defineProperty(vm, key, {
/** * get主要作兩個事情 * 1. 收集觀察當前key的wathcer(即依賴當前key的操做) * 2. 獲取值 */
get () {
// 這是做用1
if (Dep.target) {
dep.subs.push(Dep.target)
}
// 這是做用2
return val
},
/** * set也是兩個事情 * 1. 修改目標值 * 2. 執行依賴當前key的watcher */
set (newVal) {
// 這是做用1
val = newVal
// 這是做用2
for(cb of dep.subs) {
cb.call(vm)
}
}
})
}
複製代碼
Watcher是觀察者類,用來建立依賴某屬性的操做(如指令,渲染,計算屬性等)github
class Watcher {
/** * vm: 實例 * cb: 依賴某屬性的操做函數 */
constructor (vm, cb) {
// 把當前的操做掛載到Dep上
Dep.target = cb
/** * 執行操做,兩個做用 * 1. 進行操做的初始化 * 2. 觸發屬性的get方法,使當前cb被收集 */
cb.call(vm)
Dep.target = null
}
}
複製代碼
那麼咱們就使用上面定義好的函數寫個例子閉包
<div>
<p class="text"></p>
<div>
複製代碼
let vm = new Vue({
// 假設有data
data: {msg: 1},
// 編譯過程當中有某個v-text指令,咱們抽象爲vText函數,依賴屬性msg(此函數也表明全部依賴其餘屬性的操做)
renderFun: {
vText () {
document.querySelector('.text').innerText = this.msg
}
}
})
// 修改vue實例的值,觀察變化
vm.msg = 333
複製代碼
那麼咱們也寫一個vue的簡易構造函數函數
class Vue {
constructor (options) {
let data = options.data
let renderFun = options.renderFun
// initData
Object.keys(data).forEach(key => {
observer(this, key, data[key])
})
// 模擬計算屬性,watcher,指令等依賴屬性的操做
Object.keys(renderFun).forEach(key => {
new Watcher(this, renderFun[key])
})
}
}
複製代碼
完整的代碼能夠看demo部分的兩個連接ui
new Vue()
data
中屬性進行屬性劫持
get
進行觀察者watcher
的收集和值得獲取;set
進行值的更新和依賴隊列中watcher
的執行computed\watcher
或模板編譯
過程當中的指令
函數進行初始化,咱們以renderFun
代替renderFun
中的每一個功能函數進行new Watcher()
工做vText
爲例子,在new Wathcer()
過程當中
vText
掛載到全局通用的Dep.target
上vText
,其中有讀vm.msg
的操做,則觸發msg屬性的get,進入Dep.target
判斷,將Dep.target
即vText
收集進msg
的subs
依賴隊列中,此時vText
執行完畢,頁面innetText
被修改Dep.target
置空vm.msg = 333
,則觸發msg
的set
set
先修改msg
的值msg
依賴隊列中的全部watcher
的函數,即vText
,頁面的innerText
被同步更新總之幾者的關係就是在observer
的get
中將對當前屬性的watcher
收集進dep
,在observer
的set
中執行收集到的watcher
。this
而vue的真正的執行過程毫不是上面寫的這麼簡單,好比watcher
的執行就毫不是簡單的遍歷執行,並且還對observer
進行了很大程度的簡化。省略了諸如_proxy
、defineReactive
等函數。spa
寫這樣一個最簡實現主要是爲了梳理一下主幹,下降閱讀源碼的難度。.net