Vue的組件和數據流管理

1、靈活的Vue

1. 高效的追蹤機制

      Vue經過Object.defineProperty定義數據的存取描述符(set和get),來追蹤數據變化。css

      在Vue組件初始化時,data下的屬性會被循環遍歷添加上set和get,直到全部的子孫屬性都被添加完。當data的某一屬性值發生了變化(執行set)時,它就會向訂閱了該數據的組件發佈通知,去更新組件。前端

      在set裏,組件能夠訂閱該數據變化的通知。vue

      set和get叫對象的存取描述符,與存取描述符對應的叫數據描述符。兩者不能共存,默認狀況下,是數據描述符。以下: react

      Vue在初始化的時候,會刪掉數據描述符,加上存取描述符。這種方式追蹤數據變化,比React更高效。

      React用比較引用來追蹤數據變化的方式。在setState的時候,React會用新的state直接替換掉舊state。webpack

      即使不改變數據的數值屬性,只要數據的引用地址發生變化了,React組件也會觸發更新。若是不加處理,React組件會多不少次更新。

      以下面,每次執行handleClick,即使沒有改變數據的數值,(但引用地址發生變化了),組件也會從新渲染。這個渲染是不必的。ios

      爲了不組件的這種沒意義的渲染,React推出了PureComponent(純組件),幫助開發者作一層數據的淺比較,僅引用地址發生變化的,就不必從新渲染了。開發的時候,還能夠攔截shouldComponentUpdate生命週期鉤子,顯式地干預是否須要從新渲染組件。

      在獲得通知數據變化時,React和Vue都採用了vDOM diff算法,各自去更新組件。在追蹤數據變化上,Vue比React更加高效。es6

2. 使人尷尬的v-model

      Vue沒法檢測對象屬性的增刪變化和數組索引長度的變化,在定義object類型的數據時,通常把它下面的屬性也定義了。可是,JS是一門動態語言,能夠不預先定義對象,而且任意地操做對象的屬性。結合v-model使用時,object類型數據的屬性也能夠不用預先定義。web

      例如,在表單型的組件裏,一個常見的行爲,不給formData定義title、type等這些屬性,v-model會自動響應用戶輸入,並添加屬性到formData上。ajax

<template>
    <div>
        <input v-model="formData.title" placeholder="請輸入標題" />
        <select v-model="formData.type">
            <option value="1">類別</option>
        </select>
    </div>
</template>
data() {
    return {
        formData: {}
    }
}
複製代碼

      props是組件間自上向下通訊數據的方式,子組件逆向修改props,Vue會警告應當避免直接操做props,用data或者computed屬性代替,修改值也不會被傳遞到父組件。算法

      然而,藉助v-model,object類型props值,也能夠由子組件傳遞給父組件。簡單類型的props值仍舊沒法修改。

      原來,v-model內部檢測到綁定的是對象的key時(indexOf('.') > 0),會調用set方法,更新屬性到對象上,並給組件添加了訂閱事件。

Vue1.0採用的是雙向數據綁定,Vue2.0採用的是單向數據流,即只能父對子通訊,子對父通訊要用回調函數的方式。 Vue3.0之前沒法檢測到數據屬性的增刪變化,經常使用Vue.prototype.$set和JSON.parse(JSON.stringify)去糾正這一點。將來Vue3.0將用proxy代替Object.defineProperty監聽數據變化,proxy將直接監聽對象,而不是監聽屬性,它能夠檢測到數組和對象的增刪變化。

3. 做用域問題

      JS開發有三大難點,原型、閉包和做用域。在es6 module的幫助下,原型和閉包的坑已經很少了,剩下的做用域成了最常遇到的坑。

      在React當中,給組件添加事件必需要修正函數的執行做用域。像下面這樣,onClick是定義在全局做用域上的,它的this就是undefined。由於js是靜態做用域語言,它做用域是定義時肯定的。在這裏,要拿到React組件實例內部的state,全部bind函數的做用域到組件實例上。

      在每個事件函數上,不厭其煩地綁定函數的做用域,這在Vue裏是根本不care的。Vue會在組件實例化的時候,把methods裏的函數做用域都綁定到組件實例上

      CSS的做用域問題,也是困擾前端開發者的大問題。命名衝突的className會帶來意想不到的驚喜,採用提高權重的方式,能夠提升選擇器的競爭力,但這會讓項目更加臃腫,CSS管理混亂不堪,維護開發很是麻煩。

      Vue採用相似shadow DOM的方式對CSS進行封裝,添加了scoped屬性後,CSS只做用在組件內部,組件之間的CSS不會互相影響。

      React採用css in js、css module、style-component的方式封裝CSS,都沒有Vue好用。

      React的css in js寫法

4. Vue的靈活性

      相比React,Vue和JS這門語言契合度是最高的,它沒有科班化的數據流管理,沒有刀耕火種的JS編寫方式。

      Vue的追蹤數據機制、v-model雙向綁定和js/css做用域,都很巧妙的利用JS做爲一門動態語言的優點。這使Vue成爲一門很是容易上手的技術框架,在快節奏、頻繁的迭代的開發需求中佔有一席之地。我在商業產品部一年多一共開發和維護了9個項目,包括1個react、1個angular和7個vue項目。Vue很是容易上手,有些簡單的需求,就讓後端同窗代勞了。

      一直以來,都有一個爭議點,Vue適合作小型項目,React適合開發大型項目。之前,Vue飽受詬病的是數據流管理,實際上,如今Vue2和React已經相差不大了,藉助v-model和vuex,Vue甚至比React更勝一籌。將來Vue3會用Typescript寫,構建項目將會更加穩健。

