電商網站項目總結:Vuex 帶來全新的編程體驗

若是在簡歷上寫「XX電商系統」的實現,其實第一直覺是這我的必定是從培訓班出來的。而咱們「項目管理」課程正好就是作一個小型電商網站。開發時長一個月左右,包含買家端、賣家端、管理員端,雖然業務邏輯比較常見,可是此次開發仍有收穫,最重要的一點收穫就是 對Vuex有了真正的實踐和認識。 因此,本文大部分介紹Vuex在該項目上的實踐以及所踩的坑,另外的部分則是項目中一些其餘要點的總結。html

過去那臃腫的Vue組件

前面的幾回項目開發,拿到後端接口和需求以後,會按照如下的方式去實現功能:vue

  1. 劃分出 Components 和 Pages,儘可能保證一些組件可以複用。
  2. 抽象出部分能夠複用的 SCSS 代碼以及設計通用的 style class。
  3. 基於某個 HTTP 庫(如 axios)配置並封裝一層底層方法,並基於該方法之上書寫具體的API 函數。
  4. 在頁面和組件內調用上述封裝的方法,在函數回調或Promisethen方法裏進行數據更新或者作更多的業務處理。

好像沒什麼問題哈?

第一、二、3步基本上是沒太大問題的,問題就出在第4步,其結果是致使一個Vue文件愈來愈大,到後面一個複雜的頁面vue文件將會包含以下內容:webpack

  • data裏大量的數據字段,有控制是否展現元素的渲染用數據用於"上鎖"的變量等。
  • createdmounted裏包含了大量的請求回調處理
  • template裏使用的組件的屬性內,綁定了大量的props

最後的結果就是vue文件變得臃腫。ios

文件越長,可讀難度會提升,可維護性會下降。web

誰也不喜歡看一個包含了邏輯處理、界面處理、數據處理的冗雜代碼文件。ajax

因此,爲了解決上述問題,引入 Vuex 是很是必要的。vuex

其實,以前也一直在使用Vuex,不過用的目的僅僅是爲了「保存一點數據可以全局使用」而已,根本沒有深刻使用ActionGettersMutations等。 本文關於Vuex不會過多介紹其原理以及好處,主要關注實踐以及使用心得,Vuex介紹或入門請移步官方文檔編程

乾淨、解耦合、職責分明

筆者所作的,是將關於數據請求和更新,基本都放到了Vuex裏,讓Vuex成爲了整個項目的「數據集散中心」,而vue組件裏,僅僅是進行事件調度(dispatch)綁定來自Vuex的數據axios

簡單的例子

咱們以商品詳情頁面舉例,其包含以下的功能:後端

  • 第一次進入頁面根據URL參數中的id請求商品數據。
  • 根據商品的屬性選擇更新庫存和價格。
  • 添加購物車並更新小紅點。
  • 下單。
  • 收藏商品。

如圖所示:

-w1270

若是咱們不使用Vuex,代碼的結構可能以下:

<template>

<template>

