前端日益發展,從最初的 HTML、CSS、JavaScript 三大基礎,到後來的 jQuery、Backbone、AngularJS,再到現在的 Angular、React、Vue 三大框架流行,技術的演進既帶來了更多的可能,也帶來了一些問題。例如:團隊如何高效合作、項目如何統一維護、代碼如何規範等等。前端工程化的出現,就是爲了解決這些日益突出的問題。它旨在制訂規範化的前端工作流,並規範統一項目的模塊化開發和前端資源,讓代碼的維護和互相協作更加容易更加方便。

今年我們團隊由 Angular 技術棧轉變成 React 技術棧,在這個大背景下,我們急需一套完善的工程化方案來幫助技術棧落地。在通過確定目標、定義規範、技術調研、開發實現等一系列步驟之後,制定了一套完善的工程化方案。它幫助解決開發流程中的問題,讓開發更加專注業務本身,提高整個系統生產效率。

目標定義作爲一個工程化方案,最終的目標是儘可能解決項目生命週期裏遇到的問題,例如:

  • 規範保障

每個團隊都會根據實踐經驗,總結出一套自己的規範(項目規範和流程規範)。讓這一套規範在落地到實際的開發中時,除了人爲的約束,更多的應該是通過工具約束。工程化就是把團隊的經驗沉澱到腳手架和開發套件中,讓新項目或新成員可以複用這些經驗。

  • 提效

一個好的工程化方案,是可以提升開發效率的,這個提效包括初始化項目,完備項目的 dev 環境,數據 mock,build 打包等一系列流程,既要讓項目“搭建 - 開發 - 部署”更快捷高效,也要方便項目的後期維護和迭代。

  • 減少人力

在開發過程中可能會遇到一些重複人力的工作。例如 A、B 兩個系統有一個相似的列表頁,這個列表頁不能作爲組件抽離,但是又在多處能用到,這部分資源(代碼模板)複用急需一箇中間媒介來完成。假如資源 / 項目需要升級了,大多情況是資源開發者出一個升級文檔,然後升級的人按照文檔一步步修改。這是很重複的一份工作,因爲假如你手上有十個項目,你就需要把相同的事情做十遍,這一步重複的工作我們希望可以通過工具來完成。

同時,我們的工程化方案應該符合以下四個特點:

  • 漸進式(技術演化能力)易於迭代
  • 擴展性(可伸縮、可插拔)易於部門共建
  • 易用性(貼合實際)易落地
  • 數據統計與分析

基於以上的目標,下面來看一下整個工程化的方案設計。

首先,介紹整個方案的架構圖:

架構圖

可以看到整個架構圖分爲四層:

1、底層依賴

最底層依賴了規範、webpack、schematics、node、eslint,它是上次架構的基石。

  • 規範這裏包括了項目規範和流程規範,是整套方案需要遵守的約定;因爲這套規範是根據大量的實際業務總結出來的,所以它是貼合業務場景,便於整體方案落地的。
  • schematics 引入主要是爲了解決上面說到的資源相關的問題,後面會詳細講到。

2、插件封裝層

這一層主要是對上層需要實現的功能做一個劃分,然後通過插件的形式實現。例如:

  • 將一系列模板抽離作爲一個集合,然後將項目初始化時需要做的模板選擇和模板處理放到 init 插件中完成,不同團隊可以根據規範自定義插件;
  • 開發編譯階段需要的 webpack 和 server、build 腳本放在 @sharkr/scripts 插件中完成,不同團隊可以根據規範自定義插件;
  • 前面說到的資源複用和資源升級問題,統稱爲文件處理相關,@sharkr/schematics-cli 插件提供這個能力;
  • @sharkr/eslint-config-react 插件提供 eslint 檢查;
  • 還有一部分公共能力封裝成 util 插件,方便共享。

這種插件的方式可以使工具具有很強的擴展性,在跟其他團隊共建時,可以很方便的做一些自定義,但是又符合統一的規範。

3、統一命令層

