《Node.js在CLI下的工程化體系實踐》成都OSC源創會分享總結

背景: 隨着開發團隊規模不斷髮展壯大,在人員增長的同時也帶來了協做成本的增長,業務項目愈來愈多,類型也各不相同。常見的類型有組件類、活動類、基於React+redux的業務項目、RN項目、Node.js項目等等。若是想要對每一個項目進行一些規範的約束好比Git提交規範、Javascript規範簡直難於登天。全部的這些,只由於缺乏一個好用的工程化工具。從項目建立、開發、構建、代碼規範檢查到最終項目上線,經過CLI能夠提高效率,同時保障開發規範的實施。javascript

Node.js實現CLI的基本原理

關鍵點在於package.json裏面的bin字段。模塊全局安裝,對於類unix系統,在/usr/local/bin目錄建立軟連接;對於windows系統,在C:\Users\username\AppData\Roaming\npm目錄建立軟連接。
模塊局部安裝,會在項目內的./node_modules/.bin目錄建立軟連接。前端

現代化web工程的生命週期

隨着前端工程的不斷演進,一方面工程變得日趨複雜,同時對規範和質量的訴求在不斷增長。現代化web工程應該包含如下幾個階段:初始化、開發、構建、檢查、發佈。以下圖所示:java

痛點1:項目拷貝

項目拷貝存在的問題顯而易見,大體有如下三個方面:node

  • 容易出錯;一旦某個關鍵文件拷貝丟失或者錯誤,極可能須要耗費半天到一天的時間排查環境問題。
  • 不一樣場景下對目錄結構要求不一樣;平時開發過程當中,工程一般會分爲運營活動、Hybrid業務、入口級別的項目(對性能和體驗有極致和苛刻的要求)。須要基於RN或者Node.js的首屏直出,還有經常使用的業務組件等的開發。
  • 新的Feature和BugFix難以同步;某個同窗開發過程當中增長的新方法或者解決的bug很難傳遞給其它同窗而且沉澱成經驗積累下來。

社區裏面提供了完美的Yeoman解決方案,它是爲了自動化項目的建立而生。Yeoman建立項目包括如下幾個階段:react

  • initializing: 初始化一些狀態之類的,一般是和用戶輸入的 options 或者 arguments 打交道
  • prompting: 和用戶交互的時候(命令行問答之類的)調用
  • configuring: 保存配置文件(如 .babelrc 等)
  • writing: 生成模板文件
  • install: 安裝依賴
  • end: 結束部分,初始代碼自動提交

咱們只須要繼承Yeoman的Generator類作模板定製化,基於Yeoman的腳手架設計思路應該以下圖所示:
git

首先,開發者會和CLI進行交互,開發者會告訴CLI須要建立哪種類型的項目,CLI收到命令後。從本地已經安裝的Yeoman腳手架裏面選擇某種類型的模板。而後,CLI會調用Gitlab API在遠程建立倉庫而且授予開發者master權限。接下來,會根據實際業務場景須要,自動化申請一些打點信息,常見的如離線包id,監控告警id等等。以後,在本地目錄生成代碼而且安裝項目依賴的npm包,最後將本次初始化生成的全部代碼自動提交到遠程Git倉庫。github

痛點2:運營配置頻繁修改

基於React+redux組件化開發方式中,一個頁面或者webapp是由多個容器組件拼裝後渲染而成。
web

某個組件一般是由:模板、cgi數據和事件組成。理想狀況下,開發和產品和平共處,你能夠把一個組件寫成下面這個樣子,好比規則組件:npm

render() {
    return (
        <div className="lottery-rule">
            <div className="section">
                <h3>活動時間:</h3>
                <p>9月14日~9月30日</p>
            </div>
            <div className="section">
                <h3>活動規則:</h3>
                <p>一、活動期間,在NOW app上錄製小視頻,上傳成功後便可參賽。</p>
                <p>二、根據參賽小視頻得到的點贊數進行排行。</p>
                <p>三、按照城市評選,分別評選「明日之子」(僅限男性參加)和」閃亮女神「僅限女性參加。</p>
            </div>
        </div>
    );
}

咋一看,上面的寫法沒什麼問題。實際確極可能是七、8次的文案修改,甚至對外入口開放後仍然要修改文案或者圖片等靜態數據。而後,你須要走代碼發佈流程。json

更好的解決思路是:在開發某個業務組件以前,結合以往的經驗,分析哪些靜態數據極可能是須要高頻次的修改。將這些高頻次修改的靜態數據抽離出來,對於萬年不變的數據則沒有必要抽出來。那麼,如何將靜態數據動態化呢?

答案是: Schema First , 開發組件以前先設計Schema,經過schema生成一個form表單,達到靜態數據和模板分離。若是使用React開發,能夠基於react-jsonschema-form定製。靜態數據和模板分離以後應該以下圖:

痛點3:缺乏協做規範

此處以Git commit規範爲例子進行相關改進介紹。

良好的Git commit規範有如下優點:

  • 加快Review的流程
  • 根據Commit元數據生成Changelog
  • 後續維護者能夠知道feature被添加的緣由

