跳到主要內容
黯羽輕揚每天積累一點點

用 gulp 花 5 分鐘優化工作流

免費2016-08-20#Tool#gulp教程#BrowserSync#多浏览器同步#前端工作流

優化工作流,節省時間提高效率

寫在前面

5 分鐘是昨天的梗,不要打我,哈哈

一。為什麼是 gulp?

說起構建工具(打包工具),就想起���傷的 grunt,還有去年 5 月的那篇筆記:[Grunt 教程](/articles/grunt 教程/)

可選的構建工具非常多:

但為什麼是 gulp?

首先,grunt 過氣了,去年 5 月 grunt 還勉強撐得住(插件數量比 gulp 多很多,所以才有了那篇筆記),現在已經沒什麼優勢了,插件數量不再是 gulp 的短板

如 npm scripts 所說,pipe 不是 gulp 原創,操作系統本來就支持,所以 npm scripts 的理念是直面 shell 命令,去掉不需要的抽象層(grunt, gulp...),比如 gulp-jshint,直接執行 jshint 提供的 cli 不就完了,要 gulp 幹啥?

至於 webpack/rollup,應該和 grunt/gulp 差不多(都屬於 npm scripts 眼中不必要的抽象層),沒有必要天天換構建工具。所以,選擇 gulp 僅僅是因為成本(團隊本來就在用)

二。npm scripts

從設計理念上來看,npm scripts 似乎更超前些,幾點原因:

  • 去掉中間抽象層,直接使用 package 提供的 cli,不用等插件更新就能用上 package 新特性

  • 結構更簡單,降低了調試複雜度(package 的問題?配置的問題?插件版本/插件本身的問題?項目代碼的問題?)

  • 可以執行操作系統所能執行的任何腳本(python、bash 腳本等等,gulp + shelljs 勉強跟上,grunt 掉隊了)

  • 沒有插件,但「插件」數量卻最多(等人寫插件幹啥?能搞到的所有能通過命令行執行的腳本全都是「插件」)

