標籤:執行 dom操作 log 寫法 動態綁定 印象 觀察 訂閱 api
Vue.js,其簡潔的API以及活躍的社區,對於打算從angular轉向Vue還是挺友好的,打算最近一段時間去整理下Vue自己的一些思考,加深下對於此的印象。
Vue與Angular同屬於MVVM架構,MVVM的本質是通過資料繫結連結View和Model,讓資料的變化自動對應為視圖的更新。Vue.js在資料繫結的API設計上借鑒了Angular的指令機制:使用者可以通過具有特殊首碼的HTML 屬性來實現資料繫結,也可以使用常見的花括弧模板插值,或是在表單元素上使用雙向繫結:
<!-- 指令 --><span v-text="msg"></span><!-- 插值 --><span>{{msg}}</span><!-- 雙向繫結 --><input v-model="msg">
插值本質上也是指令,只是為了方便模板的書寫。在模板的編譯過程中,Vue.js會為每一處需要動態更新的DOM節點建立一個指令對象。每當一個指令對象觀測的資料變化時,它便會對所綁定的目標節點執行相應的DOM操作。基於指令的資料繫結使得具體的DOM操作都被合理地封裝在指令定義中,業務代碼只需要涉及模板和對資料狀態的操作即可,這使得應用的開發效率和可維護性都大大提升。
與Angular不同的是,Vue.js的API裡並沒有繁雜的module、controller、scope、factory、service等概念,一切都是以“ViewModel 執行個體”為基本單位:
<!-- 模板 --><div id="app"> {{msg}}</div>
// 原生對象即資料var data = { msg: ‘hello!‘}// 建立一個 ViewModel 執行個體var vm = new Vue({ // 選擇目標元素 el: ‘#app‘, // 提供初始資料 data: data})
渲染結果:
<div id="app"> hello!</div>
在渲染的同時,Vue.js也已經完成了資料的動態綁定:此時如果改動data.msg的值,DOM將自動更新。是不是非常簡單易懂呢?除此之外,Vue.js對自訂指令、過濾器的API也做了大幅的簡化,如果你有Angular的開發經驗,上手會非常迅速。
資料觀測的實現
Angular的資料觀測採用的“髒值檢查機制”,每一個指令都會有一個對應的用來觀察到對象的變化,這個應用叫做watcher,一個範圍中可能會有多個watcher,當資料發生變化,頁面更新,Angular會遍曆當前頁面中所有的watcher,對他們一一求值,與之前的值進行對比,如果求值的結果變化了,就會觸發對應的更新,這個過程叫做digest cycle。髒值檢查的劣勢也就有兩點:
- 任何資料變動都意味著當前範圍的每一個watcher需要被重新求值,因此當watcher的數量龐大時,應用的效能就不可避免地受到影響,並且很難最佳化。
- 當資料變動時,架構並不能主動偵測到變化的發生,需要手動觸發digest cycle才能觸發相應的DOM 更新。Angular通過在DOM事件處理函數中自動觸發digest cycle部分規避了這個問題,但還是有很多情況需要使用者手動進行觸發,這一點很容易在初學者出現問題,不知道什麼情況。
Vue.js採用的則是基於依賴收集的觀測機制。也有一說法,叫做資料劫持,用到了ES5的Object.defineProperty(),所以它只能相容到IE9之後的環境。它的基本原理是:
- 將原生的資料改造成 “可觀察對象”。一個可觀察對象可以被取值,也可以被賦值。
- 在watcher的求值過程中,每一個被取值的可觀察對象都會將當前的watcher註冊為自己的一個訂閱者,並成為當前watcher的一個依賴。
- 當一個被依賴的可觀察對象被賦值時,它會通知所有訂閱自己的watcher重新求值,並觸發相應的更新。
- 依賴收集的優點在於可以精確、主動地追蹤資料的變化,不存在上述提到的髒檢查的兩個問題。但傳統的依賴收集實現,比如Knockout,通常需要包裹原生資料來製造可觀察對象,在取值和賦值時需要採用函數調用的形式,在進行資料操作時寫法繁瑣,不夠直觀;同時,對複雜嵌套結構的對象支援也不理想。
Vue.js與angular在資料實現的思考