第三層是統一命令層,這一層做的事情就是,最終對用戶暴露一個統一的工具 @sharkr/cli ,在 cli 裏去規範常用的一些命令,當然這些命令的具體實現都是調用下一層的插件。這些命令會應用到項目開發的生命週期中去。

在這一層中會添加埋點,用於收集命令、項目、使用包等相關情況,以便做數據的統計與分析,瞭解實際落地情況,也爲未來迭代版本提供有效參考。

4、項目生命週期

項目生命週期,就是指項目從初始化,到開發(或後期迭代),再到 CICD 的這麼一個週期。可以看到下一層的命令大多都可以對應到項目生命週期中,例如:

  • init 完成項目初始化
  • dev 可以使開發者快速的進入開發
  • add 可以給項目增加一個資源
  • generate 可以執行資源裏定義的命令
  • update 幫助快速升級資源
  • lint 編碼規範校驗
  • test 對開發過程中的代碼做一個測試
  • build 在 CI 階段可以幫助完成編譯打包

通過這整個架構可以看到,我們的工具可以介入到項目生命週期裏的所有環節,那麼這些環節有的一些問題和痛點,也可以通過工具去解決。下面將會從項目生命週期的角度講解一下我們工程化的一個具體方案。

首先,在項目初始化過程中需要解決的一個問題就是:幫助規範落地。

把規範分爲項目規範和流程規範,流程規範上面也介紹了,通過工具做了一個約束,那麼項目規範怎麼在初始化的時候落地呢?

我們的做法是,將這部分規範落地到模板當中,用戶通過 @sharkr/cli 去 init 項目,選擇合適的模板,init 插件完成模板處理,然後就初始化了一個符合規範的項目。

這裏提到選擇合適模板,init 插件完成模板處理,那 init 插件是怎麼做的呢?

每個團隊都會有一些常用的業務場景,例如我們 B 端,會維護好幾個模板,純 web 的、帶 node server 的、應用於微應用的,那麼這一系列模板作爲一個集合,配套的會出一個相應的 init 插件,這個插件可以完成這系列模板的初始化處理。

執行 init 命令時,cli 調用對應的 init 插件,用戶根據提示輸入項目相關配置項,init 插件根據配置項處理模板。

假如說其他團隊也有自己的模板,那麼同樣的,他們也可以根據規範提供模板配套的 init 插件供 cli 調用,方便共建。

這是插件的一個優勢,可以方便擴展自定義的部分,但是它也帶來了一個小問題。這部分插件不是很穩定,可能經常需要更新,它的頻繁更新會給用戶帶來影響。這就需要有一個較好的插件更新機制,方便迭代。

插件更新

先來看一下最初採用的插件更新機制,如下圖:

  • 用戶執行 sr init myapp,調用 @sharkr/cli
  • @sharkr/cli 檢查 init 插件版本
  • 發現版本不是最新的,提示用戶 update @sharkr/cli
  • 用戶全局 update @sharkr/cli
  • 再次執行 sr init myapp, 調用 @sharkr/cli
  • @sharkr/cli 調用最新的 init 處理

這種方式需要用戶經常手動更新,不是很友好,所以在後來方案設計時,改用了 npx 的方式調用 init 插件。

npx 是 npm5.2 版本中新增的命令,它能臨時下載一個模塊並且運行它,運行之後再刪除這個模塊。

  • 用戶執行 sr init myapp,調用 @sharkr/cli
  • @sharkr/cli 使用 npx 調用最新的 init 插件處理

通過方案二,可以看到使用 npx 的方式調用插件,可以使用戶在任何時候使用 init 命令都能調用最新的 init 插件完成項目初始化。

說完了項目初始化,再來看一下開發 / 迭代過程中會遇到哪些問題。

完成項目初始化進入開發階段,這個階段應該是關注業務本身、減少重複工作、高效快速的。

1、dev 環境

開始項目開發遇到的第一個問題就是 dev 環境。需要 webpack 配置編譯代碼、需要一個本地 server、需要一個數據轉發、需要 eslint 配置規範編碼等。

