Preface
5 minutes is yesterday's meme, don't hit me, haha
I. Why gulp?
Speaking of build tools (bundling tools), think of the sad grunt, and that note from May last year: [Grunt Tutorial](/articles/grunt 教程/)
There are many build tools to choose from:
-
grunt that crazily writes configuration files
-
gulp that pipes here and there
-
webpack with a great logo
-
npm scripts that doesn't like beating around the bush
-
rollup that claims to be epoch-making
-
...
But why gulp?
First, grunt is outdated, last May grunt could barely hold on (plugin count was much more than gulp, that's why there was that note), now there's no advantage anymore, plugin count is no longer gulp's shortcoming
As npm scripts says, pipe is not gulp's original creation, operating system already supports it, so npm scripts' philosophy is face shell commands directly, remove unnecessary abstraction layers (grunt, gulp...), for example gulp-jshint, just execute jshint's provided cli directly, what do you need gulp for?
As for webpack/rollup, should be similar to grunt/gulp (all belong to unnecessary abstraction layers in npm scripts' eyes), no need to change build tools every day. So, choosing gulp is merely because of cost (team was already using it)
II. npm scripts
From a design philosophy perspective, npm scripts seems more advanced, several reasons:
-
Remove intermediate abstraction layer, directly use cli provided by package, can use package's new features without waiting for plugin updates
-
Simpler structure, reduces debugging complexity (package problem?
configuration problem?plugin version/plugin itself problem?project code problem?) -
Can execute any script that operating system can execute (python, bash scripts, etc., gulp + shelljs barely keeps up, grunt fell behind)
-
No plugins, but "plugin" count is the most (why wait for others to write plugins? All scripts that can be executed through command line are "plugins")
Of course, there are also some disadvantages:
-
package.json cannot add comments. Write in readme? Still a bit uncomfortable
-
Need to know a little about command line. Although npm encapsulates over 1000 basic commands, the process from "delete folder" to
rm -rfto rimraf is still too tortuous, understanding common commands is necessary -
Doesn't support variables. Supports environment variables and any scripts, enough (
"build": "node bin/build.js")
Example:
// 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"
}
}
More complex commands can write a bash shell script, or node + shelljs, nothing extra
III. Create a node Command Line Tool
Want to customize a global command, can execute in any directory? Actually very easy
-
Write nodejs code, add Shebang (add
#!/usr/bin/env nodeon first line of file, indicates using node to execute this file), save asxxx.js -
npm initor directly handwrite apackage.json -
Modify package.json, add
"bin": {"hoho": "xxx.js"} -
npm linklink local package to global (similar to windows environment variable path)
Key steps are like this, but exist several problems:
-
How to make command cross-platform? (windows, unix)
-
How to get command line arguments?
-
How to use
gulp?
shelljs module provides cross-platform commands; yargs module can get command line arguments; using gulp needs npm install gulp --save-dev locally install gulp and add dependency; additionally, want to output colorful things in command line, can also use colors module
Note: Locally installing gulp is necessary, because gulp's design philosophy is not wanting all packages to depend on one global gulp, so to require('gulp') must locally install one, feels a bit uncomfortable with many more files, but more flexible
Command line tool is just step 0, important but not critical, specific steps can see this: Nodejs Making Command Line Tools
IV. Optimize Workflow with gulp
"Workflow" here includes but not limited to basic bundling steps (compilation, code checking, resource minification, etc.), for example tools needed during development, and tools needed for release, all belong to workflow. The process of optimizing workflow is simplifying these steps, handing boring and tedious things to tools to handle, we can free ourselves to do more things, thereby improving efficiency
To make tools easy to extend and maintain, need to consider structure, for example:
.
|____bin
| |____wf.js # Command line entry (receives command line instructions, distributes to sub-commands)
|____config.json # Configuration parameters (such as paths glob that need to be synced)
|____gulpfile.js # Define gulp task
|____index.js # Package entry (package.main)
|____lib # Sub-commands
| |____wf-ftp.js # Self-test, submit for testing
| |____wf-sync.js # Multi-browser sync
| |____wf-test.js
|____node_modules
| |____...
|____package.json
|____README.md
Among them wf-sync is a wrapper for Browser Sync, supports multi-browser sync (refresh sync, scroll sync, style change sync, etc.), very worry-free during development
wf-ftp provides command line ftp support, can configure paths glob to upload and remote paths, remote environment is relatively stable, used to confirm effects with PM, UI during development, after development completes wf ftp -r bundle and submit for testing
Key in above structure are index and wf, former responsible for initialization, latter similar to routing
Top-level index.js responsible for initializing global settings, and agreeing on sub-commands, providing sub-command execution methods
var argv = require('yargs').argv;
var colors = require("colors");
var path = require("path");
var config = require("./config.json");
// Command line output colorful things
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;
// Execute sub-commands (agree on sub-command calling method)
wf.run = function() {
//Current command
var cmd = process.argv[2];
//Execution path
var cwd = argv.path || argv.p || process.cwd();
wf.cmd = cmd;
wf.cwd = cwd;
var cmdFile = require("./lib/wf-" + cmd);
// Require each module in lib must export a run() method as entry
cmdFile.run && cmdFile.run();
};
module.exports = wf;
Middle-level bin/wf.js responsible for routing control, as follows:
#!/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) {
// Set sub-commands
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,
// Whether required
demand: v.demand
});
});
yargs.help("h").alias("h", "help");
}).argv;
if (argv.h || argv.help) {
yargs.help('h').argv;
return;
}
// Execute sub-commands
wf.run();
}
} else {
if (yargs.argv.h || yargs.argv.help) {
yargs.usage('Usage: wf [options]')
.example('wf test', 'Test if installation is successful')
.example('wf ftp', 'ftp upload')
.example('wf sync', 'Multi-browser sync')
.help('h')
.alias('h', 'help').argv;
} else {
shell.exec("wf -h");
}
}
Lower-level lib/wf-xxx.js responsible for executing tasks defined in gulpfile.js, for example wf-sync.js:
/**
* Multi-browser sync
*/
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: "Multi-browser sync",
usage: "Usage: wf sync"
};
Bottom-level gulp task responsible for doing work, for example sync in gulpfile.js:
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);
});
Below that we don't care anymore, look back at each module's role:
shelljs Responsible for executing commands, similar to `require('child_process').spawn(CMD)`, but provides cross-platform wrapper
yargs Responsible for receiving and passing command line arguments, command line->node->gulp task. Additionally responsible for generating documentation (command line help)
colors Icing on the cake, not important
gulp and n plugins Responsible for doing work
package.json declares dependencies, entry and executable file location, as follows:
{
"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"
}
}
After npm link to global, can happily:
wf go # Create project directory
wf sync # Start Browser Sync, enter crazy development mode
wf ftp # Upload to ftp personal directory, confirm effects with PM, UI
wf ftp -a # Listen for file changes, automatically ftp, enable when effects change frequently
wf ftp -r # Development complete, submit for testing, bundle and upload to ftp public directory
V. Summary
Optimize workflow, save time and improve efficiency
Use grunt? gulp? webpack? fis? rollup? npm scripts? None important, using handy tools to simplify daily work is the purpose
Invest 2 hours return many years thing, the earlier do the better, and, node makes everything familiar and simple, pure FEer can also quickly create a useful command line tool
References
-
Why I Gave Up Gulp and Grunt, Switched to npm scripts (Part 2): These 3 articles are all good, objectively accept
No comments yet. Be the first to share your thoughts.