メインコンテンツへ移動

gulp で 5 分間でワークフローを最適化

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

ワークフローを最適化し、時間を節約して効率を向上

はじめに

5 分間は昨日のネタです、殴らないでください、哈哈

一.なぜ gulp なのか?

ビルドツール(バンドルツール)と言えば、悲しい grunt を思い出します。そして去年 5 月のあのノート:[Grunt チュートリアル](/articles/grunt チュートリアル/)

選択可能なビルドツールは非常に多いです:

  • 設定ファイルを狂ったように書く grunt

  • pipe して pipe して gulp

  • ロゴがとても素晴らしい webpack

  • 遠回しなのが嫌いな npm scripts

  • 時代を超えたと自称する rollup

  • ...

しかし、なぜ 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 -rf から rimraf までのプロセスはあまりにも曲折しており、常用コマンドの理解は必須

  • 変数をサポートしない。環境変数とあらゆるスクリプトをサポート、十分("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 が 1 つのグローバル gulp に依存することを望んでいないため、require('gulp') するにはローカルに 1 つインストールする必要があり、ファイルが多すぎてあまり快適ではありませんが、より柔軟です

コマンドラインツールは第 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-syncBrowser 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 中の各モジュールが必ず run() メソッドを export して入口とするよう要求
    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.jsgulpfile.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 でも分分钟で使いやすいコマンドラインツールを作れる

参考資料

コメント

コメントはまだありません

コメントを書く