這一部分可以通過 @sharkr/scripts 提供 webpack 配置和 server 腳本,在模板裏集成調用,用 koa 起一個本地服務,koa 中間件完成轉發等。@sharkr/eslint-config-react 定義編碼規範。在這個環節規範了流程和配置,有效的保障項目質量。

有了一個完備的 dev 環境,再來看一下前面說到的資源複用和資源 / 項目升級。

2、資源複用和資源 / 項目升級

資源複用

簡單把資源分爲兩類,一類是有固定輸入輸出,可抽離的,例如時間控件,下拉列表等;還有一類是無固定輸入輸出,不可抽離,但是又具有某些共性的,例如 B 端常見的列表頁、詳情頁,這一部分資源沒有一個很好的複用方式。

對於第一類資源,可以採用封裝組件的形式完成資源共享:

對於第二類資源,以前的做法基本是 Ctrl+c、Ctrl+v 來複用,這種做法不夠高級,還效率不高,所以在 cli 中提供了另外一種做法,那就是將代碼模板抽離到一個 schematic 包裏,然後通過 @sharkr/cli 來安裝這個包,並且執行裏面的 schematic。

資源 / 項目升級

同樣的,假如項目或者是資源需要升級,就需要在項目中升級依賴,可能還需要修改配置、甚至調整目錄結構、修改 api 調用等。這種升級以前的做法是,出一份升級文檔,升級者按照文檔一步步修改項目完成升級。假如有幾十個項目,就需要幾十個人做相同的事幾十遍,這無疑是一個大的人力消耗,作爲一個提效工具,就是需要將這部分重複的工作通過代碼完成。

把升級文檔裏做的事情編寫成 schematic 包

通過 @sharkr/cli 來安裝這個包,並且執行裏面的 schematic

這麼一看,好像很簡單,不管是資源複用,還是項目升級,都寫成 schematic 包就好了,那麼 schematic 到底是什麼呢?

簡介

schematics 是 ngCli 團隊開發的一個強大和通用的工作流程工具,開發者可以將變換應用於項目中,例如:創建新組件、添加配置項、修改現有項目,或者更新你的代碼來修復更新依賴時帶來的 break change。

原理

schematics 如它名字一樣,可以理解爲一個描述了具體操作的原理圖。

schematics 的輸入是一個樹,包含一個基礎區域(一組已經存在的文件)和一個臨時區域(要應用於基礎區域的更改列表)),schemtics 描述了對 Tree 的修改,並將這些修改合併到臨時區域的更改列表中,再往外輸出一個新的 Tree。

在整個操作完結,並得到確認後,鏈條中所有描述的修改纔會真正被應用。

更多 schematics 知識:

優勢

和常規的 JavaScript 腳本工具相比,它的優勢在哪呢?

  • 開發便捷

提供了豐富而強大的通用能力,幫助開發者快速開發:在 code generate 領域,可以通過使用其模板能力,構建各種類型的動態場景代碼模板,快速生成代碼。同時還提供了豐富的 util,提供包括 ast、git 初始化、TslintFixTask tslint 處理等能力。

  • 易於調試

schmatics 由於其虛擬樹的設計,在開發階段支持幹運行,並不會對文件系統執行任何直接操作,方便開發者在項目中進行調試,安全無污染。

  • 可擴展性和可重用性

從其原理可以看出,schmatics 的整體設計,遵循了函數式範式,schematics 不會產生副作用(副作用只會被記錄在緩存區中),具備原子性。schematics 可以自由 Compose 成新的 schematics。

  • 測試友好

schmatics 提供了完備的測試支持庫,測試用例書寫沒有障礙。

schematics 封裝

既然 schematics 處理文件很友好,而且剛好能解決文件處理問題,於是我們就引入了 schematics 完成文件處理,並對它做了一層封裝。

  • 最底層主要依賴 @angular-devkit/schematics 和 @angular-devkit/core 提供 schematics 基礎能力;
  • 將公共能力提取到 @schematics/util 方便開發調用;
  • 提供 schematics 開發模板,方便開發新建 schematics 包;封裝 schematics 的 cli,也就是說它也可以單獨調用命令;
  • 資源(通用資源、項目改造資源、模板升級資源等)會做一些整合,這些資源集合將作爲物料維護在物料海,將來會跟我們正在開發的物料平臺對接;
  • 向上暴露所有的命令,最終在 @sharkr/cli 作爲統一出口。

