本篇文章主要介紹騰訊IVWEB團隊從0到1在工程化的思考和實踐。feflow的全稱是Front-end flow(前端工做流),致力於提高研發效率和規範的工程化解決方案。願景是經過feflow,可使項目建立、開發、構建、規範檢查到最終項目上線的整個過程更加自動化和標準化。前端
爲了解決上述問題,咱們於17年2月底開始投入工程化feflow工具的開發和相關規範的制定,目前已經研發出了 feflow 的 CLI 版本,後續會推出 GUI 版本。node
爲了讓 feflow 的具備高可擴展性,咱們設計了4層結構,分別是:插件生態、內核層、參數解析器和控制檯。除了貫穿整個開發工做流的基礎命令選擇經過內部插件內置在CLI 的Core裏面,其它非必要命令統一經過插件機制進行擴展。webpack
另外一方面,爲了使得 feflow 可以適用多種類型的項目。咱們開發了多種類型的業務腳手架,如:活動模板、App H5模板、RN模板和業務組件模板。git
當用戶在控制檯裏面輸入某個命令。首先會經過CLI 的參數解析器,將這個命令解析成一個object對象,而後傳遞給CLI 的內核。全部的命令都是經過內核上下文提供的 register 函數 進行註冊的,一方面內核自身會讀取內置插件 註冊的基礎命令,另外一方面,內核會讀取本地已經安裝的外部插件註冊的命令。若是找到用戶輸入的命令則開始執行命令對應的回調函數。github
# 初始化項目 $ feflow init # 本地開發 $ feflow dev # 代碼質量檢查 $ feflow lint # 打包構建 $ feflow build # 代碼發佈 $ feflow publish # 安裝插件、腳手架等 $ feflow install package # 配置本地客戶端,如: npm 的源和 proxy $ feflow config <key> <value>
前面提到,CLI 的命令包含兩部分,分別是內置在內核裏的基礎命令和外部插件提供的命令。那麼外部插件要如何設計呢?web
這裏有一個很是巧妙的設計,經過使用node提供的module和vm模塊,能夠通注入feflow全局變量來訪問到cli的實例。從而可以訪問cli上的各類屬性,好比config, log和一些helper等。npm
loadPlugin(path, callback) { const self = this; return fs.readFile(path).then((script) => { const module = new Module(path); module.filename = path; module.paths = Module._nodeModulePaths(path); function require(path) { return module.require(path); } require.resolve = function(request) { return Module._resolveFilename(request, module); }; require.main = process.mainModule; require.extensions = Module._extensions; require.cache = Module._cache; // Inject feflow variable script = '(function(exports, require, module, __filename, __dirname, feflow){' + script + '});'; const fn = vm.runInThisContext(script, path); return fn(module.exports, require, module, path, pathFn.dirname(path), self); }).asCallback(callback); }
命令須要以feflow.cmd.register進行註冊,好比:json
feflow.cmd.register('deps', 'Config ivweb dependencies', function(args) { console.log(args); // Plugin logic here. });
說明:babel
能夠經過feflow.version獲取當前feflow的版本,feflow.baseDir 獲取feflow跟目錄(在用戶目錄下的.feflow),經過feflow.pluginDir 獲取插件目錄架構
經過feflow.log來進行相關命令行日誌輸出
const log = feflow.log; log.info() // 提示日誌,控制檯中顯示綠色 log.debug() // 調試日誌, 命令行增長--debug能夠開啓,控制檯中顯示灰色 log.warn() // 警告日誌,控制檯中顯示黃色背景 log.error() // 錯誤日誌,控制檯中顯示紅色 log.fatal() // 致命錯誤日誌,,控制檯中顯示紅色
插件開發完成後,能夠經過 feflow 提供的 install 命令安裝插件。安裝的插件會放置在本地客戶端 ~/.feflow/node_modules 文件夾下,而且寫入到 ~/.feflow/package.json 中
$ feflow install feflow-plugin-xxx // 安裝某個插件
以後每次運行命令時,便會從本地加載插件所註冊的命令
當CLI發佈了一個新的版本,可能咱們會廢棄掉某些功能或者提供了新功能。這個時候若是用戶依然使用的是舊版本,因爲某些服務已經廢棄掉了則會報錯。在這種新舊版本不兼容的狀況下,如何強制用戶進行CLI的升級呢?須要在運行命令以前檢查本地的CLI是否和遠程提供的新版本是否兼容。在新舊版本不兼容時,會強制全量更新。如何判斷當前用戶安裝的本地版本和遠程最新版本是否兼容呢?
這裏很是巧妙的運用了一下 npm 的 registry機制,每次發佈新版本,咱們會在 package.json 裏面新增一個自定義字段 compatibleVersion,它的值是一個 semver 的版本號。本地檢查時,會讀取本地已經安裝的版本和遠程最新的版本進行比較,看看是否知足 compatibleVersion 的要求。若是不知足,則會自動運行 npm install feflow-cli
到最新的版本。
"configs": { "compatibleVersion": ">=0.13.0" },
對於插件,採起的是增量更新機制。每一個發佈到 npm 上的插件的package.json 中一樣會有上面的這個字段,對於本地安裝的不兼容的插件列表,會採起增量更新。
項目拷貝存在的問題顯而易見,大體有如下三個方面:
社區裏面提供了完美的Yeoman解決方案,它是爲了自動化項目的建立而生。Yeoman建立項目包括如下幾個階段:
咱們只須要繼承Yeoman的Generator類作模板定製化,基於Yeoman的腳手架設計思路應該以下圖所示:
當開發者輸入 feflow init 命令時,開發者會告訴CLI須要建立哪種類型的項目,CLI收到命令後。從本地已經安裝的Yeoman腳手架裏面選擇某種類型的模板。而後,CLI會調用Gitlab API在遠程建立倉庫而且授予開發者master權限。接下來,會根據實際業務場景須要,自動化申請一些打點信息,常見的如離線包id,監控告警id等等。以後,在本地目錄生成代碼而且安裝項目依賴的npm包,最後將本次初始化生成的全部代碼自動提交到遠程Git倉庫。
爲了讓feflow 支持多種類型的構建環境,好比 Fis3 和 webpack,或者前不久剛推出的號稱零配置成本的 Parcel 構建。在每一個項目的跟目錄會放置一份配置文件,名稱爲 feflow.json。它的配置多是這樣的:
{ "builderType": "builder-webpack3", "builderOptions": { "moduleName": "mobile", "bizName": "category", "minifyHTML": true, "minifyCSS": true, "minifyJS": true, "usePx2rem": true, "remUnit": 100, "remPrecision": 8, "inject": true, "port": 8001 } }
builderType爲構建的npm包,builderOptions爲構建的參數配置。
騰訊IVWEB團隊的工程化解決方案feflow已經開源:Github主頁:https://github.com/feflow/feflow
若是對您的團隊或者項目有幫助,請給個Star支持一下哈~