上週沒有更新原創技術文章,原因是忙著重構一個新接手的專案,此專案因為專案技術負責人離職,雖然投入人力持續增多,前端達到4人,後端3人,但因為新參與的童鞋對程式碼結構和業務的理解,導致專案開發了一個多月,還有一堆問題,達不到上線要求。接手專案之後,一開始對專案業務場景和程式碼進行簡單的了梳理,跟了一天專案,觀察提測後FE+RD的狀態,自己也動手改了一天程式碼,不管是FE+RD包括自己都覺得實在是太難受了,小坑大坑不斷,於是決定重構。重構不是個人衝動,而是的的確確存在各種大大小小的問題:
介面太碎:專案本身按照Vue元件化開發,但是頁面每個元件獨自採用vuex+ajax請求管理自己的資料,比如:首頁由輪播圖、各種列表和使用者資訊展現組成,導致首頁從上到下7~8個模組,每個模組都各自發自己的請求,訪問首頁需要同時發出8個ajax請求
因為「1」提到的,單個元件資料都是使用Vuex進行管理,導致Vuex的store太亂:而且FE按照元件去開發,各自跟後端RD兌介面、聯調,但是沒有人來統籌安排,導致大量重複工作;除此之外,action和mutation中的業務邏輯程式碼太多,而不同頁面需要不同的資料格式,則導致:
或者在mutation當中對資料進行重新整理
或者新開個介面,這樣就造成介面越來越多,mutation部分程式碼越來越重。
一開始設計或者溝通有誤。比如:使用者資訊相關的介面,需要傳入使用者id(uid),而不是通過登入cookie從passport獲取;第三方介面需要使用者資訊,竟然請求的時候將cookie發給對方(幸好cookie是http only的,沒有調通被我及時發現)
重複程式碼太多,抽象能力太差。一份程式碼在多個地方複製,導致程式碼改來改去最後都不知道哪裡改了哪裡沒改
命名太亂,包括url、方法名之類,還有錯別字,getAdcontent(使用者地址資訊),getmaildetail(使用者地址資訊)
研發人員缺乏全域性意識,只管自己的程式碼,而不關心整個流程。由於前後投入人較多,沒人對整個專案有把控,只能面向自己的需求程式設計。比如:積分獲取頁面,獲取成功之後,聯調成功,但是實際在積分獲取列表頁面卻沒有相關的記錄資訊;在比如:任何使用者都可以領走別人的獎品,原因後端沒做獎品是否是當前登入使用者獲取的校驗
問題定位能力不夠,遇見問題一調就是半天,找不到根本問題
介紹下專案背景:
此專案是一個使用者積分任務和消費專案,一些頁面需要使用者登入,頁面主要包括:首頁、任務+列表、商品+列表、個人資訊和記錄以及其他類(說明和規則等)
專案用Vue+yog2編寫,ajax請求部分使用vue resource
架構改造
整個專案還是用Vue+yog2來寫,針對進入頁面分為兩種情況:
第一次通過網站URL進入某個頁面,我稱為:「首次後端渲染」
非首次已經進入頁面URL後,使用者點選連結在專案內跳頁,我稱之為:「非首次跳轉」
整個流程整理如下:兩個流程從「兩個小人」開始看起

後端node Server程式碼部分
程式碼流程如下:
router → middleware → page/api action → model → ral請求資料複製程式碼
其中在action部分,專門寫了個 baseAction
函式,封裝了重複的程式碼,使用時傳入用於獲取資料的model方法和處理資料的方法即可。
render部分,針對頁面第一次請求需要將頁面資料放在HTML片段中chunk輸出,而不是通過ajax請求(為什麼不用vue ssr,可以看下歷史公眾號文章《Vue SSR 從入門到Case Study》,之後單頁內跳轉是ajax請求)。詳細程式碼如下:
client.tpl 部分程式碼:

baseAction 部分實現chunked

Client Vue部分程式碼
client主要流程是:
vue router → created時期 判斷是否有頁面資料 →複製程式碼
提交mutation(有資料),dispatch action(非同步拉取資料)→ state觸發修改,頁面dom生成複製程式碼
這部分流程圖主要展現是Vuex和Vue resource部分的程式碼,通過Vuex的dispatch action,觸發Vue resource的非同步請求,等返回資料則commit mutation。
後端渲染+SPA單頁應用
經過改造後整個流程變成:
「首次後端渲染」:此時需要後端渲染主要HTML+頁面資料,利用chunked,先將不依賴後端資料部分返給瀏覽器,頁面資料和後面的HTML拿到資料後再返給瀏覽器。
client.tpl
被「一分為二」:HTML[0] + HTML[1]將頁面通用的css和js lib庫,放在HTML[0]中,首先返回,瀏覽器先解析下載
業務程式碼初始化程式碼放在HTML[1], 等到獲取到後臺API資料一起返回
「非首次跳轉」:這是一個單頁應用的流程,使用者點選連結,實際走的是vue的router,然後出發vue頁面渲染,URL是通過history pushState mode更改實際URL,這時候如果強刷或者複製url在瀏覽器開啟,又走「首次後端渲染」
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:

改造後module針對業務梳理的四個大頁面內容,保留了四個:

減少mutation資料處理邏輯
複用後端介面資料格式,減少mutation資料處理邏輯
改造前很多action存在下面的程式碼(注意箭頭部分):

其中這個迴圈主要做兩件事情:修改 type
、修改 img_url
為 url
,實際根本沒有必要:
修改
type
:實際這已經是頁面view層的邏輯了,在vue的模板或者computed中做更合適修改
img_url
為url
:這裡實際是產品的封面圖,改成url
反而更不合適了,而且導致了資料不一致
程式碼可以直接用 item
即可!即不需要做額外的迴圈處理,保證資料一致性,避免前端的二次設計
API顯性宣告
之前所有的api都是走了一個 proxy
,通過node轉發一下,直接到了後臺API介面,程式碼如下:

看似很方便甚至有點暗爽的實現,實則帶來了下面的問題:
介面非顯性宣告,降低可控性,造成沒法列舉有多少介面,各個介面需要什麼引數,增加維護成本
安全性!後臺這個服務是完全暴漏給了前端,如果存在敏感的介面,前端js就可以直接透傳利用
改造後的程式碼放在model層,供「首次後端渲染」和「非首次單頁」ajax請求使用:
優化
除了做程式碼重構改造外,還在間隙中做了一些優化,這裡記錄一下:
後端渲染使用chunked
詳見本文「後端node server部分程式碼」和「後端渲染+SPA」
資料複用
很多頁面設計會在首頁和列表頁面存在有產品的title、圖片和簡單的一些meta,例如下圖:

點選連結進去詳情頁面可以直接利用,這部分資料我們做了複用。
實現方法是:頁面點選的時候,將該條資料內容commit給下一個頁面的mutation。
快取
快取在node和前端Ajax API多有,後端node主要快取的是首頁,因為首頁需要請求4個介面(介面梳理後),其中三個介面是跟使用者登入態無關的,這三個介面可以用lru-cache快取起來。
前端的ajax api快取是在 get
請求增加的,可以根據實際情況用,根據url作為key,使用sessionStorage儲存(同時cache類自己實現了快取時間)
技巧
除了優化外,我在介紹下兩個技巧:單頁切換view的loading和統一的錯誤處理。
在單頁跳轉內,下一個view需要API請求獲取資料,然後才能渲染,這時候需要載入個loading顯示(或者做個切換動效)。
原理是:
利用eventBus,在router中新增兩個事件
closeLoading
和vue.action.error
,分別用於「關閉loading」和「展現頁面資料錯誤的錯誤頁」loading展現在router的
beforeEach
的鉤子內實現,loading的事件在vuex的action獲取資料成功之後傳送錯誤的觸發有vuex 的 action / mutation 來傳送事件
eventBus也不用自己寫,可以直接用Vue例項的 $on
、 $emit
、 $once
等就夠了,程式碼如下:
import Vue from 'vue'複製程式碼
export default new Vue()複製程式碼
總結
重構主要做的事情是:
統一介面請求,重新梳理API;
收斂Vuex store設計(包括mutation/action的聚合,state簡化);明確Vuex的使用邊界
明確元件職責邊界,劃分「充血元件」與「貧血元件」
抽象封裝重複的程式碼邏輯
做了一些簡單優化
說下成果:專案已經delay很久,從4月初到5月初已經一個月了還沒上線,接手專案是五一前,整個重構共2個人力用了三天完成95%的工作,目前已經提測,
下面是重構專案的團隊內部問題總結:
專案開發中一定要有大局意識,雖然現在專案多是分元件來的開發方式,但是開發前要跟大家交代清楚約定、規範,什麼該做什麼不該做;
技術負責人多 check 程式碼,防止錯誤的道路上越行越遠;
要有全域性意識,關注整個流程,不要只看到自己的「一畝三分地」,比如:在某個商品頁面,購買/兌換成功了,不要認為沒有問題了,可能記錄頁面還沒有展現(後臺介面沒有入庫);
Don’t repeat yourself!看見重複程式碼就渾身難受,「抽象」能力是工程師的基本能力。
增強debug能力,發現問題直覺就能判斷出來哪個環境,然後針對性debug
未經允許,請勿轉載。