框架設計:PC 端單頁多頁框架如何設計與落地

著做權歸做者全部。商業轉載請聯繫 Scott 得到受權,非商業轉載請註明出處[務必保留全文,勿作刪減]。

框架的價值更多體如今應用層次上面,是一種整合做用,一般框架是一套大而全的特定問題域的解決方案,它與庫的主要區別無外乎控制反轉、可擴展性和不可修改,尤爲控制反轉是框架具有的典型特徵:在框架裏面,程序的控制流程不禁調用者決定,而由框架決定,而對於若干類似的庫,調用者能夠自由切換庫和選擇性調用庫 API,再直白一些,框架是一套規範而庫是一堆砌方法。css

每一個團隊都有一個框架夢,咱們也不例外,只要遇到的足夠複雜的問題,對團隊的總體效率與穩定不斷形成衝擊的時候,每每就是去作問題概括和推出框架的時候,此次咱們聊一聊關於 PC 網站的網頁前端開發方案,咱們遇到的問題和對於框架的初步嘗試,爲保持給你們輕鬆的閱讀體驗和更寬的視角,本文不會深刻代碼細節,至於源碼未來咱們某天開源後你們即可一睹芳容。html

啓動框架的初心

一切技術決策皆有特定業務背景,咱們在平常的開發中,主要面臨以下三類場景:前端

  • 中後臺的單頁 PC ERP/CRM 等系統,上百種權限的十幾個角色在後臺各自的工做界面,每一個角色都有十幾個到幾十個獨立頁面的業務流程,好比物流專員的發車調度、銷售助理的加價推新等等,對於這些系統有多是一個獨立入口的 SPA,也有多是多個獨立入口的多 SPA 集合,在同一個倉庫內維護
  • 前臺的 PC 網站,幾十個關聯性不強的頁面,沒有太複雜的邏輯,主要作展現,好比公司官網
  • 在手機瀏覽器上的 H5 網站,幾十上百個有強業務邏輯的頁面,也有角色的區分,好比有交易業務的移動版商城

在小菜前期三四年的創業歷程中,咱們紮根在銷地作撮合型的 toB 交易,全部的協同流程都移動化,交易也依託於獨立的 APP,彼時也正是移動互聯網崛起的時候,咱們對於 PC 系統的建設和沉澱比較簡單粗暴,公司中臺只有一個囊括了幾乎全部角色的 ERP(含 CRM 部分功能)後臺,上百個頁面,jQuery 與 React 共存, 經過 webpack 設計了多入口的打包方案,每個獨立的子系統(面向某類角色的業務流程頁面)都獨立打包,但隨着咱們的業務規模攀升和生鮮供應鏈的上下游打通,業務場景愈來愈多,特定層面的業務愈來愈垂直細分,包括中臺和工具層面的建設,整個大中臺開始有了雛形,十幾個後臺 PC 系統像雨後春筍同樣快速啓動,倉促上線,同時數量上也絲毫沒有沒看到減速的態勢,咱們必須有一套標準方案在作到快速的複製來下降重複開發與維護的成本,不爲炫技只爲解決問題就是咱們的初心。node

遇到了那些挑戰

在這樣的業務背景下,咱們遇到了巨大的挑戰,主要是以下幾方面:react

生態豐富卻失去約束

咱們知道  React 自身單薄,可是周邊生態異常豐富,每有一個新特性出來就會多出很是多的輪子,在眼花繚亂的周邊中,咱們早期也迷失其中:webpack

  • 在跨組價通訊和狀態管理上,咱們用過 context、event-emiiter、redux、mobx 還有基於 redux 包裝的 rematch等,好比 16 年起 redux 異步解決方案就有很是多的輪子:thunk、saga、redux-observable 等,而在中後臺項目,尤爲是 CMS 類型項目,幾乎不會涉及競態的問題,有些方案實現的有點牛刀殺雞
  • 在 css in js 方案上,有原始的 sass/less scope 方案、css modules 、 style-components 、jss 等一樣讓人眼花繚亂,很容易選擇新的和捨棄舊的
  • 在路由上,有 react-router 、reach-router、react-navigation 等。咱們雖然沿用了 react-router, 但也不溫馨,在路由權限這裏咱們本身徹底去定製開發