<script> export default { data() { detail: { name: '', shopId: '', id: '', pic: '', description: '', price: '', updateTime: '', categoryId: '', stock: 0, createTime: '', attributeList: {}, collectId: 0, }, // 一大堆商品的字段數據 【MARK】 }, created(){ this.$http.get('/product/:id') .then() // 以及一端更新其餘數據的請求 【MARK】 }, methods(){ clickCart(){ this.$http.get('xxx') .then('xxx') this.$emit('xxx'); // 與上層組件通訊更新購物車的紅點 } // 一堆用戶事件的觸發方法,每一個方法內都是一堆http請求和回調處理 【MARK】 }, computed:{ // 一些基於商品數據判斷狀態的數據 【MARK】 } } </script>

<style> <!-- 忽略相關樣式代碼--> <style> 複製代碼

若是按照上述的方式來寫,雖然能夠保證關於這個頁面的邏輯處理都保留在這個vue文件內,可是卻顯得十分臃腫,若是後續要加更多的功能抑或是修改,其須要讀的代碼可能更多。

因此,以上代碼示例中的被【MARK】標記的部分,將是Vuex能夠優化的。 最後的代碼以下圖所示:

代碼的可讀性提升了很多:

  • 該文件裏看不到任何的http請求,也看不到任何請求路徑。(請求路徑通常是靜態的,直接寫HTTP請求路徑至關於寫死了數據的來源,對於後期的更改數據來源很是不友好)
  • data裏不含任何業務數據字段,僅保留控制頁面的字段
  • 代碼結構變得簡單,只有【觸發事件】-【回調】這樣簡單的邏輯。

關於數據處理的代碼,所有封裝到了Vuex上了:

咱們在Vuex層,不只請求了數據,同時對返回的數據作了判斷以及不一樣的邏輯處理,同時設置Getters使的頁面可以基於Getters提供的數據作各種二元判斷

Vuex層帶來了以下的好處:

  • 在不一樣的頁面能夠觸發相同的Actions,就這個例子,我只要寫了一次加入購物車的函數,在各個頁面均可以使用了。
  • Actions之間能夠組合使用,經過組合的方式,咱們能夠更加輕易的實現複雜的業務邏輯,好比添加商品進購物車的同時更新商品數據並更新購物車數據,這樣須要寫三次HTTP請求的業務邏輯,此時只須要將三個Actions進行組合便可,而這幾個Actions還能夠在其餘地方單獨使用。
  • 直觀,一眼看過去,就知道商品模型上有哪些動做,對於 New Guy 熟悉業務邏輯更加方便,也方便開發者檢查本身的代碼或者函數是否考慮周全。(BTW,在一堆HTML裏找邏輯是真的難受)
  • 減小組件間通訊次數,一箇中心化的數據倉庫帶來的好處就是減小了數據層層傳遞的操做,按照之前的編碼習慣,我會大量使用$emitprops來實現父子或兄弟組件通訊,而如今,只須要在組件中觸發相應的事件綁定相應的數據便可。

全新的業務編寫體驗

直觀體驗來講,完全使用Vuex後,編碼思路清晰很多,由於寫完總體的一個功能須要2~3天,而Vuex對於「間斷性編程」很是有幫助,可以迅速撿起上一次開發的進度。

使用Vuex以後就是按照以下的步驟進行開發了:

(第一、二、3步不變,如前文) 4.構建Vuex的Store模型,定義所需的方法。 5.編寫組件並綁定在Vuex上的相關數據和方法。 6.編寫觸發dispatch後的回調。

筆者認爲使用Vuex幾個重要的原則

使用Vuex存在必定的規範,在官網上已經陳列了諸如表單處理測試項目結構的規範,而下面的一些原則僅僅是筆者的一些經驗之談,不必定正確,有所幫助即是最好的。

【不處理頁面跳轉】:頁面跳轉在dispatch的回調裏進行處理。

最初個人設計是在Action請求數據以後同時也負責全部的跳轉邏輯彈窗邏輯,不過,這樣作又讓視圖層數據層耦合了,使得我抽象出來的Action的複用性變得極差,同時,在vue文件看到關於視圖層的處理纔算是比較正常的編寫邏輯。

【單一職責原則】:mutationaction儘可能保持只作一件事原則。

正如上一條原則中提到的要提升action的複用性,那麼就要保證須要複用的action只作一個功能,如更新A數據更新B數據不要直接融合成一個action,而是分別定義兩個action,並經過組合的方式來實現一個更復雜的功能,有點相似精簡指令集的思想。

如:

actions:{
    updateA(){}, // A 可能在其餘地方被單獨使用
    updateB(){},  // B 可能在其餘地方被單獨使用
    updateAandB({dispatch}){
        dispatch("updateA");
        dispatch("updateB");
    }, // 同時觸發 A B 的複雜action
}
複製代碼

【使用Promise和AA】(Async & Await)

爲了讓dispatch函數擁有Promise風格的處理回調的能力,可讓action的返回值做爲一個Promise對象,如:

// store.js
actions:{
    updateA(){
        return Promise.resolve()
    }
}
複製代碼

在組件內就可使用.then

...
created(){
    this.$store.dispatch('updateA')
        .then(() => {
            ...
        })
}
...
複製代碼

Vuex通常建議在actions進行異步操做,因此爲了讓代碼更加優雅,能夠用AA以下編寫:

// store.js
actions:{
    async updateA(){
        const result = await ajax('/**/**');
        if(result){
            return Promise.resolve()
        }
    }
}
複製代碼

因此,在實踐中的代碼如圖:

【使用namespace】,爲了你本身好。

通常使用vuex,會根據實體或者頁面來拆分state造成module,一個module裏包含其所需的全部actions,states,mutation等。如電商裏,通常有商品訂單用戶這幾個實體能夠拆成module,拆分出來以後如圖:

拆分的好處是更加獨立直觀了,壞處是若是不使用命名空間,可能形成actions,mutations衝突,正好Vuex也提供namespace的配置,參考官方文檔便可。

此次使用的不足

未使用namespace

雖然總結到了原則裏,不過此次並無使用namespace,算是此次比較大的不足了,解決的辦法是」手動namespace「,如圖:

actions的方法名前綴加上該實體名用於區分不一樣的方法,實在是比較拙劣的手段了。

部分代碼仍然有重複書寫的狀況

狀況發生在獲取到異步數據以後的代碼,通常以下這段代碼會大量重複:

if (result.code){
    return Promise.resolve(data);
}
return Promise.reject(msg);
複製代碼

雖然不算多,可是這邏輯仍然有可抽象出來的可能性,下次打算在RootState裏添加一個action,用於處理這樣的後端返回數據,指望結果以下:

actions: {
    parseResult({}, result){
      if (result.code){
        return Promise.resolve(data);
      }
        return Promise.reject(msg);
    },
    async getA({dispatch}){
        const result = await http.get('xx/xx');
        return dispatch('parseResult', result); // One line , it's clean
    }
}
複製代碼

其餘收穫

擁抱 Vue CLI 3

3.0 基本在配置上作到極簡化了。好久沒開發Vue的項目了,記得上一個Vue項目裏,仍然有一大堆xxx.congfig.js,而如今,基本集成到了CLI內部了,經過一個vue.config.js可知足大部分開發需求。本項目配置以下圖:

比之前的配置更加直觀了,更多請參考:cli.vuejs.org/zh/,至於插件機制,本次開發未用上,只是在安裝第三方包的時候能夠用vue add了,有GUI能夠查看安裝了哪些插件。

Vue.use 和 Vue.install使用的地方彙總到plugins目錄下

本項目是一個多頁面應用,總共有3個Vue實例,因此但願可以按需注入想要的第三方依賴如axios,qrcode,lazyload等一些工具庫或第三方組件。因而都將其彙總到plugins目錄下了,以下圖:

-w848

-w842

這樣封裝的好處是,我能夠對每一個Vue實例按需導入第三方依賴,一行import就能夠,以下圖:

-w448

開始考慮優化了

一個大型的web app 打包壓縮後都不超過500kb,因此就以此做爲Benchmark來優化該項目打包後的大小,按需引入第三方依賴(特別是Elementlodash)減小了很多項目體積。不過最主要的仍是得學會用webpack analyse tool來分析。

結論

總之,本次項目主要是刷新了對Vuex的認知,其可以讓咱們編寫的vue組件可讀性和可維護性更高。同時,也可以讓數據更新這塊的代碼複用性更高。經過Vuex來構建一個中心化的數據集散中心還能讓開發的思路更順暢,值得學習。

參考:

Vuex 是什麼?