下面再來看下,開發和使用一個 schematics 包需要做點什麼呢?

首先,對於開發者來說,需要做以下三步:

  • 根據 schematics 規範開發一個 schematics 包
  • 定義配置項(可選)
  • 定義 schema 命令

其次,對於用戶來說,需要做以下三步:

  • 使用 @sharkr/cli 調用 schematics 包
  • 輸入配置(可選)
  • 完成項目修改

那麼 @sharkr/cli 做了什麼了?

  • @sharkr/cli 調用 @sharkr/schematics 插件
  • schematics 插件解析出自定義配置項,生成用戶會話
  • 根據用戶會話拿到 option,傳入 option 調用 Rule 函數
  • 修改文件放到暫存區
  • 確認修改後更新物理文件

這裏 Rule 函數是需要開發者實現的,任何你想寫成文檔的都能通過代碼方式寫在這個 Rule 函數里。

schematics 實踐

下面來看一個實際的例子:前段時間我們的項目都需要完成服務上雲,爲了實現上雲,需要調整下 CI 腳本,還要往項目裏放一些環境配置文件,涉及到多處文件修改和增加,所以我們寫了一個 @sharkr/ng-cloud-add 的 schematics 包達到快速改造項目的目的。以下是效果:

核心代碼寫起來跟寫文檔類似:

以上就講完了在開發 / 迭代過程中遇到的問題和解決方案,最後再看一下 CICD 階段做了什麼。

這部分跟 dev 環境類似,也需要提供 webpack 配置、build 腳本、還約束了 build 打包後的目錄規範、配置部署相關文件。

價值與後續計劃 價值通過介紹以上的一些方案設計,可以發現這套工具可以讓開發更專注業務本身,初始化項目之後就可以直接進入開發;重複的操作可以通過 schematics 完成,省去重複的人力成本;項目 / 資源升級可以更加快速推進;規範和文檔沉澱到工具中供使用,非常有利於規範的遵守。

後續計劃接下來長遠的規劃將會從以下兩點展開:

  • 與資源平臺結合。目前放在 schematics 包裏的資源沒有一個很好的展示平臺,後續將會對接物料平臺,讓資源更容易被使用。
  • vscode 可視化擴展能力。爲了讓開發在開發過程中更加快捷高效,後續會結合 vscode 的插件能力,讓添加資源更容易。

本篇分享主要從四個方面對工程化實踐做了一個闡述:

  • 開發規範的落地依賴模板收斂項目規範,cli 工具收斂流程規範的方式,並在初始化項目時將模板應用於實際項目場景。
  • cli 工具的擴展依賴於插件式,對於自定義的部分提供相應插件即可擴展,而插件的更新依賴 npx 調用的方式,解決用戶需要頻繁更新問題。
  • 方便快速初始化項目,提供完備的 dev 環境和 build 腳本等提升了開發效率。
  • 資源複用和資源 / 項目升級統稱爲文件處理,這部分借用了 angular 裏的 schematics 功能來完成。在這部分還講了什麼是 schematics 以及它在 cli 裏做了什麼。

最後特別感謝同事波哥,給了我非常多的指導和幫助!

作者簡介

撲撲香,2016 年畢業,網易嚴選業務研發部前端開發,先後參與過企業郵、線下店、數據產品等多個項目開發,目前致力於效率工具開發。

延伸閱讀:

解析Angular 7的十大特性-InfoQ

我是如何爲 Angular Components 做出貢獻的-InfoQ

網易嚴選如何打造數倉規範和評價體系?-InfoQ

關注我並轉發此篇文章,私信我“領取資料”,即可免費獲得InfoQ價值4999元迷你書,點擊文末「瞭解更多」,即可移步InfoQ官網,獲取最新資訊~

相關文章