二. 5種數據流管理方式

1. props + emit回調

react和vue都在用的組件通訊方式之一,簡約又簡單。

依賴組件父子關係。

以下,子組件實例化過程當中,若是發現父組件訂閱了子組件的事件,就會把訂閱的事件添加到events列表裏,以此容許開發者來發布事件,即$emit事件。

添加訂閱者到event隊列。

若是是祖孫級組件和兄弟級組件,它們之間的通訊就須要不少個emit回調在組件之間傳遞。這種通訊方式會組件耦合性太強,程序穩定性下降。

2. props + eventBus

適用於全部組件,不依賴組件之間的嵌套關係。

在EventBus裏,實例化一個Vue實例做爲一個觀察者,全部的組件都做爲訂閱者。

// EventBus.js
import Vue from 'vue';

export default new Vue();
複製代碼
// 添加訂閱者
eventBus.$on('reset-preview', this.closeHandler);
// 發佈通知
eventBus.$emit('preview');
複製代碼

Vue內部實現了on和emit兩個方法,on方法添加訂閱者到隊列裏,emit在事件變化時,發佈通知給訂閱者。

EventBus雖然不依賴組件嵌套關係,可是數據流向是隨意的,對於複雜的業務需求,難以支撐。因此須要一箇中心化的觀察者,觀察數據變化,自上而下組件響應數據變化,自下而上更新數據變化到觀察者中心。

emit回調和eventBus的區別是什麼?

emit回調是強調組件關係,父組件是訂閱者,子組件是發佈者。 eventBus不關心組件關係,eventBus實例化的一個實例是發佈者,組件都是訂閱者。

3. vuex

優勢:

  1. 解決全部祖孫級和父子級組件嵌套的數據流問題
  2. 緩存數據,減小http請求次數
  3. 單向數據流,數據流向更清晰
  4. 減小props和回調函數,組件之間解耦

使用vuex須要注意的點

  1. 與後臺約束性強的數據,不宜寫到vuex裏。

vuex和vue同樣都是經過劫持setter/getter追蹤數據變化的,因此vuex也不能檢測到array和object的增刪。

例如表單型的數據,須要增長字段,向後臺提交。vuex沒法處理屬性增刪,$store.commit到store裏,store裏的數據不會更新。

  1. 在數據生命週期結束時,清空store裏緩存的數據。

例如,一個複雜類型的數據,從服務端請求出來存到vuex裏,而後在多個組件之間傳遞,在數據處理結束以後,又提交給服務端。此時,應該清除vuex保存的數據,以便在下一次打開頁面時,vuex裏的數據是乾淨的。

通常,在組件的created生命週期檢查vuex緩存是否存在,沒有緩存,則從新拉數據。在組件生命週期結束前,重置store,清除數據緩存。

什麼狀況下適合用vuex呢?

  1. 在須要緩存數據的時候
  2. emit實在解決不了時候

只有在用React實在解決不了的時候,才用Redux。--------Redux做者

過分使用vuex,將使項目變得臃腫,組件之間耦合度增長。

4. route

傳統型,適用於各頁面之間的數據傳遞。對於一些須要粘貼url讓其餘人訪問的需求,須要在router里加上必須參數,而不能在vuex裏。

好比篩選列表頁,媒體流量桶管理頁。這種頁面結構相同,種類又衆多。不能拆分紅多個頁面,但又須要獨立的頁面展現效果,適合把數據保存在route裏。

使用router傳參時,須要注意:

  1. 組件生命週期鉤子(確保必需的route參數) 1.1 對於依賴router保存數據的頁面,注意在vue組件生命週期鉤子里加校驗 1.2 父子組件的渲染順序是,父created ---> 子created ---> 子mounted ---> 父mounted,因此在created生命週期裏校驗route參數,確保組件render之後有正確的數據顯示。

1.3 watch route變化,及時更新數據。

5. 組件實例方法調用

用於嵌套的表單型組件

好比建立父任務時,父任務表單裏同時又能夠添加子任務,子認爲有分別能夠添加不一樣的任務獎勵規則。 好比建立訂單時,能夠添加廣告組、廣告計劃、廣告創意,廣告創意裏能夠添加各種素材。 這些嵌套複雜的表單型組件,比較適合用這個。

三. 組件拆分原則

1. 善用slot,開閉規則。

修改封閉,擴展開放。

2. 功能拆分,單一職責規則

功能組件仍是ui組件?簡化使用。 好比pagnition處理total < 1時,不顯示分頁器 好比篩選列表組件,保留UI部分,把提交按鈕用slot寫進去 功能組件,好比upload組件

3. 最少知道原則

儘量減小外部依賴 組件內的功能點,對外界的依賴越少越好。 越簡單越好。 參考設計的最高境界,Kiss規則。

四. 一些注意事項

1. 利用$next獲取組件渲染以後的dom

2. v-for的key

3. 覆寫複雜類型的props

4. ajax的封裝

建立axios的實例對ajax的封裝,減小處理回調的代碼量。直接修改axios的攔截器會污染整個工程ajax調用規則。對於屢次import進來的文件,webpack只會打包一次。

相關文章
相關標籤/搜索