此處採用Google angular項目的提交做爲參考,整理出Git commit的解決方案:

具體的提交格式要求以下:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

對格式的說明以下:

  • type表明某次提交的類型,好比是修復一個bug仍是增長一個新的feature。全部的type類型以下:
  • feat: 新增feature
  • fix: 修復bug
  • docs: 僅僅修改了文檔,好比README, CHANGELOG, CONTRIBUTE等等
  • style: 僅僅修改了空格、格式縮進、都好等等,不改變代碼邏輯
  • refactor: 代碼重構,沒有加新功能或者修復bug
  • perf: 優化相關,好比提高性能、體驗
  • test: 測試用例,包括單元測試、集成測試等
  • chore: 改變構建流程、或者增長依賴庫、工具等
  • revert: 回滾到上一個版本

一鍵生成Changelog版本日誌:

痛點4: 缺乏代碼規範

一次血淋淋的生產環境事故:2017年4月13日,騰訊高級工程師小聖在作充值業務時,修改了蘋果iap支付配置,將JSON配置增長了重複的key。代碼發佈後,有小部分使用了vivo手機的用戶反饋充值頁面白屏,沒法在Now app內進行充值。最後問題定位是:vivo手機使用了系統自帶的webview而沒有使用X5內核,解析JSON時遇到重複key報錯,致使頁面白屏。

分析:現代化的瀏覽器對於JSON裏面的重複key會作兼容處理,可是某些老舊的瀏覽器內核並不會,好比此處的vivo手機,致使代碼直接出錯。那麼,如何避免相似問題再次出現呢?

此處不得不說起ESLint,ESLint於2013年6月推出最新版本v4.6.0,是一款適用於Javascript和JSX的代碼規範檢查工具,相比JSLint和JSHint而言,它更加靈活,支持自定義配置、插件擴展和配置錯誤級別。雖然接入ESLint會給團隊的同窗增長很多代碼修改的成本,可是從長遠來看,收益確定是大於付出的。

Javascript規範制定的原則:

  • 不重複造輪子,基於eslint:recommend 配置並改進
  • 可以幫助發現代碼錯誤的規則,所有開啓
  • 配置不該該依賴於某個具體項目,而應儘量的合理
  • 幫助保持團隊的代碼風格統一,而不是限制開發體驗
  • 有對應的解釋文檔

爲了更好的定製和維護Javascript規範,咱們建立了eslint的shareable config。一方面,咱們以爲eslint:recommend 裏面的部分配置定義的錯誤級別過於嚴格,好比代碼裏面出現了console會致使校驗錯誤,另外一方面,它沒有包含ESLint的最佳實踐和其它規則。咱們定義的部分規則解釋以下:

規則名稱 錯誤級別 說明
for-direction error for 循環的方向要求必須正確
getter-return error getter必須有返回值,而且禁止返回值爲undefined, 好比 return;
no-await-in-loop off 容許在循環裏面使用await
no-console off 容許在代碼裏面使用console
no-prototype-builtins warn 直接調用對象原型鏈上的方法
valid-jsdoc off 函數註釋必定要遵照jsdoc規則
no-template-curly-in-string warn 在字符串裏面出現{和}進行警告
accessor-pairs warn getter和setter沒有成對出現時給出警告
array-callback-return error 對於數據相關操做函數好比reduce, map, filter等,callback必須有return
block-scoped-var error 把var關鍵字當作塊級做用域,防止變量提高致使的bug
class-methods-use-this error 要求在Class裏面合理使用this,若是某個方法沒有使用this,則應該申明爲靜態方法
complexity off 關閉代碼複雜度限制
default-case error switch case語句裏面必定須要default分支

ESLint的執行能夠接入到PUSH hook裏面,步驟以下:

#1, 安裝husky
$ npm install husky --save-dev

#2, 集成進npm script
{
  "scripts": {
    "precommit": "validate-commit-msg",
    "prepush": "eslint src ./.eslintrc.js --ext '.js,.jsx'"
  }
}

CLI設計

CLI的做用是將工程開發過程當中遇到的一系列痛點問題鏈接起來,提高開發效率,同時保障規範的實施。

插件設計

插件實現原理

這裏有一個很是巧妙的設計,經過使用node提供的module和vm模塊,能夠通注入feflow全局變量來訪問到cli的實例。從而可以訪問cli上的各類屬性,好比config, log和一些helper等。

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進行註冊,好比:

feflow.cmd.register('deps', 'Config ivweb dependencies', function(args) {
    console.log(args); 
    // Plugin logic here.
});

說明:

  • register有3個參數,第一個是子命令名稱,第二個是命令描述說明信息,第三個是對應的子命令執行邏輯函數。
  • feflow會將命令行參數args解析成Object對象,傳遞給插件處理函數

配置

能夠經過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()   // 致命錯誤日誌,,控制檯中顯示紅色

最後

感謝OSC源創匯提供的交流機會,能和廣大開發者分享和交流學習,CLI源代碼託管在Github和碼雲上:

附件:本次分享PPT

相關文章
相關標籤/搜索