整個過程趟過來,咱們會發現 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 個核心的三方包,分別是:

  • commander: 用於處理命令行內的參數
  • inquier:  處理交互式命令行的問答

init

在 init 階段選擇須要初始化的模板(模板來自模板市場,如今能夠簡單認爲模板市場就是一個代碼倉庫)根據選擇的類型去倉庫內拉取相應的代碼,而後安裝 node_modules 包,整個工程就搭建起來了。

dev

dev 則是啓動本地開發環境,有時咱們須要本地啓動不一樣的環境查看效果。在命令行設計時,咱們能夠針對常見的三個環境作不一樣的啓動命令。如啓動測試環境 dev:test, 就會依賴項目中針對不一樣環境配置的參數作總體替換。

build

build 則是打包本地的項目,也一樣能夠根據不一樣環境作不一樣的打包策略。

deploy

可能大多數公司也會從這個階段過渡過來,因此提一下。在早前沒有接入發佈平臺前,咱們是本地發佈的,html scp 到應用服務,靜態資源走CDN,此時的 deploy 也須要針對不一樣環境作不一樣的打包策略。接入發佈平臺後,這個命令就應當被廢棄了。本地打包帶來的問題很是多,服務器權限、lock問題、協做效率等等。

test

依託 jest 對項目進行關鍵點的測試。測試當然很是重要,想作到100%覆蓋率則會耗費很是多的時間,咱們不強求100%覆蓋,只要求對工具函數和業務組件作到覆蓋。

對 webpack 的思考

前面咱們簡單介紹了幾個經常使用命令,dev、build、deploy 都和 webpack 強相關。咱們經常會將 webpack 相關的命令都放在 package.json 內,webpack 也一樣存在於項目內。但這樣就會出現以前所說兩個問題,分別是基礎環境重複配置暴露基礎環境配置容易被修改,最終業務項目各自爲政,難以維護。

因此,咱們須要對業務開發者屏蔽 webpack 配置,將 webpack 的配置放入 cli 的工程內,當在業務項目中調用全局的命令時則根據響應的對應的子命令參數,獲取對應的 webpack 配置。

將 webpack 的配置收斂在全局則會出現沒法擴展的問題,爲了知足不一樣業務項目的需求,咱們約定在業務項目的根目錄內存放一份配置文件,在調用命令時會先讀取響應的配置合併到內存中,這裏會涉及 webpack 配置合併的問題, 可以使用 webpack-merge 或者 webpack-chain 這樣的工具進行合併。

工具鏈輔助

框架周邊必定是圍繞着各類工具鏈,而且它的重要性不亞於框架自己:

關於效率

  • 數據 mock      mocks.js 定製
  • 簡化路由配置  glob 讀取文件夾做爲路由
  • 所改即所見     webpack-dev-server 的 hotreload(H5 營銷活動系統)與保持狀態的 react-hot-reload(長業務流程須要保留state)
  • 代碼生成器         定製生成  pageFile、service、model 文件的命令

關於質量

  • code lint 基於 eslint 定製,配合 husky 作到開發時、提交時、上線時的校驗
  • 提交規範 commit-lint
  • 指定代碼規約作 code review

發佈

  • 緩存策略

    • 動態資源 html 放應用服務器,走協商緩存
    • 靜態資源 hash化,放入 CDN,走強緩存
  • 發佈策略

    • html scp 上傳到應用服務器,這件事前期是項目負責人本地發佈,後期是發佈平臺操做
    • cdn 資源 hash 化,永久緩存

如何作到可升級

  • 基礎配置收斂進全局
  • 通用組件、工具函數用 npm 包管理

