Feflow是一個前端集成開發環境,最新版本是v0.14.1,託管在Github上:feflow。目前已經在NOW直播、花樣直播、花樣交友、手Q附近、羣視頻、羣送禮、迴音等業務普遍使用。有60+ WEB/IOS/Andriod 穩定用戶,累計投入生產項目達到200+。javascript
本文將會詳細介紹Feflow的技術架構和實現原理。前端
Feflow於2017年3月份投入開發,最初只是爲了解決項目建立不智能的問題。後面逐步解決了團隊的構建、規範、CI和自動化問題,最終隨着功能的不斷完善而成爲團隊基礎的前端集成開發環境。java
開發Feflow並非爲了重複造輪子,最核心的目的是打造一體化的工做流程和一致性的團隊開發方式。它基於社區已有的完備工具鏈體系,取衆家之所長。node
下圖描述了社區工具鏈生態: git
Feflow 借鑑了 Pipline 的思想,將平常的研發工做劃分爲:初始化、本地開發、打包構建、檢查、發佈上線五個步驟。分別對應 init、dev、build、test和deploy五個基本命令。github
除了服務好基本的開發工做流和規範,Feflow 提供了易於擴展的插件機制,用於打造團隊統一的工具鏈生態。npm
下圖介紹了Feflow的系統架構,從下到上分爲4層。分別是控制檯、參數解析器、Feflow內核、插件層。對應的功能分別是:json
插件是爲了擴展子命令而設計的,Feflow插件須要以 feflow-plugin-*
開頭,插件開發完成須要發佈到npm或者tnpm。架構
在 Feflow 插件中,能夠直接經過全局變量 feflow
來獲取上下文。這個實現是藉助 Node.js 提供的 module 和 vm模塊來實現全局變量的注入。從而可以訪問上下文的各類屬性和方法,包括:函數
部分實現源碼:
/** * Load a plugin with vm module and inject feflow variable, * feflow is an instance and has context environment. * * @param path {String} Plugin path * @param callback {Function} Callback */
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 對象,全部的命令都須要經過 cmd 進行註冊,部分實現源碼:
/** * Register a command, unique entrance for command registry. * * @param name {String} command name * @param desc {String} command description * @param options * @param fn {Function} command callback */
register(name, desc, options, fn) {
if (!name) throw new TypeError('name is required');
if (!fn) {
if (options) {
if (typeof options === 'function') {
fn = options;
if (typeof desc === 'object') { // name, options, fn
options = desc;
desc = '';
} else { // name, desc, fn
options = {};
}
} else {
throw new TypeError('fn must be a function');
}
} else {
// name, fn
if (typeof desc === 'function') {
fn = desc;
options = {};
desc = '';
} else {
throw new TypeError('fn must be a function');
}
}
}
if (fn.length > 1) {
fn = Promise.promisify(fn);
} else {
fn = Promise.method(fn);
}
const c = this.store[name.toLowerCase()] = fn;
c.options = options;
c.desc = desc;
this.alias = abbrev(Object.keys(this.store));
}
複製代碼
插件開發好而且發佈到 npm 或者 tnpm 後,接下來就是插件安裝使用了。經過如下命令安裝一個插件:
$ feflow install <package>
複製代碼
Feflow 會將插件安裝在 ~/.feflow/node_modules
下。
對於軟件系統而言,都會存在發佈新版本增長Feature或者修復Bug的狀況。好比你們玩王者榮耀或者吃雞遊戲時剛剛進入啓動界面會下載更新包,部分大版本會強制升級等等。
Feflow也提供了增量更新機制,每次初始化Feflow時都會將本地的版本和遠程版本進行比較,若是本地版本和遠程版本不兼容,則會強制幫開發者進行增量更新。
版本檢查機制主要是藉助 npm 的 registry 機制來實現的。若是你有使用過Feflow或者它的插件,你會發現相關npm包的 package.json
裏面有一個自定義字段:
"configs": {
"compatibleVersion": ">=0.14.0"
},
...
複製代碼
compatibleVersion
表示和即將發佈版本兼容的用戶使用版本,此處聽從 semver 版本校驗規範。若是是兼容的,則不會幫開發者更新本地插件,不兼容則會強制更新。
更新機制最大的優點是:統一管控能力,將最新的Feature同步給開發者,同時能夠保證你們使用的版本沒有致命Bug。
下圖是建立項目的效果(能夠保證團隊每次建立新項目使用的是最新的腳手架):
Feflow 上下文提供了 log 對象,經過這個對象可讓控制檯裏面顯示出規範的日誌輸出。
const log = feflow.log;
log.info() // 提示日誌,控制檯中顯示綠色
log.debug() // 調試日誌, 命令行增長--debug能夠開啓,控制檯中顯示灰色
log.warn() // 警告日誌,控制檯中顯示黃色背景
log.error() // 錯誤日誌,控制檯中顯示紅色
log.fatal() // 致命錯誤日誌,,控制檯中顯示紅色
複製代碼
若是使用了 Feflow 上下文提供了 log 對象進行日誌輸出,那麼這些日誌信息會寫入到 ~/.feflow/logs
本地文件系統裏面。這便於後續問題的排查及錯誤上報等。
Feflow提供了日誌分片的能力,將日誌按天進行輸出。
反饋或建議地址:issues,若是您的業務但願接入Feflow,能夠聯繫我。