Gulp is a build system that can improve how you develop websites by
automating common tasks, such as compiling preprocessed CSS, minifying
JavaScript and reloading the browser. In this article, we’ll see how you
can use Gulp to change your development workflow, making it faster and more efficient.
Gulp by itself doesn’t really do much, but a huge number of plugins are available, which you can see on the plugins page or by searching
Other build tools are available, such as Grunt and, more recently, Broccoli, but I believe Gulp to be superior (see the “Why Gulp?” section below). I’ve compiled a longer list of build tools written in JavaScript.
Gulp is open source and can be found on GitHub.
Put the following in your
What actually happened here, though?
We’re doing a few things in our
In the example above, the
In diagram form, this is what happens:
When there is only one task, the function doesn’t really do much. However, consider the following code:
This task will take all files matching
If you’re familiar with Grunt, then you’ll notice that this is pretty different to how Grunt does it. Grunt doesn’t use streams; instead, it takes files, runs a single task on them and saves them to new files, repeating the entire process for every task. This results in a lot of hits to the file system, making it slower than Gulp.
For a more comprehensive read on streams, check out the “Stream Handbook.”
Gulp uses node-glob to get the files from the glob or globs you specify. It’s easiest to explain using examples:
Let’s say that we have a directory named
A task may also be a list of other tasks. Suppose we want to define a
Most plugins are pretty easy to use, have good documentation and are run in the same way (by piping a stream of file objects to it). They’ll then usually modify the files (although some, such as validators, will not) and return the new files to be passed to the next plugin.
Let’s expand on our
You might have noticed that the JSHint plugin is called twice. That’s because the first line runs JSHint on the files, which only attaches a
The other two plugins are clearer: the
After running that code, the
Version 0.4.0 of gulp-load-plugins, released in early March, added lazy plugin loading, which improves performance. Plugins are not loaded until you call them, meaning that you don’t have to worry about unused plugins in
To watch a file or files, use the
Let’s say that we have a
You can also give the
When you make changes to code, BrowserSync either reloads the page or, if it is CSS, injects the CSS, meaning that the page doesn’t need to be refreshed. This is very useful if your website isn’t refresh-resistant. Suppose you’re developing four clicks into a single-page application, and refreshing the page would take you back to the starting page. With LiveReload, you would need to click four times every time you make a change. BrowserSync, however, would just inject the changes when you modify the CSS, so you wouldn’t need to click back.
BrowserSync also synchronizes clicks, form actions and your scroll position between browsers. You could open a couple of browsers on your desktop and another on an iPhone and then navigate the website. The links would be followed on all of them, and as you scroll down the page, the pages on all of the devices would scroll down (usually smoothly, too!). When you input text in a form, it would be entered in every window. And when you don’t want this behavior, you can turn it off.
BrowserSync doesn’t require a browser plugin because it serves your files for you (or proxies them, if they’re dynamic) and serves a script that opens a socket between the browser and the server. This hasn’t caused any problems for me in the past, though.
There isn’t actually a plugin for Gulp because BrowserSync doesn’t manipulate files, so it wouldn’t really work as one. However, the BrowserSync module on npm can be called directly from Gulp. First, install it through npm:
The developer of BrowserSync has written about some other things you can do in his BrowserSync + Gulp repository.
The two most popular build tools in JavaScript are Grunt and Gulp. Grunt was very popular in 2013 and completely changed how a lot of people develop websites. Thousands of plugins are available for it, doing everything from linting, minifying and concatenating code to installing packages using Bower and starting an Express server. This approach is pretty different from Gulp’s, which has only plugins to perform small individuals tasks that do things with files. Because tasks are just JavaScript (unlike the large object that Grunt uses), you don’t need a plugin; you can just start an Express server the normal way.
Grunt tasks tend to be over-configured, requiring a large object containing properties that you really wouldn’t want to have to care about, while the same task in Gulp might take up only a few lines. Let’s look at a simple
Because Grunt hits the file system far more often than Gulp, which uses streams, Gulp is nearly always much faster than Grunt. For a small LESS file, the
from https://www.smashingmagazine.com/2014/06/building-with-gulp/
What Is Gulp? Link
Gulp is a build system, meaning that you can use it to automate common tasks in the development of a website. It’s built on Node.js, and both the Gulp source and your Gulp file, where you define tasks, are written in JavaScript (or something like CoffeeScript, if you so choose). This makes it perfect if you’re a front-end developer: You can write tasks to lint your JavaScript and CSS, parse your templates, and compile your LESS when the file has changed (and these are just a few examples), and in a language that you’re probably already familiar with.Gulp by itself doesn’t really do much, but a huge number of plugins are available, which you can see on the plugins page or by searching
gulpplugin
on npm. For example, there are plugins to run JSHint, compile your CoffeeScript, run Mocha tests and even update your version number.Other build tools are available, such as Grunt and, more recently, Broccoli, but I believe Gulp to be superior (see the “Why Gulp?” section below). I’ve compiled a longer list of build tools written in JavaScript.
Gulp is open source and can be found on GitHub.
Installing Gulp Link
Installing Gulp is pretty easy. First, install the Gulp package globally:npm install -g gulp
Then, install it in your project:npm install --save-dev gulp
Using Gulp Link
Let’s create a Gulp task to minify one of our JavaScript files. Create a file namedgulpfile.js
. This is where you will define your Gulp tasks, which will be run using the gulp
command.Put the following in your
gulpfile.js
file:var gulp = require('gulp'),
uglify = require('gulp-uglify');
gulp.task('minify', function () {
gulp.src('js/app.js')
.pipe(uglify())
.pipe(gulp.dest('build'))
});
Install gulp-uglify
through npm by running npm install --save-dev gulp-uglify
, and then run the task by running gulp minify
. Assuming that you have a file named app.js
in the js
directory, a new app.js
will be created in the build directory containing the minified contents of js/app.js
.What actually happened here, though?
We’re doing a few things in our
gulpfile.js
file. First, we’re loading the gulp
and gulp-uglify
modules:var gulp = require('gulp'),
uglify = require('gulp-uglify');
Then, we’re defining a task named minify
, which, when run, will call the function provided as the second argument:gulp.task('minify', function () {
});
Finally — and this is where it gets tricky — we’re defining what our task should actually do:gulp.src('js/app.js')
.pipe(uglify())
.pipe(gulp.dest('build'))
Unless you’re familiar with streams, which most front-end developers are not, the code above won’t mean much to you.Streams Link
Streams enable you to pass some data through a number of usually small functions, which will modify the data and then pass the modified data to the next function.In the example above, the
gulp.src()
function takes a
string that matches a file or number of files (known as a “glob”), and
creates a stream of objects representing those files. They are then
passed (or piped) to the uglify()
function, which takes the file objects and returns new file objects with a minified source. That output is then piped to the gulp.dest()
function, which saves the changed files.In diagram form, this is what happens:
When there is only one task, the function doesn’t really do much. However, consider the following code:
gulp.task('js', function () {
return gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(uglify())
.pipe(concat('app.js'))
.pipe(gulp.dest('build'));
});
To run this yourself, install gulp
, gulp-jshint
, gulp-uglify
and gulp-concat
.This task will take all files matching
js/*.js
(i.e. all JavaScript files from the js
directory), run JSHint on them and print the output, uglify each file, and then concatenate them, saving them to build/app.js
. In diagram form:If you’re familiar with Grunt, then you’ll notice that this is pretty different to how Grunt does it. Grunt doesn’t use streams; instead, it takes files, runs a single task on them and saves them to new files, repeating the entire process for every task. This results in a lot of hits to the file system, making it slower than Gulp.
For a more comprehensive read on streams, check out the “Stream Handbook.”
gulp.src() Link
Thegulp.src()
function takes a glob (i.e. a string
matching one or more files) or an array of globs and returns a stream
that can be piped to plugins.Gulp uses node-glob to get the files from the glob or globs you specify. It’s easiest to explain using examples:
js/app.js
Matches the exact filejs/*.js
Matches all files ending in.js
in thejs
directory onlyjs/**/*.js
Matches all files ending in.js
in thejs
directory and all child directories!js/app.js
Excludesjs/app.js
from the match, which is useful if you want to match all files in a directory except for a particular file*.+(js|css)
Matches all files in the root directory ending in.js
or.css
Let’s say that we have a directory named
js
containing
JavaScript files, some minified and some not, and we want to create a
task to minify the files that aren’t already minified. To do this, we
match all JavaScript files in the directory and then exclude all files
ending in .min.js
:gulp.src(['js/**/*.js', '!js/**/*.min.js'])
Defining Tasks Link
To define a task, use thegulp.task()
function. When you define a simple task, this function takes two attributes: the task’s name and a function to run.gulp.task('greet', function () {
console.log('Hello world!');
});
Running gulp greet
will result in “Hello world” being printed to the console.A task may also be a list of other tasks. Suppose we want to define a
build
task that runs three other tasks, css
, js
and imgs
. We can do this by specifying an array of tasks, instead of the function:gulp.task('build', ['css', 'js', 'imgs']);
These will be run asynchronously, so you can’t assume that the css
task will have finished running by the time js
starts — in fact, it probably won’t have. To make sure that a task has
finished running before another task runs, you can specify dependencies
by combining the array of tasks with the function. For example, to
define a css
task that checks that the greet
task has finished running before it runs, you can do this:gulp.task('css', ['greet'], function () {
// Deal with CSS here
});
Now, when you run the css
task, Gulp will execute the greet
task, wait for it to finish, and then call the function that you’ve specified.Default Tasks Link
You can define a default task that runs when you just rungulp
. You can do this by defining a task named default
.gulp.task('default', function () {
// Your default task
});
Plugins Link
You can use a number of plugins — over 600, in fact — with Gulp. You will find them listed on the plugins page or by searchinggulpplugin
on npm. Some plugins are tagged “gulpfriendly”; these aren’t plugins
but are designed to work well with Gulp. Be aware when searching
directly on npm that you won’t be able to see whether a plugin has been
blacklisted (scrolling to the bottom of the plugins page, you will see
that many are).Most plugins are pretty easy to use, have good documentation and are run in the same way (by piping a stream of file objects to it). They’ll then usually modify the files (although some, such as validators, will not) and return the new files to be passed to the next plugin.
Let’s expand on our
js
task from earlier:var gulp = require('gulp'),
jshint = require('gulp-jshint'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat');
gulp.task('js', function () {
return gulp.src('js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(uglify())
.pipe(concat('app.js'))
.pipe(gulp.dest('build'));
});
We’re using three plugins here, gulp-jshint, gulp-uglify and gulp-concat. You can see in the README
files for the plugins that they’re pretty easy to use; options are available, but the defaults are usually good enough.You might have noticed that the JSHint plugin is called twice. That’s because the first line runs JSHint on the files, which only attaches a
jshint
property to the file objects without outputting anything. You can either read the jshint
property yourself or pass it to the default JSHint reporter or to another reporter, such as jshint-stylish.The other two plugins are clearer: the
uglify()
function minifies the code, and the concat('app.js')
function concatenates all of the files into a single file named app.js
.gulp-load-plugins Link
A module that I find really useful is gulp-load-plugins, which automatically loads any Gulp plugins from yourpackage.json
file and attaches them to an object. Its most basic usage is as follows:var gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins();
You can put that all on one line (var plugins = require('gulp-load-plugins')();
), but I’m not a huge fan of inline require
calls.After running that code, the
plugins
object will contain your plugins, camel-casing their names (for example, gulp-ruby-sass
would be loaded to plugins.rubySass
). You can then use them as if they were required normally. For example, our js
task from before would be reduced to the following:var gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins();
gulp.task('js', function () {
return gulp.src('js/*.js')
.pipe(plugins.jshint())
.pipe(plugins.jshint.reporter('default'))
.pipe(plugins.uglify())
.pipe(plugins.concat('app.js'))
.pipe(gulp.dest('build'));
});
This assumes a package.json
file that is something like the following:{
"devDependencies": {
"gulp-concat": "~2.2.0",
"gulp-uglify": "~0.2.1",
"gulp-jshint": "~1.5.1",
"gulp": "~3.5.6"
}
}
In this example, it’s not actually much shorter. However, with longer
and more complicated Gulp files, it reduces a load of includes to just
one or two lines.Version 0.4.0 of gulp-load-plugins, released in early March, added lazy plugin loading, which improves performance. Plugins are not loaded until you call them, meaning that you don’t have to worry about unused plugins in
package.json
affecting performance (although you
should probably clean them up anyway). In other words, if you run a
task that requires only two plugins, it won’t load all of the plugins
that the other tasks require.Watching Files Link
Gulp has the ability to watch files for changes and then run a task or tasks when changes are detected. This feature is amazingly useful (and, for me, probably Gulp’s single most useful one). You can save your LESS file, and Gulp will turn it into CSS and update the browser without your having to do anything.To watch a file or files, use the
gulp.watch()
function, which takes a glob or array of globs (the same as gulp.src()
) and either an array of tasks to run or a callback.Let’s say that we have a
build
task that turns our template files into HTML, and we want to define a watch
task that watches our template files for changes and runs the task to turn them into HTML. We can use the watch
function as follows:gulp.task('watch', function () {
gulp.watch('templates/*.tmpl.html', ['build']);
});
Now, when we change a template file, the build
task will run and the HTML will be generated.You can also give the
watch
function a callback, instead of an array of tasks. In this case, the callback would be given an event
object containing some information about the event that triggered the callback:gulp.watch('templates/*.tmpl.html', function (event) {
console.log('Event type: ' + event.type); // added, changed, or deleted
console.log('Event path: ' + event.path); // The path of the modified file
});
Another nifty feature of gulp.watch()
is that it returns what is known as a watcher
. Use the watcher
to listen for additional events or to add files to the watch
. For example, to both run a list of tasks and call a function, you could add a listener to the change
event on the returned watcher
:var watcher = gulp.watch('templates/*.tmpl.html', ['build']);
watcher.on('change', function (event) {
console.log('Event type: ' + event.type); // added, changed, or deleted
console.log('Event path: ' + event.path); // The path of the modified file
});
In addition to the change
event, you can listen for a number of other events:end
Fires when the watcher ends (meaning that tasks and callbacks won’t be called anymore when files are changed)error
Fires when an error occursready
Fires when the files have been found and are being watchednomatch
Fires when the glob doesn’t match any files
watcher
object also contains some methods that you can call:watcher.end()
Stops thewatcher
(so that no more tasks or callbacks will be called)watcher.files()
Returns a list of files being watched by thewatcher
watcher.add(glob)
Adds files to thewatcher
that match the specified glob (also, accepts an optional callback as a second argument)watcher.remove(filepath)
Removes a particular file from thewatcher
Reloading Changes In The Browser Link
You can get Gulp to reload or update the browser when you — or, for that matter, anything else, such as a Gulp task — changes a file. There are two ways to do this. The first is to use the LiveReload plugin, and the second is to use BrowserSyncLiveReload Link
LiveReload combines with browser extensions (including a Chrome extension) to reload your browser every time a change to a file is detected. It can be used with the gulp-watch plugin or with the built-ingulp.watch()
that I described earlier. Here’s an example from the README
file of the gulp-livereload repository:var gulp = require('gulp'),
less = require('gulp-less'),
livereload = require('gulp-livereload'),
watch = require('gulp-watch');
gulp.task('less', function() {
gulp.src('less/*.less')
.pipe(watch())
.pipe(less())
.pipe(gulp.dest('css'))
.pipe(livereload());
});
This will watch all files matching less/*.less
for changes. When a change is detected, it will generate the CSS, save the files and reload the browser.BrowserSync Link
An alternative to LiveReload is available. BrowserSync is similar in that it displays changes in the browser, but it has a lot more functionality.When you make changes to code, BrowserSync either reloads the page or, if it is CSS, injects the CSS, meaning that the page doesn’t need to be refreshed. This is very useful if your website isn’t refresh-resistant. Suppose you’re developing four clicks into a single-page application, and refreshing the page would take you back to the starting page. With LiveReload, you would need to click four times every time you make a change. BrowserSync, however, would just inject the changes when you modify the CSS, so you wouldn’t need to click back.
BrowserSync also synchronizes clicks, form actions and your scroll position between browsers. You could open a couple of browsers on your desktop and another on an iPhone and then navigate the website. The links would be followed on all of them, and as you scroll down the page, the pages on all of the devices would scroll down (usually smoothly, too!). When you input text in a form, it would be entered in every window. And when you don’t want this behavior, you can turn it off.
BrowserSync doesn’t require a browser plugin because it serves your files for you (or proxies them, if they’re dynamic) and serves a script that opens a socket between the browser and the server. This hasn’t caused any problems for me in the past, though.
There isn’t actually a plugin for Gulp because BrowserSync doesn’t manipulate files, so it wouldn’t really work as one. However, the BrowserSync module on npm can be called directly from Gulp. First, install it through npm:
npm install --save-dev browser-sync
Then, the following gulpfile.js
will start BrowserSync and watch some files:var gulp = require('gulp'),
browserSync = require('browser-sync');
gulp.task('browser-sync', function () {
var files = [
'app/**/*.html',
'app/assets/css/**/*.css',
'app/assets/imgs/**/*.png',
'app/assets/js/**/*.js'
];
browserSync.init(files, {
server: {
baseDir: './app'
}
});
});
Running gulp browser-sync
would then watch the matching files for changes and start a server that serves the files in the app
directory.The developer of BrowserSync has written about some other things you can do in his BrowserSync + Gulp repository.
Why Gulp? Link
As mentioned, Gulp is one of quite a few build tools available in JavaScript, and other build tools not written in JavaScript are available, too, including Rake. Why should you choose Gulp?The two most popular build tools in JavaScript are Grunt and Gulp. Grunt was very popular in 2013 and completely changed how a lot of people develop websites. Thousands of plugins are available for it, doing everything from linting, minifying and concatenating code to installing packages using Bower and starting an Express server. This approach is pretty different from Gulp’s, which has only plugins to perform small individuals tasks that do things with files. Because tasks are just JavaScript (unlike the large object that Grunt uses), you don’t need a plugin; you can just start an Express server the normal way.
Grunt tasks tend to be over-configured, requiring a large object containing properties that you really wouldn’t want to have to care about, while the same task in Gulp might take up only a few lines. Let’s look at a simple
gruntfile.js
that defines a css
task to convert our LESS to CSS and then run Autoprefixer on it:grunt.initConfig({
less: {
development: {
files: {
"build/tmp/app.css": "assets/app.less"
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 version', 'ie 8', 'ie 9']
},
multiple_files: {
expand: true,
flatten: true,
src: 'build/tmp/app.css',
dest: 'build/'
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.registerTask('css', ['less', 'autoprefixer']);
Compare this to the gulpfile.js
file that does the same:var gulp = require('gulp'),
less = require('gulp-less'),
autoprefix = require('gulp-autoprefixer');
gulp.task('css', function () {
gulp.src('assets/app.less')
.pipe(less())
.pipe(autoprefix('last 2 version', 'ie 8', 'ie 9'))
.pipe(gulp.dest('build'));
});
The gulpfile.js
version is considerably more readable and smaller.Because Grunt hits the file system far more often than Gulp, which uses streams, Gulp is nearly always much faster than Grunt. For a small LESS file, the
gulpfile.js
file above would usually take about 6 milliseconds. The gruntfile.js
would usually take about 50 milliseconds — more than eight times
longer. This is a tiny example, but with longer files, the amount of
time increases significantly.from https://www.smashingmagazine.com/2014/06/building-with-gulp/
No comments:
Post a Comment