記一次 Vue 專案的重構

三水清發表於2017-05-31

上週沒有更新原創技術文章,原因是忙著重構一個新接手的專案,此專案因為專案技術負責人離職,雖然投入人力持續增多,前端達到4人,後端3人,但因為新參與的童鞋對程式碼結構和業務的理解,導致專案開發了一個多月,還有一堆問題,達不到上線要求。接手專案之後,一開始對專案業務場景和程式碼進行簡單的了梳理,跟了一天專案,觀察提測後FE+RD的狀態,自己也動手改了一天程式碼,不管是FE+RD包括自己都覺得實在是太難受了,小坑大坑不斷,於是決定重構。重構不是個人衝動,而是的的確確存在各種大大小小的問題:

  1. 介面太碎:專案本身按照Vue元件化開發,但是頁面每個元件獨自採用vuex+ajax請求管理自己的資料,比如:首頁由輪播圖、各種列表和使用者資訊展現組成,導致首頁從上到下7~8個模組,每個模組都各自發自己的請求,訪問首頁需要同時發出8個ajax請求

  2. 因為「1」提到的,單個元件資料都是使用Vuex進行管理,導致Vuex的store太亂:而且FE按照元件去開發,各自跟後端RD兌介面、聯調,但是沒有人來統籌安排,導致大量重複工作;除此之外,action和mutation中的業務邏輯程式碼太多,而不同頁面需要不同的資料格式,則導致:

    1. 或者在mutation當中對資料進行重新整理

    2. 或者新開個介面,這樣就造成介面越來越多,mutation部分程式碼越來越重。

  3. 一開始設計或者溝通有誤。比如:使用者資訊相關的介面,需要傳入使用者id(uid),而不是通過登入cookie從passport獲取;第三方介面需要使用者資訊,竟然請求的時候將cookie發給對方(幸好cookie是http only的,沒有調通被我及時發現)

  4. 重複程式碼太多,抽象能力太差。一份程式碼在多個地方複製,導致程式碼改來改去最後都不知道哪裡改了哪裡沒改

  5. 命名太亂,包括url、方法名之類,還有錯別字,getAdcontent(使用者地址資訊),getmaildetail(使用者地址資訊)

  6. 研發人員缺乏全域性意識,只管自己的程式碼,而不關心整個流程。由於前後投入人較多,沒人對整個專案有把控,只能面向自己的需求程式設計。比如:積分獲取頁面,獲取成功之後,聯調成功,但是實際在積分獲取列表頁面卻沒有相關的記錄資訊;在比如:任何使用者都可以領走別人的獎品,原因後端沒做獎品是否是當前登入使用者獲取的校驗

  7. 問題定位能力不夠,遇見問題一調就是半天,找不到根本問題

介紹下專案背景:

此專案是一個使用者積分任務和消費專案,一些頁面需要使用者登入,頁面主要包括:首頁、任務+列表、商品+列表、個人資訊和記錄以及其他類(說明和規則等)
專案用Vue+yog2編寫,ajax請求部分使用vue resource

架構改造

整個專案還是用Vue+yog2來寫,針對進入頁面分為兩種情況:

  1. 第一次通過網站URL進入某個頁面,我稱為:「首次後端渲染

  2. 非首次已經進入頁面URL後,使用者點選連結在專案內跳頁,我稱之為:「非首次跳轉

整個流程整理如下:兩個流程從「兩個小人」開始看起
記一次 Vue 專案的重構

後端node Server程式碼部分

程式碼流程如下:

  1. router → middleware → page/api action → model → ral請求資料複製程式碼

其中在action部分,專門寫了個 baseAction函式,封裝了重複的程式碼,使用時傳入用於獲取資料的model方法和處理資料的方法即可。

