vue是一套用於構建用戶界面的漸進式框架。與其它大型框架不同的是,Vue 被設計爲可以自底向上逐層應用。Vue 的核心庫只關注視圖層,不僅易於上手,還便於與第三方庫或既有項目整合。
vue是一個MVVM框架,MVVM是一個MVC框架的改進版,由model-view-viewModel三塊組成,由viewModel來是實現數據(model)和視圖(view)之間的通信。
生命週期
生命週期中有多個事件鉤子,讓我們在控制整個Vue實例的過程時更容易形成好的邏輯。
beforeCreate、created、beforeMounted、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
底層實現原理
vue是一個MVVM的單向數據流,數據雙向綁定的框架,2.0的雙向數據綁定是由Object.defineProperty來實現,3.0是由Object.proxy來實現。
具體實現流程:
- Observer:核心是通過Obeject.defineProperty()來監聽數據的變動,這個函數內部可以定義setter和getter,每當數據發生變化,就會觸發setter,進行數據劫持,然後通知訂閱者,訂閱者就是Watcher。
-
Watcher:訂閱者作爲Observer和Compile之間通信的橋樑。在收到屬性變化的通知後,調用自身的更新函數,觸發Compile中綁定的回調函數。
-
Compile:主要做的事情是解析模板指令,將模板中的變量替換成數據,然後初始化渲染頁面視圖,並將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變動,收到通知,更新視圖。
虛擬 DOM
虛擬DOM本質上就是一個普通的JavaScript對象,裏面能很好的表示DOM元素需要記錄的信息:節點名稱、屬性,文本,子節點等。比如下:
{ // 節點類型 type: 'ul', // 節點的屬性,包括dom原生屬性和自定義屬性 props: { class: 'list', style: 'color:red;' }, // 子節點數組 // 子對象結構也是一樣,包含了type,props,children,沒有子節點的話就是普通文本 // 子對象擁有子節點時,繼續往下擴展就行 children: [ {type: 'li',props: {class: 'list'},children: ['利羣']}, {type: 'li',props: {class: 'list'},children: ['玉溪']}, {type: 'li',props: {class: 'list'},children: ['黃鶴樓']} ] }
虛擬DOM的必要性:
virtual-dom
(後文簡稱vdom
)的概念大規模的推廣還是得益於react
出現,virtual-dom
也是react
這個框架的非常重要的特性之一。相比於頻繁的手動去操作dom
而帶來性能問題,vdom
很好的將dom
做了一層映射關係,進而將在我們本需要直接進行dom
的一系列操作,映射到了操作vdom
,而vdom
上定義了關於真實dom
的一些關鍵的信息,vdom
完全是用js
去實現,和宿主瀏覽器沒有任何聯繫,此外得益於js
的執行速度,將原本需要在真實dom
進行的創建節點
,刪除節點
,添加節點
等一系列複雜的dom
操作全部放到vdom
中進行,這樣就通過操作vdom
來提高直接操作的dom
的效率和性能。
Virtual DOM 算法實現的大致邏輯:
- 用JavaScript對象結構DOM樹的結構,然後用這個結構構建成一個真正的DOM樹,並渲染到文檔中去。
- 當JavaScript對象發生變化,則重新構建一顆新的對象樹,然後比較兩個對象樹,記錄兩棵樹之間的差別。
- 把記錄的差別應用到步驟1所構建的DOM樹上,完成更新。
watcher與computed的區別
watcher:一個值影響多個值;監聽已有屬性;允許異步操作
computed:多個值影響一個值;生成一個新的屬性;不允許異步操作;必須有依賴型數據;當依賴數據發生變化纔會重新計算,否則用緩存;內部有getter和setter方法。
created與mounted的區別
主要的區別在於是否有渲染DOM樹。
- beforeCreate:DOM節點、data、methods都不能獲取。
- created:DOM節點不能獲取,data、methods可以獲取。
- beforeMounted:DOM節點不能獲取,data、methods可以獲取。
- mounted:DOM節點、data、methods都可以獲取。
v-for與v-if爲什麼避免一起使用
v-for的優先級高於v-if,所以v-if會執行在每一個v-for的子元素中,從而降低性能。
解決方法:
- 將v-if置於v-for的上層
- 將需要循環的屬性通過computed進行過濾,然後v-for循環computed屬性。
循環指令中,key值的作用
主要作用是用來提高虛擬DOM更新的效率,是在diff算法判斷中,用來判斷兩個節點是否相同的一個很重要的標準。
組件間的通信
- 父傳子props,v-model,子傳父emit
- 全局vuex,eventBus(Vue.prototype.bus = new Vue())
vue.use的用途及原理
use方法是用來擴展插件的。源碼如下(src/core/global-api/use.js):
export function initUse (Vue: GlobalAPI) { Vue.use = function (plugin: Function | Object) { const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) // 如果當前plugin已經被use調用過了,則直接返回 if (installedPlugins.indexOf(plugin) > -1) { return this } // additional parameters const args = toArray(arguments, 1) args.unshift(this) // 如果plugin對象中有install方法,則執行 if (typeof plugin.install === 'function') { plugin.install.apply(plugin, args) // 如果plugin本身就是方法,則執行 } else if (typeof plugin === 'function') { plugin.apply(null, args) } // 最後再將plugin緩存起來 installedPlugins.push(plugin) return this } }
vue.$nextTick方法的用途及原理
以下爲vue源碼(src/core/util/next-tick.js):
let timerFunc // 如果有Promise且是原生API,則選用Promise(微任務) if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true // 如果支持MutationObserver,則用MutationObserver(MutationObserver是用來監聽DOM變化的API,爲微任務) } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true // 如果支持setTmmediate,則用setTmmediate(可以看成setTimeout,兼容性不好,僅IE10+及nodejs支持,爲宏任務) } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. // 最後的應急方案,setTimeout方法(宏任務) timerFunc = () => { setTimeout(flushCallbacks, 0) } }
上面代碼分析得出,在vue被實例化執行的時候,首先判斷微任務(Promise > MutationObserver)是否支持,然後再判斷宏任務(setImmediate)是否支持,最後採用setTimeout方法。具體之間的區別可以查下JavaScript的事件循環,微任務與宏任務的區別。
mixin
Mixins 使我們能夠爲 Vue 組件編寫可插拔和可重用的功能。如果我們希望在多個組件間重用一組選項,比如data、computed、methods等,那麼我們可以將其寫成mixin,並在需要的地方引用它,然後通過mixin合併到當前組件。如果一個組件中有引入mixin,那麼mixin中的生命週期hook將優先組件自己內的hook。
vuex
Vuex 是一個專爲 Vue.js 應用程序開發的響應式狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
規則:
- 應用層級的狀態應該集中到單個 store 對象中。
- 提交 mutation 是更改狀態的唯一方法,並且這個過程是同步的。
- 異步邏輯都應該封裝到 action 裏面。
5個核心概念:
- state:單一的狀態樹對象,包含了全部的應用層級狀態。可以簡單的理解爲整個狀態管理的倉庫地址,可以設置內部所有屬性的初始值,並且整個store狀態是響應式的,每當state內的屬性值改變,監視的vue組件也會同步更新。
- mutation:提交 mutation是更改 Vuex 的 store 中的狀態的唯一方法,它是一個同步函數。
- action:類似與mutation,不同的是action不能直接更改state,要通過提交mutation來更改。在action內可以執行任何異步任務,也可以通過dispath調用另一個action任務。
- module:當項目很複雜的時候,我們可以使用module來進行模塊劃分。每個module內部都有自己的state,mutation,action,getter、對於模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象、action內部使用的根節點爲context.rootState、getter使用的根節點爲第三個參數。默認情況下,模塊內部的state是註冊在全局命名空間的,如果有使用namespaced爲true,那麼在引用的時候,需要在前面加上模塊名稱,如(this.$store.dispath('moduleName/actionFunction()'))。
- getter:可以認爲爲store的計算屬性,通過它可以對state狀態的值進行重新計算緩存並返回。
數據傳輸流程:
- 在實例化Vuex.Store()的時候,創建一個全局倉庫state,並將內部屬性進行數據劫持。
- state內部數據需要修改的時候需要遵循單項數據流,在組件中通過dispath方法調用action方法。
- action內部方法處理完成後,然後再調用commit方法通知mutation。
- mutation內部方法用來修改state中的數據。
- state內部數據更改完成,因其數據爲響應式,則組件中依賴的值也會相應變化
未完待續......