cli 遵循語義化,升級與否由開發者本身決定。爲了及時通知到開發者,咱們能夠在 npm run dev 腳本里加入一段腳本用於 check 本地 cli 的版本和最新的cli版本是否一致,若是不一致則吐出 CHANGELOG 的連接,而後由業務開發同窗閱讀後選擇是否升級。

光是解決基礎環境的升級還不夠,在業務系統中咱們將通用的請求函數、異常處理、通用的組件也通通抽成 npm 包,各個業務團隊利用 npm 語義特性能夠進行無痛的升級。因此,鼓勵將業務中搜集到的問題和好的解決方案抽出併發布 npm 包。

關於模板市場

利用 yeoman 定製相關的 templates,而各個模板須要各條線的業務同窗貢獻過來,目前使用最多的是中後臺的項目,所以質量最好的中後臺相關的。

React 的工程化

前面所講的是工具包相關的,這部分講講業務代碼部分的工程化,工具是開發效率的利器,雖然開發環境問題已經被 cli 解決,工程化規範是保證業務項目牢靠的關鍵,須要一套規約。

自助餐清單

  • css in js:  jss 搭配 less/sass
  • 狀態管理:  rematch
  • 路由:react-router
  • ui 庫: ant-design
  • 測試框架:jest
  • mock 工具:mock.js

簡版工程結構

.
├── 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 中管理。這麼作的目的:

  • 複用一些 comb 的接口
  • 態管理層的心智負擔,例如一些字段的default
  • 方便測試一些關鍵業務點

與後端交互

RESTFul 部分抽出公用的網關工具函數並放入私有的 npm 包進行版本管理,GraphQL 部分抽出 pc 版的 gpm 包對接 GQServer。

單頁和多頁

從 webpack 配置上看,除了入口配置有點不一樣,其餘部分幾乎是同樣的。單頁一般適用於業務之間聯繫較爲緊密的總體應用,例如CRM系統。多頁則適用於頁面很是多,而且彼此之間沒有太多關係的應用,例如營銷 H5 系統。

中後臺系統中咱們應該按照業務域劃分出各自的子系統,不該該將所有的頁面都放入同一個工程。起初內部的 ERP 系統從 Java VM 模板中遷移出來,不得以用多頁形式進行過渡。如今切出業務子系統,各個小系統由各個小團隊本身維護,解耦且擁有單頁良好的體驗,愈來愈多的業務子系統的啓動,也是咱們作框架的必要性之一。

總結

實現框架的全鏈路建設是一個長週期的事情,須要持續不斷的投人力進去,雖然聽上去很高大上,但執行起來仍是相對瑣碎,尤爲是框架推出階段和升級階段,基本上框架的實現者就是在當客服,固然相對優質的升級文檔或者版本間的兼容程度會下降這種工做量,不管怎樣,對於團隊來講,有大量業務場景支持的框架會迸發它更大的價值,對於我的來講,參與框架的建設是一個很考覈全方位能力的過程,而對於全部使用框架的同窗來講,則多了一個順手的工具同時也多了一個能夠學習的封裝範例,也正由於如此咱們對於這件事情持 open 的態度,目前咱們只是實現了它的從 0 到 1,還有監控集成、單元測試覆蓋、發佈系統接入、差別化打包方案、系統模板平臺等等不少有意思的方向尚未更多的同窗加入去共創去建設,還有至關長一段路要走。

:::info
Scott 近兩年不管是面試仍是線下線上的技術分享,遇到許許多多前端同窗,因爲團隊緣由,我的緣由,職業成長,技術方向,甚至家庭等等緣由,在理想國與現實之間,在放棄與堅守之間,搖擺不停,心酸硬扛,你們能夠找我聊聊南聊聊北,對工程師的宿命有更多的瞭解,有更多的看見與聽見,Scott 微信: codingdream,也能夠來關注 Scott 語雀跟進最新動態,本文未經許可不準轉載,得到許可請聯繫 Scott,不然在公衆號上直接轉載,尤爲是裁剪內容後轉載,我都會直接進行投訴處理。
:::

2.png
1.png

相關文章
相關標籤/搜索