render部分,針對頁面第一次請求需要將頁面資料放在HTML片段中chunk輸出,而不是通過ajax請求(為什麼不用vue ssr,可以看下歷史公眾號文章《Vue SSR 從入門到Case Study》,之後單頁內跳轉是ajax請求)。詳細程式碼如下:

client.tpl 部分程式碼:
記一次 Vue 專案的重構

baseAction 部分實現chunked
記一次 Vue 專案的重構

Client Vue部分程式碼

client主要流程是:

  1. vue router → created時期 判斷是否有頁面資料 →複製程式碼

  2. 提交mutation(有資料),dispatch action(非同步拉取資料)→ state觸發修改,頁面dom生成複製程式碼

這部分流程圖主要展現是Vuex和Vue resource部分的程式碼,通過Vuex的dispatch action,觸發Vue resource的非同步請求,等返回資料則commit mutation。

後端渲染+SPA單頁應用

經過改造後整個流程變成:

  1. 「首次後端渲染」:此時需要後端渲染主要HTML+頁面資料,利用chunked,先將不依賴後端資料部分返給瀏覽器,頁面資料和後面的HTML拿到資料後再返給瀏覽器。 client.tpl被「一分為二」:HTML[0] + HTML[1]

    1. 將頁面通用的css和js lib庫,放在HTML[0]中,首先返回,瀏覽器先解析下載

    2. 業務程式碼初始化程式碼放在HTML[1], 等到獲取到後臺API資料一起返回

  2. 「非首次跳轉」:這是一個單頁應用的流程,使用者點選連結,實際走的是vue的router,然後出發vue頁面渲染,URL是通過history pushState mode更改實際URL,這時候如果強刷或者複製url在瀏覽器開啟,又走「首次後端渲染」

    1. vue頁面渲染需要的資料是通過vue-resource發起ajax請求,拿到資料之後commit mutation改變state

Vuex梳理

之前程式碼每個元件都單獨ajax請求自己的資料,導致Vuex的module特別多特別亂,而且後端api介面太多太碎,不好維護。最後開發的童鞋自己都在群裡抱怨,找個action或者mutation都不知道在哪個檔案內,需要搜程式碼。。

首先做約定,明確什麼時候使用Vuex:

頁面view相關的資料才使用Vuex來管理,頁面功能性ajax(例如簽到、兌換)不要使用vuex;「首次後端渲染」和「非首次跳轉」的view資料都通過commit mutation來修改state,最終對映到DOM表現上;功能性ajax則在元件內自己發請求實現,保持元件內聚;

P.S:這就是「貧血元件」和「充血元件」的區別,本身元件內的狀態和邏輯都放在全域性store管理,會增加store複雜度,降低效率(程式碼效能和開發效率)

然後,收斂vuex module

收斂是根據業務頁面做的,前文提到:

頁面主要包括:首頁、任務+列表、商品+列表、個人資訊和記錄以及其他類(說明和規則等)

其中需要資料的有:首頁、任務(詳情、列表)、商品(詳情、列表)和個人中心四個。

改造前module:
記一次 Vue 專案的重構

改造後module針對業務梳理的四個大頁面內容,保留了四個:
記一次 Vue 專案的重構

減少mutation資料處理邏輯

複用後端介面資料格式,減少mutation資料處理邏輯

改造前很多action存在下面的程式碼(注意箭頭部分):
記一次 Vue 專案的重構

其中這個迴圈主要做兩件事情:修改 type、修改 img_urlurl,實際根本沒有必要:

  • 修改 type:實際這已經是頁面view層的邏輯了,在vue的模板或者computed中做更合適

  • 修改 img_urlurl:這裡實際是產品的封面圖,改成 url反而更不合適了,而且導致了資料不一致

程式碼可以直接用 item即可!即不需要做額外的迴圈處理,保證資料一致性,避免前端的二次設計

API顯性宣告

之前所有的api都是走了一個 proxy,通過node轉發一下,直接到了後臺API介面,程式碼如下:
記一次 Vue 專案的重構