當然,也存在一些缺點:

  • package.json 無法添加註釋。寫在 readme 裡?還是有點難受

  • 需要對命令行多少了解一點。雖然 npm 封裝了 1000 多個基礎命令,但從「刪除文件夾」到 rm -rfrimraf 的過程還是太曲折,了解常用命令是必須的

  • 不支持變量。支持環境變量和任何腳本,足夠了("build": "node bin/build.js"

示例:

// package.json
{
    ...
    "scripts": {
        // basic
        "watch-test": "mocha --watch --reporter spec test",
        "test": "mocha \"src/**/*.test.js\" --require test/setup.js --compilers js:babel-register",
        // pipe
        "build-js": "browserify -t reactify app/js/main.js | uglifyjs -mc > static/bundle.js",
        // and(&&)、or(||)
        "build": "npm run build-js && npm run build-less",
        // concurrent
        "watch": "npm run watch-js & npm run watch-less & npm run watch-server"
    }
}

更複雜的命令可以寫個 bash shell 腳本,或者 node + shelljs,沒有多餘的東西

三。捏一個 node 命令行工具

想自定義一個全局命令,在任何目錄都能執行?其實很容易的

  1. 編寫 nodejs 代碼,添上 Shebang(在文件首行添上 #!/usr/bin/env node,表示用 node 來執行該文件),保存 xxx.js

  2. npm init 或者直接手寫個 package.json

  3. 修改 package.json,添加 "bin": {"hoho": "xxx.js"}

  4. npm link 把本地 package link 到全局(類似於 windows 環境變量 path)

關鍵步驟是這樣,但存在幾個問題

  • 命令如何跨平台?(windows、unix)

  • 如何獲取命令行參數?

  • 如何使用 gulp

shelljs 模塊提供了跨平台命令;yargs 模塊能夠獲取命令行參數;使用 gulp 需要 npm install gulp --save-dev本地安裝 gulp 並添加依賴;此外,想要在命令行輸出五顏六色的東西,還可以用 colors 模塊

注意:本地安裝 gulp 是必須的,因為 gulp 的設計理念是不希望所有 package 依賴一個全局 gulp,所以要 require('gulp') 就必須本地安裝一個,多很多文件感覺不太舒服,但更靈活

命令行工具只是第 0 步,重要但不關鍵,具體步驟可以查看這個:Nodejs 製作命令行工具

四。用 gulp 優化工作流

這裡的「工作流」包括但不限於基本的打包步驟(編譯、代碼檢查、資源壓縮等等),比如開發過程中需要的工具,以及發布所需的工具,都屬於工作流。優化工作流的過程就是簡化這些步驟,把枯燥繁瑣的事情交給工具來辦,我們就能解脫出來做更多的事情,從而提高效率

為了讓工具容易擴展和維護,需要考慮結構,比如:

.
|____bin
| |____wf.js        # 命令行入口(接收命令行指令,分發給子命令)
|____config.json    # 配置參數(比如需要同步的路徑 glob)
|____gulpfile.js    # 定義 gulp task
|____index.js       # package 入口(package.main)
|____lib            # 子命令
| |____wf-ftp.js    # 自測、提測
| |____wf-sync.js   # 多瀏覽器同步
| |____wf-test.js
|____node_modules
| |____...
|____package.json
|____README.md

其中 wf-sync 是對 Browser Sync 的包裝,支持多瀏覽器同步(刷新同步、滾動同步、樣式變化同步等等),開發過程中非常省心

wf-ftp 提供命令行 ftp 支持,可配置待上傳路徑 glob 及遠程路徑,遠程環境相對穩定,用於在開發過程中與 PM、UI 確認效果,開發完成後 wf ftp -r 打包提測

上面結構中關鍵是 indexwf,前者負責初始化,後者類似於路由

頂層 index.js 負責初始化全局設置,並約定子命令,提供子命令執行方法

var argv = require('yargs').argv;
var colors = require("colors");
var path = require("path");
var config = require("./config.json");

// 命令行輸出五顏六色的東西
colors.setTheme({
    warn: 'yellow',
    debug: 'blue',
    ...
});
var wf = {
    log: function(str) {
        console.log(colors.log(str));
    },
    ...
};
Object.defineProperty(global, 'wf', {
    enumerable: true,
    writable: false,
    Configurable: false,
    value: wf
});

wf.config = config;

// 執行子命令(約定子命令的調用方式)
wf.run = function() {
    // 當前命令
    var cmd = process.argv[2];
    // 執行路徑
    var cwd = argv.path || argv.p || process.cwd();
    wf.cmd = cmd;
    wf.cwd = cwd;
    var cmdFile = require("./lib/wf-" + cmd);
// 要求 lib 中的每個模塊都必須 export 一個 run() 方法作為入口
    cmdFile.run && cmdFile.run();
};

module.exports = wf;

中層 bin/wf.js 負責路由控制,如下:

#!/usr/bin/env node

var wf = require("../index.js");
var _ = require("underscore");
var yargs = require('yargs');
var shell = require("shelljs");

var cmd = process.argv[2];
var cmdList = {
    "test": "test",
    "ftp": "ftp",
    "sync": "sync"
};

if (cmdList[cmd]) {
    var cmdMod = require("../lib/wf-" + cmd);
    if (cmdMod && cmdMod.name && cmdMod.desc) {
        // 設置子命令
        var argv = yargs.command(cmdMod.name, cmdMod.desc, function(yargs) {
            cmdMod.usage && yargs.usage(cmdMod.usage);
            cmdMod.param && _.each(cmdMod.param, function(v, i) {
                yargs.option(v.short, {
                    alias: v.full,
                    describe: v.describe,
                    // 是否必須
                    demand: v.demand
                });
            });
            yargs.help("h").alias("h", "help");
        }).argv;
        if (argv.h || argv.help) {
            yargs.help('h').argv;
            return;
        }
        
        // 執行子命令
        wf.run();
    }
} else {
    if (yargs.argv.h || yargs.argv.help) {
        yargs.usage('Usage: wf [options]')
            .example('wf test', '測試安裝是否成功')
            .example('wf ftp', 'ftp 上傳')
            .example('wf sync', '多瀏覽器同步')
            .help('h')
            .alias('h', 'help').argv;
    } else {
        shell.exec("wf -h");
    }
}

下層 lib/wf-xxx.js 負責執行 gulpfile.js 中定義的 task,例如 wf-sync.js

/**
 * 多瀏覽器同步
 */
var shell = require('shelljs');
var argv = require('yargs').argv;

module.exports = {
    run: function() {
        var cwd = wf.cwd;
        var cmd = wf.cmd;

        shell.cd(__dirname + '/../');
        shell.exec('gulp sync --cwd=' + cwd);
    },
    name: 'sync',
    desc: "多瀏覽器同步",
    usage: "Usage: wf sync"
};

底層 gulp task 負責幹活,例如 gulpfile.js 中的 sync

var browserSync = require('browser-sync').create(),

/**
 * BrowserSync task.
 * 
 *
 * Usage: `gulp sync`
 */
gulp.task('sync', function() {
    // browser sync
    browserSync.init({
        port: 3333,
        server: cwd
    });
    gulp.watch(cwd + '**/*.*').on('change', browserSync.reload);
});

再下面我們就不關心了,回頭看看各模塊的作用:

shelljs 負責執行命令,類似於 `require('child_process').spawn(CMD)`,但提供了跨平台包裝
yargs   負責接收並傳遞命令行參數,命令行->node->gulp task。此外還負責生成文檔(命令行幫助)

colors  錦上添花的東西,不重要

gulp 及 n 個插件   負責幹活

package.json 聲明依賴、入口與可執行文件位置,如下:

{
  "name": "wf",
  "version": "1.0.0",
  "description": "for better workflow",
  "main": "index.js",
  "dependencies": {},
  "devDependencies": {
    "browser-sync": "^2.14.0",
    "colors": "^1.1.2",
    "gulp": "^3.9.1",
    "gulp-rename": "^1.2.2",
    "gulp-util": "^3.0.7",
    "shelljs": "^0.7.3",
    "underscore": "^1.8.3",
    "vinyl-ftp": "^0.5.0",
    "yargs": "^4.8.1"
  },
  "bin": {
    "wf": "bin/wf.js"
  }
}

npm link 到全局之後,就可以愉快地:

wf go       # 創建項目目錄

wf sync     # 開啟 Browser Sync,進入瘋狂開發模式

wf ftp      # 上傳到 ftp 個人目錄,與 PM、UI 確認效果
wf ftp -a   # 監聽文件變化,自動 ftp,效果頻繁變更時開啟

wf ftp -r   # 開發完畢,提測,打包並上傳到 ftp 公共目錄

五。總結

優化工作流,節省時間提高效率

用 grunt?gulp?webpack?fis?rollup?npm scripts?都不重要,用順手的工具簡化日常工作才是目的

投資 2 小時回報很多年的事情,越早做越好,而且,node 讓一切變得熟悉而簡單了,純 FEer 也能分分鐘搞出一個好用的命令行工具

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論