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部分的兩個連接this
new Vue()
對data進行初始化,對data
中屬性進行屬性劫持.net
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
code
set
先修改msg
的值msg
依賴隊列中的全部watcher
的函數,即vText
,頁面的innerText
被同步更新總之幾者的關係就是在observer
的get
中將對當前屬性的watcher
收集進dep
,在observer
的set
中執行收集到的watcher
。
而vue的真正的執行過程毫不是上面寫的這麼簡單,好比watcher的執行就毫不是簡單的遍歷執行,並且還對observer進行了很大程度的簡化。咱們還省略了諸如_proxy
、defineReactive
等出現頻率較高的函數。寫這樣一個最簡實現主要是爲了梳理一下主幹,下降閱讀源碼的難度。