當我們沉浸在旺盛的需求之中時,整個人便會成為一台工作的機器,切着類似的頁面,寫着同樣的邏輯,重復着昨天或者上個月做的事情,時間久了,覺得膩味,沒有什么創新,也沒有明顯的成長。用一句通俗的話來講:工作五年,后面四年重復着第一年的活兒。

很多人嘗試跳出這個怪圈,不過基於環境壓力和思維受阻,最后又不得不選擇放棄。今天想通過介紹如何高效有保障地開發一個無線頁面來幫助大家找到突破口。
日常開發狀態
很多無線頁面的開發有兩種模式,一種是后台輸出 JSON 數據,前端根據數據來渲染頁面(同步模式);第二種是前端異步加載后端數據然后渲染(異步模式)。當然,兩種模式夾雜在一起也是存在的,這種情況一般會有一個由前端控制的中間層提供同步數據和異步數據。為了減少前后端的溝通成本,往往采用第二種模式。
拿到設計稿后,與后端同學約定接口格式,讓后端同學盡快提供 mock 數據,如果提供不了,便自己構造測試數據。接着回到自己的工位上切圖,切圖過程中會解決好響應式問題和兼容性問題,待到后端產出真實數據時,更換 JS 中的接口地址,聯調 ok 便發布頁面,大功告成!
整個流程很順暢,這對一個工作了三四年的程序員來說,沒有任何壓力便完成甚至提前完成了任務。但是,回過頭來想一想,整個開發過程中我們留下了什么?沉淀了什么?
放慢節奏,我們再走一遍流程
對於上面的開發流程,先提出幾個常見的問題:
- 你是如何良好處理大、中、小等各型號手機的適配問題的?Media Query?等比布局?
- 模塊如何渲染,模板和接口數據如何拼裝?字符串拼接?正則替換?模板引擎?
- 本地、預發和線上三套環境,如何進行無痕切換?
- 如果開發時接口有變動,線上數據暫未產出,本地 mock 接口如何快速響應?
- 如何解決異步 JSONP 接口的安全問題?JSONP 接口請求異常、超時、失敗等情況如何處理?
- 頁面中的 Slide 和 Tab 邏輯如何寫?復制之前寫過的代碼?找一個好用的組件?
- 圖片的懶加載處理如何控制?腳本的懶執行如何控制?
- 首屏加載頁面空白體驗如何優化?
- 頁面回退 Session 和 Token 失效如何處理?
- …
上面提出的幾個問題,列的不全面。有一些可能是你經常碰到的,甚至有了成熟的解決方案,而也有一些問題可能是你從未考慮過的。
我們把整個前端開發流程做簡單切割:切圖、獲取數據、渲染、事件綁定、數據統計、頁面優化、監控。這種切割很暴力,也比較粗糙,不過它不妨礙我們在下面討論,作為前端工程師,除了完成日常需求外,還要做什么?還能做什么?
切圖
隱約還記得三年前,我接了一個無線頁面的外包活兒,頁面的結構很簡單,但我做的很糟糕。為了適配不同尺寸的機型,我寫了無數 Media Query,加上當時采用的 em 作單位,很多細節位置都沒控制好。
回到現在,已經有了很通用、主流的方案——使用 rem,動態計算 html 標簽的 font-size,思路很簡單,但是存在不少的坑,和一些較難理解的概念,Google 搜索下 lib-flexible 能夠找到這些問題以及解決方案。不過我們切圖時還可以思考一些其他的問題:
- 各類靜態資源(image/css/js/font)如何放置?新建各種文件夾?
- 是否還是修改代碼再刷新頁面的調試手段?考慮過 liveload?
- CSS 復用率如何?跨項目的復用率呢?使用預處理語言封裝基類?
- 還在心算從 px 折算為 rem?用計算器算?
- 身旁放 20 台機器測試頁面兼容性?
以上問題,沒有哪一個會讓人特別苦惱,但是堆積起來,卻讓我們的開發效率和開發體驗落后了好幾個檔次。這些問題並非無解,我們可以嘗試着幫助同事和團隊找到問題的答案,比如:
- 統一團隊的本地構建環境,初始化一個工程目錄的腳手架
- 統一打包腳本,實時編譯和預覽
- 封裝預處理基類,屏蔽 rem 計算,比如編譯時自動轉換 px 為 rem
- 構建雲測平台,雲端測試各種機型兼容性,打開網頁輸入網址即可批量測試
有些解決方案只需要幾行腳本就能搞定,而有一些可能需要投入時間和精力。
獲取數據
本地、預發、線上三套環境,如何做到環境的順滑切換?我在百度的時候,團隊最常用的方案就是:
- 線上測試,本地反向代理到預發或者線上環境;
- 本地測試,則使用 apache 開啟服務提供 mock 接口
可一旦與后端約定的接口有變動,本地 mock 數據也要跟着一起變動。這個問題有什么好的處理方案?在團隊中,好的方案一定不是幾行文字的提示或指引,而是通過流程和監控來控制!
這里提到的獲取數據,細想之下可不是什么輕松的事情。有很多問題需要思考:
- 如何保障 JSONP 數據的安全問題?refer 限制?token 驗證?
- 數據來源很多,如何減少頁面的請求數量?讓后端合並數據?如果是多個團隊提供數據呢?
- 如何控制需求變化導致的接口格式變化?
- 如何處理接口的不穩定問題?
- 如何處理超時問題?
- 如何產生容災數據?如何獲取容災數據?
- 如何控制數據緩存?如前端控制緩存一分鍾?
- 如何對接口做監控?
- 如何減少數據的重復請求問題?
以上每個問題都有很多處理方案,而這些問題不僅僅是自己會遇到,身邊的同事也會遇到。如果可以站在團隊的角度去思考問題,很多思路會比較容易涌現出來,比如:
- 構建一個平台,用於接口格式約定,通過約定好的格式,系統自動生成 mock 數據,用於本地開發,后端也必須遵循這個接口約定,任何接口的變動,mock 數據自動變動
- 構建一個平台,讓不規范的數據進入這個平台,規范化輸出,前端只考慮規范化的接口提示和解析,同時該平台產出數據的備份接口
- 前端添加一個請求 Hub,當頁面有很多請求出來時,合並請求統一發出,當數據回來時,統一儲存和過濾
數據是最容易出問題的地方,每一個接口請求都需要一大堆的邏輯處理異常。倘若接口格式、開發流程和前端模式都可以規范化,我們需要做的就剩下套公式,這種高效你能否想象?
渲染
大膽地揣測下大家在寫一個模塊的時候,跟我一樣也是這么划分的函數:
var Module = function() { this.init(); }; // 初始化 Module.prototype.init = function() { this.fetchData(function() { // do something }); }; // 綁定事件 Module.prototype.bindEvent = function() { // ... }; // 獲取數據 Module.prototype.fetchData = function(cb) { var self = this; ajax({}).then(function(data) { self.renderData(data); }).catch(function() { self._fetchDataFailed(); }).fin(function() { cb && cb(); }); }; // 渲染數據 Module.prototype.renderData = function(data) { data = this._resolveData(data); // ... this.bindEvent(); }; // 處理數據 Module.prototype._resolveData = function() { // ... }; // 加載失敗 Module.prototype._fetchDataFailed = function() { // ... };
在代碼中寫大量的字符串模板不管一個模塊有多么簡單,它基本都會包含以上步驟,倘若沒有用函數隔離每步操作的意圖,代碼會顯得十分散亂。我經常看到,有同學把「渲染」這一塊的代碼被放到「獲取數據」甚至是「初始化」中,這種程序結構顯然是不合理的。同時,也經常會看到渲染時,
- 寫一個工具函數,解析字符串模塊中的循環邏輯
- 使用 replace 函數正則替換字符串變量
- 使用 innerHTML 函數插入拼裝好的字符串
- 在渲染模塊中添加大量邏輯
以上,沒有哪一種是不正確的,我也沒有對哪一種寫法開噴的意圖。但是至少我們可以在多次編程經驗中提煉出一些有價值的內容:
- 團隊統一的模板引擎,並且提供模塊的離線編譯,提高線上運行效率
- 提供安全機制,保障插入的數據不會產生安全問題
- 嚴格編程范式,分離視圖和邏輯層,把數據處理好了再送入模板
有一個可執行的編碼規范,加上適當合理的 Code Review,整個團隊代碼便會如出一轍。
后續操作
本想寫成一篇長文,把每個環節可以綜合考慮的問題都提出來,不過本文的目的,只是表述一些觀點,期望大家在編程的時候,有更多基於團隊的思考,針對具體問題提出一些通用的解決方案。比如下面,再提出幾個問題:
- 首屏加載白屏問題,如何處理?本地緩存?等待提示?假數據?同步輸出?
- 如何減少頁面的請求?資源內斂?如何做到自動內斂所有的資源?
- 頁面報錯的統計如何做?做了之后如何分析?分析之后如何推動線上錯誤減少?
- 頁面發布時如何自動回歸檢測?點下鏈接看看是否 404?打開控制台看看是否有報錯?滾屏看看圖片是否加載正確?
- …
以上問題,都有相當成熟的解決方案,那你們團隊呢?
小結
當發現工作做起來索然無味的時候,我腦海中蹦出來的第一個念頭是:最近是不是有點放縱了?
我喜歡用編程解決問題,只要是重復的事情,我一定會想盡辦法簡化,然后交給機器去做。我希望今年可以用程序解決更多的問題。
本文轉自我的個人博客:http://www.barretlee.com/blog/2016/07/21/donnot-repeat-yourself/