看似很方便甚至有點暗爽的實現,實則帶來了下面的問題:

  1. 介面非顯性宣告,降低可控性,造成沒法列舉有多少介面,各個介面需要什麼引數,增加維護成本

  2. 安全性!後臺這個服務是完全暴漏給了前端,如果存在敏感的介面,前端js就可以直接透傳利用

改造後的程式碼放在model層,供「首次後端渲染」和「非首次單頁」ajax請求使用:

記一次 Vue 專案的重構

優化

除了做程式碼重構改造外,還在間隙中做了一些優化,這裡記錄一下:

後端渲染使用chunked

詳見本文「後端node server部分程式碼」和「後端渲染+SPA」

資料複用

很多頁面設計會在首頁和列表頁面存在有產品的title、圖片和簡單的一些meta,例如下圖:

記一次 Vue 專案的重構

點選連結進去詳情頁面可以直接利用,這部分資料我們做了複用。

實現方法是:頁面點選的時候,將該條資料內容commit給下一個頁面的mutation。

快取

快取在node和前端Ajax API多有,後端node主要快取的是首頁,因為首頁需要請求4個介面(介面梳理後),其中三個介面是跟使用者登入態無關的,這三個介面可以用lru-cache快取起來。

前端的ajax api快取是在 get請求增加的,可以根據實際情況用,根據url作為key,使用sessionStorage儲存(同時cache類自己實現了快取時間)

記一次 Vue 專案的重構

技巧

除了優化外,我在介紹下兩個技巧:單頁切換view的loading和統一的錯誤處理。

在單頁跳轉內,下一個view需要API請求獲取資料,然後才能渲染,這時候需要載入個loading顯示(或者做個切換動效)。

原理是:

  1. 利用eventBus,在router中新增兩個事件 closeLoadingvue.action.error,分別用於「關閉loading」和「展現頁面資料錯誤的錯誤頁」

  2. loading展現在router的 beforeEach的鉤子內實現,loading的事件在vuex的action獲取資料成功之後傳送

  3. 錯誤的觸發有vuex 的 action / mutation 來傳送事件

記一次 Vue 專案的重構

eventBus也不用自己寫,可以直接用Vue例項的 $on$emit$once等就夠了,程式碼如下:

  1. import Vue from 'vue'複製程式碼

  2. export default new Vue()複製程式碼

總結

重構主要做的事情是:

  1. 統一介面請求,重新梳理API;

  2. 收斂Vuex store設計(包括mutation/action的聚合,state簡化);明確Vuex的使用邊界

  3. 明確元件職責邊界,劃分「充血元件」與「貧血元件」

  4. 抽象封裝重複的程式碼邏輯

  5. 做了一些簡單優化

說下成果:專案已經delay很久,從4月初到5月初已經一個月了還沒上線,接手專案是五一前,整個重構共2個人力用了三天完成95%的工作,目前已經提測,

下面是重構專案的團隊內部問題總結:

  1. 專案開發中一定要有大局意識,雖然現在專案多是分元件來的開發方式,但是開發前要跟大家交代清楚約定、規範,什麼該做什麼不該做;

  2. 技術負責人多 check 程式碼,防止錯誤的道路上越行越遠;

  3. 要有全域性意識,關注整個流程,不要只看到自己的「一畝三分地」,比如:在某個商品頁面,購買/兌換成功了,不要認為沒有問題了,可能記錄頁面還沒有展現(後臺介面沒有入庫);

  4. Don’t repeat yourself!看見重複程式碼就渾身難受,「抽象」能力是工程師的基本能力。

  5. 增強debug能力,發現問題直覺就能判斷出來哪個環境,然後針對性debug


@三水清
未經允許,請勿轉載。
掘金更新比公眾號晚一週左右。
感覺有用,歡迎關注我的公眾號,最新文章第一時間看到!
記一次 Vue 專案的重構

相關文章