著做權歸做者全部。商業轉載請聯繫 Scott 得到受權,非商業轉載請註明出處[務必保留全文,勿作刪減]。框架的價值更多體如今應用層次上面,是一種整合做用,一般框架是一套大而全的特定問題域的解決方案,它與庫的主要區別無外乎控制反轉、可擴展性和不可修改,尤爲控制反轉是框架具有的典型特徵:在框架裏面,程序的控制流程不禁調用者決定,而由框架決定,而對於若干類似的庫,調用者能夠自由切換庫和選擇性調用庫 API,再直白一些,框架是一套規範而庫是一堆砌方法。css
每一個團隊都有一個框架夢,咱們也不例外,只要遇到的足夠複雜的問題,對團隊的總體效率與穩定不斷形成衝擊的時候,每每就是去作問題概括和推出框架的時候,此次咱們聊一聊關於 PC 網站的網頁前端開發方案,咱們遇到的問題和對於框架的初步嘗試,爲保持給你們輕鬆的閱讀體驗和更寬的視角,本文不會深刻代碼細節,至於源碼未來咱們某天開源後你們即可一睹芳容。html
一切技術決策皆有特定業務背景,咱們在平常的開發中,主要面臨以下三類場景:前端
在小菜前期三四年的創業歷程中,咱們紮根在銷地作撮合型的 toB 交易,全部的協同流程都移動化,交易也依託於獨立的 APP,彼時也正是移動互聯網崛起的時候,咱們對於 PC 系統的建設和沉澱比較簡單粗暴,公司中臺只有一個囊括了幾乎全部角色的 ERP(含 CRM 部分功能)後臺,上百個頁面,jQuery 與 React 共存, 經過 webpack 設計了多入口的打包方案,每個獨立的子系統(面向某類角色的業務流程頁面)都獨立打包,但隨着咱們的業務規模攀升和生鮮供應鏈的上下游打通,業務場景愈來愈多,特定層面的業務愈來愈垂直細分,包括中臺和工具層面的建設,整個大中臺開始有了雛形,十幾個後臺 PC 系統像雨後春筍同樣快速啓動,倉促上線,同時數量上也絲毫沒有沒看到減速的態勢,咱們必須有一套標準方案在作到快速的複製來下降重複開發與維護的成本,不爲炫技只爲解決問題就是咱們的初心。node
在這樣的業務背景下,咱們遇到了巨大的挑戰,主要是以下幾方面:react
咱們知道 React 自身單薄,可是周邊生態異常豐富,每有一個新特性出來就會多出很是多的輪子,在眼花繚亂的周邊中,咱們早期也迷失其中:webpack
整個過程趟過來,咱們會發現 react 周邊工具都能衍生出本身的生態,這樣的裂變組合,致使咱們前期的前端工程千奇百怪,交叉開發時,進入的新同窗理解成本很大。web
當咱們想要享受到基礎工具升級帶來的好處,好比早前 webpack2 的 tree shaking,webpack3 的 common chunks 策略更改,那麼每一個項目都須要升級一遍,就會陷入重複開發的泥淖中,而這些對於業務的同窗最好是弱感知的,能夠得到更好的開發體驗,效率和開發幸福感天然會提高。面試
早期的代碼是一個巨大的 class,數據請求對象也揉在 class 內,這個 class 會變得很是大,不可維護。按照關注點分離的原則,UI 層和數據層應當分離,只有這樣,UI 測試和數據層的測試才能獨立,多人開發才能成爲可能。npm
前端和後端基於網關通訊,咱們會封裝調用網關的請求工具,而各個項目都有本身的實現,一旦出問題,不只排查起來沒有規律,也沒法集中解決,維護性不好。json
當出現不少內部子系統時,每一個系統啓動命令和打包命令都不一致,讓咱們的心智成本很是的高,這個在咱們開發了前端發佈系統後暴露的尤爲嚴重,咱們取消了本地發包發佈,都轉移給運維發佈平臺去作執行,但在遷入發佈平臺時,每一個項目的發佈命令不一樣,就須要寫出不一樣的運維腳本,徹底是差別化的規則,而且一旦遇到緊急線上重啓項目,還須要逐個分清楚它的命令參數,效率很低。
那麼多內部系統,每新啓動一個 PC 系統,從登陸到本地用戶態與網關的保持,與權限系統的對接(限制頁面的路由訪問層級),整個系統的工程目錄從新梳理等等各方面從新來一遍,整個配置過程弄下來一天就過去了,偶爾還碰到 webpack 和 babel 的bug,讓新項目的搭建很是的耗時。
問題多多,所有總結下來就是隨着系統的變多,沒有框架約束的項目會面臨三個問題:維護性差、效率低、擴展性差。
針對上述問題,咱們但願在複用性、規範性、可維護性上產出一套方案,重點不是作出一個什麼框架,而是系統解決掉前端工程上的這些問題,因此咱們並不糾結這個框架的自研程度或者概念上的正確性,它必定是逐年逐季度迭代的,也必定會隨着業務不斷受到巨大挑戰甚至某一天會夭折,但在特定的連續時間段內,能解放咱們的成產效率,爲業務帶來更好的支持,它的目的就達到了。
所以結合咱們業務多變,系統繁多,中後臺居多的特徵,咱們把這個框架定義成:輕量級框架 + 自助餐搭配,具體的意思以下:
輕量框架 = (工程化工具包 + 模板市場) * 可升級
工具包 = 開發工具 + 上線工具 + 監控工具
模板市場 = 中後臺項目 + 輕量工程(例如官網首頁)+ H5 工程
它能夠兼容到咱們的中後臺項目以及 H5 工程,同時能搭建輕量級的非 SPA 工程,不能囊括進來的項目就不用該框架搭建,同時它對新人同窗足夠的友好。
模板市場會承載一部分沒法歸入框架管理的定製需求,知足解決不一樣場景的須要,例如 H5 和 PC 的模板不一樣,最典型的即是兼容性要求不同。
具體程序載入的策略和執行的策略則交給框架去約束,簡單說就是業務代碼按照框架的約定(實現框架的接口),就能讓你的業務代碼程序跑起來,不按個人約定,就不能跑起來,更別提上線了。
咱們想要達到的效果是:業務開發不用關心基礎環境的任何問題,基礎環境有 bug,只要提出 issue,解決後直接升級基礎框架便可。提升開發效率,減小解決重複 bug 的狀況,最終提升業務響應速度。
上面的工具包載體就是 cli (command line tool), cli 的功能塊以下:
這裏簡單地介紹下 cli 相關的命令,cli 的交互式命令行依賴了 2 個核心的三方包,分別是:
在 init 階段選擇須要初始化的模板(模板來自模板市場,如今能夠簡單認爲模板市場就是一個代碼倉庫)根據選擇的類型去倉庫內拉取相應的代碼,而後安裝 node_modules 包,整個工程就搭建起來了。
dev 則是啓動本地開發環境,有時咱們須要本地啓動不一樣的環境查看效果。在命令行設計時,咱們能夠針對常見的三個環境作不一樣的啓動命令。如啓動測試環境 dev:test, 就會依賴項目中針對不一樣環境配置的參數作總體替換。
build 則是打包本地的項目,也一樣能夠根據不一樣環境作不一樣的打包策略。
可能大多數公司也會從這個階段過渡過來,因此提一下。在早前沒有接入發佈平臺前,咱們是本地發佈的,html scp 到應用服務,靜態資源走CDN,此時的 deploy 也須要針對不一樣環境作不一樣的打包策略。接入發佈平臺後,這個命令就應當被廢棄了。本地打包帶來的問題很是多,服務器權限、lock問題、協做效率等等。
依託 jest 對項目進行關鍵點的測試。測試當然很是重要,想作到100%覆蓋率則會耗費很是多的時間,咱們不強求100%覆蓋,只要求對工具函數和業務組件作到覆蓋。
前面咱們簡單介紹了幾個經常使用命令,dev、build、deploy 都和 webpack 強相關。咱們經常會將 webpack 相關的命令都放在 package.json 內,webpack 也一樣存在於項目內。但這樣就會出現以前所說兩個問題,分別是基礎環境重複配置和暴露基礎環境配置容易被修改,最終業務項目各自爲政,難以維護。
因此,咱們須要對業務開發者屏蔽 webpack 配置,將 webpack 的配置放入 cli 的工程內,當在業務項目中調用全局的命令時則根據響應的對應的子命令參數,獲取對應的 webpack 配置。
將 webpack 的配置收斂在全局則會出現沒法擴展的問題,爲了知足不一樣業務項目的需求,咱們約定在業務項目的根目錄內存放一份配置文件,在調用命令時會先讀取響應的配置合併到內存中,這裏會涉及 webpack 配置合併的問題, 可以使用 webpack-merge 或者 webpack-chain 這樣的工具進行合併。
框架周邊必定是圍繞着各類工具鏈,而且它的重要性不亞於框架自己:
緩存策略
發佈策略
cli 遵循語義化,升級與否由開發者本身決定。爲了及時通知到開發者,咱們能夠在 npm run dev 腳本里加入一段腳本用於 check 本地 cli 的版本和最新的cli版本是否一致,若是不一致則吐出 CHANGELOG 的連接,而後由業務開發同窗閱讀後選擇是否升級。
光是解決基礎環境的升級還不夠,在業務系統中咱們將通用的請求函數、異常處理、通用的組件也通通抽成 npm 包,各個業務團隊利用 npm 語義特性能夠進行無痛的升級。因此,鼓勵將業務中搜集到的問題和好的解決方案抽出併發布 npm 包。
利用 yeoman 定製相關的 templates,而各個模板須要各條線的業務同窗貢獻過來,目前使用最多的是中後臺的項目,所以質量最好的中後臺相關的。
前面所講的是工具包相關的,這部分講講業務代碼部分的工程化,工具是開發效率的利器,雖然開發環境問題已經被 cli 解決,工程化規範是保證業務項目牢靠的關鍵,須要一套規約。
. ├── assets // 全局的靜態資源 │ ├── icon │ └── index.js ├── components // 全局的components │ ├── index.js │ ├── menu │ │ ├── index.js │ │ └── index.less │ └── slider │ ├── index.js │ └── index.less ├── pages // 頁面,約定每個頁面都是一個路由 │ ├── 404 │ │ └── index.js │ └── user │ | ├── service.js // 存放本頁面須要的數據請求對象和業務邏輯 | | ├── models | | | └── index.js //存放本頁面本身的 rematch model 文件 | | └── index.js | └── login │ └── index.js ├── routes // 自動路由配置 │ └── index.js ├── services // 存放數據請求對象 │ ├── index.js │ └── user │ └── index.js ├── store // 狀態管理 │ ├── index.js │ └── models │ └── user.js └── utils // 工具函數
工程結構按照功能結構劃分,沒有將全部的分層文件提高到最頂層,減小來回切換的心智成本,而且明確了服務層,既解耦界面和邏輯,又方便測試。
路由權限上,咱們會從後端拉取當前用戶擁有的路由表,在 react-router 加入 component 處加入路由級權限校驗的邏輯。
關於狀態管理,咱們選擇了 rematch,簡單易用,也恰好夠用,適合的就是最好的。狀態管理可讓數據和邏輯抽離 UI 層,能夠作到更高的測試也下降了巨型 class 組件的維護成本。
服務層存放數據請求對象,即請求接口。除此之外,一些較重的業務邏輯也會放入 service 內管理,咱們但願狀態管理竟可能輕量,將其中較重的邏輯收斂入 service 中管理。這麼作的目的:
RESTFul 部分抽出公用的網關工具函數並放入私有的 npm 包進行版本管理,GraphQL 部分抽出 pc 版的 gpm 包對接 GQServer。
從 webpack 配置上看,除了入口配置有點不一樣,其餘部分幾乎是同樣的。單頁一般適用於業務之間聯繫較爲緊密的總體應用,例如CRM系統。多頁則適用於頁面很是多,而且彼此之間沒有太多關係的應用,例如營銷 H5 系統。
中後臺系統中咱們應該按照業務域劃分出各自的子系統,不該該將所有的頁面都放入同一個工程。起初內部的 ERP 系統從 Java VM 模板中遷移出來,不得以用多頁形式進行過渡。如今切出業務子系統,各個小系統由各個小團隊本身維護,解耦且擁有單頁良好的體驗,愈來愈多的業務子系統的啓動,也是咱們作框架的必要性之一。
實現框架的全鏈路建設是一個長週期的事情,須要持續不斷的投人力進去,雖然聽上去很高大上,但執行起來仍是相對瑣碎,尤爲是框架推出階段和升級階段,基本上框架的實現者就是在當客服,固然相對優質的升級文檔或者版本間的兼容程度會下降這種工做量,不管怎樣,對於團隊來講,有大量業務場景支持的框架會迸發它更大的價值,對於我的來講,參與框架的建設是一個很考覈全方位能力的過程,而對於全部使用框架的同窗來講,則多了一個順手的工具同時也多了一個能夠學習的封裝範例,也正由於如此咱們對於這件事情持 open 的態度,目前咱們只是實現了它的從 0 到 1,還有監控集成、單元測試覆蓋、發佈系統接入、差別化打包方案、系統模板平臺等等不少有意思的方向尚未更多的同窗加入去共創去建設,還有至關長一段路要走。
:::info
Scott 近兩年不管是面試仍是線下線上的技術分享,遇到許許多多前端同窗,因爲團隊緣由,我的緣由,職業成長,技術方向,甚至家庭等等緣由,在理想國與現實之間,在放棄與堅守之間,搖擺不停,心酸硬扛,你們能夠找我聊聊南聊聊北,對工程師的宿命有更多的瞭解,有更多的看見與聽見,Scott 微信: codingdream,也能夠來關注 Scott 語雀跟進最新動態,本文未經許可不準轉載,得到許可請聯繫 Scott,不然在公衆號上直接轉載,尤爲是裁剪內容後轉載,我都會直接進行投訴處理。
:::