はじめに
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 コマンドラインツールを捏造する
グローバルコマンドをカスタマイズしたい?どのディレクトリでも実行可能?実はとても簡単です
-
nodejs コードを编写し、Shebang を追加(ファイルの先頭行に
#!/usr/bin/env nodeを追加、node でこのファイルを実行することを示す)、xxx.jsとして保存 -
npm initまたは直接package.jsonを手書き -
package.json を修改し、
"bin": {"hoho": "xxx.js"}を追加 -
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-sync は Browser Sync の包装で、複数ブラウザ同期(リフレッシュ同期、スクロール同期、スタイル変化同期など)をサポートし、開発過程中で非常に楽です
wf-ftp はコマンドライン ftp サポートを提供し、アップロード待ちパス glob およびリモートパスを設定可能。リモート環境は比較的安定しており、開発過程中に PM、UI と効果を確認するために使用。開発完了後 wf ftp -r でバンドルして提測
上記構造で鍵となるのは index と wf で、前者は初期化を担当し、後者はルーティングに類似
トップレベル 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.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 でも分分钟で使いやすいコマンドラインツールを作れる
参考資料
-
私がなぜ Gulp と Grunt を放棄して npm scripts に乗り換えたか(中):この 3 篇はすべて良く、客観的に受け入れる
コメントはまだありません