An overview of the
As one of the most popular front-end construction systems, Gulp is characterized by efficiency and ease of use. It is very simple to use, basically installing Gulp’s development dependencies in your project, then creating a gulpfile.js file in your project root directory to write the build tasks that Gulp automatically executes, and then running those tasks from the command line using the GULp-provided CLI.
Method of use
The basic use
Create an empty project folder and initialize the package.json file with YARN init –yes or NPM init -y.
Gulp is installed into the project as a development dependency:
Gulp -CLI is automatically installed when you install gulp, and with this scaffolding tool you can use it to run build tasks later. Create a gulpfile.js file in the project root directory where you can write tasks for automatic builds:
Defining a build task in gulpfile.js is defined using an export function member:
Thus, a task named foo is defined in gulp, which can be executed by running gulp through YARN:
However, an error is reported saying “Task foo did not complete, did you forget to mark the end of the task?” This is because the synchronous code pattern has been removed in the latest gulP version, which stipulates that every task is an asynchronous task. When the task is finished, you need to call the callback function or use specific code to indicate that the task is complete.
In the current task, we can write a parameter named done to the function, and call this parameter when the task is finished to mark the completion of the task:
The foo task should then end normally.
If the task name is default, it appears as the default task of gulp. You can directly use YARN gulp to execute the task
Before gulp 4.0, it was necessary to introduce gulp in gulpfile.js and register tasks with the following task method:
const gulp = require('gulp')
gulp.task('bar'.done= > {
console.log('bar')
done()
})
Copy the code
Combination of task
In gulp, it provides two apis, Series and Parallel, to create serial and parallel tasks for us.
Using the series:
const { series } = require('gulp')
const task1 = done= > {
setTimeout(() = > {
console.log('task1 done')
done()
}, 3000)}const task2 = done= > {
setTimeout(() = > {
console.log('task2 done')
done()
}, 2000)}const task3 = done= > {
setTimeout(() = > {
console.log('task3 done')
done()
}, 1000)}exports.foo = series(task1, task2, task3)
Copy the code
Running results:
You can findseries
Tasks are executed one after another.
Using the parallel:
const { parallel } = require('gulp')
const task1 = done= > {
setTimeout(() = > {
console.log('task1 done')
done()
}, 3000)}const task2 = done= > {
setTimeout(() = > {
console.log('task2 done')
done()
}, 2000)}const task3 = done= > {
setTimeout(() = > {
console.log('task3 done')
done()
}, 1000)}exports.foo = parallel(task1, task2, task3)
Copy the code
The execution result is as follows:
Here you can see that the tasks are executed in parallel.
Asynchronous tasks
In both cases, a callback function is called to indicate the completion of the task. In asynchronous cases, it is how to notify gulp of the completion of the task. Gulp has a number of solutions to this problem:
The callback function
exports.callback = done= > {
console.log('cb')
done()
}
Copy the code
As before, gulp is notified of the completion of the task by calling the done function in the argument. And like the node callback, this is an error-first callback. That is, if you want to report an error while the task is executing to prevent further execution, you can pass an error object to done() :
exports.callback_err = done= > {
console.log('cb')
done(new Error('cb err'))}Copy the code
Perform this task:
A CB err error is reported here. And if this is multiple tasks running simultaneously, subsequent tasks will not be executed.
Promise
Promise is also supported in GULp as a callback solution.
exports.promise = () = > {
console.log('promise task')
// return Promise.resolve()
return Promise.reject()
}
Copy the code
When using promises here, note that the returned promise in the Resolved state indicates that the task has been successfully executed. If the returned Promise in the Rejected state indicates that the task has failed.
There is no need to pass a value in the resolve method in the return promise.resolve () statement because gulp ignores it.
Async/Await
With Node versions 8.0 or older, you can also use syntactic sugars such as async await to handle asynchronous tasks:
const timeout = time= > {
return new Promise(resolve= > setTimeout(resolve, time))
}
exports.async = async() = > {await timeout(1000)
console.log('async-await task')}Copy the code
The results are as follows:
Stream
Reading and writing files is also a common operation in build tools, and it is also asynchronous. In a gulp task, you can also return a stream object:
const fs = require('fs')
exports.stream = () = > {
const read = fs.createReadStream('package.json')
const write = fs.createWriteStream('pack.txt')
read.pipe(write)
return read
}
Copy the code
The execution result is as follows:
A new pack. TXT file will also be created in the folder:
Gulp build process
Most of the build process is to read the file and then write it to the specified location after a specific transformation. Create a CSS file under the empty project. You can write any styles in it:
Create the read stream and write stream using the FS module.
const fs = require('fs')
exports.default = () = > {
// File read stream
const read = fs.createReadStream('init.css')
// Write the file to the stream
const write = fs.createWriteStream('init.min.css')
// Import the read file into the stream
read.pipe(write)
return read
}
Copy the code
After executing this task, you can see another init.min.css file in the project. But this is just a simple copy operation, so you also need to introduce a conversion flow:
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () = > {
// File read stream
const read = fs.createReadStream('init.css')
// Write the file to the stream
const write = fs.createWriteStream('init.min.css')
// File conversion stream
const transform = new Transform({
// This transform property is the core transformation process
transform(chunk, encoding, callback) {
// chunk is the content read in the read stream
const input = chunk.toString()
const output = input.replace(/\s+/g.' ').replace(/ / \ \ *. +? \*\//g.' ')
callback(null, output)
}
})
// The read file is first imported into the conversion stream and then imported into the write stream
read
.pipe(transform)
.pipe(write)
return read
}
Copy the code
The first argument in callback is an error object, and null is passed in if there is no error
After executing the gulp task, you can see a compressed CSS file.
As can be seen from the above, the working process of GULP has three core concepts: read stream, transform stream and write stream:
We can read the object file by reading the stream, import it into the transform stream and convert it to the desired result, and then write it to the specified location by writing to the stream. This process does what we need to do in the build process.
File manipulation API + plug-in use
The file-reading API in Gulp is more powerful and easier to use than the underlying Node API, and the intermediate conversion flow is mostly done through plug-ins. Therefore, the process for creating a build task through gulp is as follows:
-
The SRC method is used to create the read stream
-
File processing is realized with the conversion flow provided by the plug-in
-
Create a write stream using the dest method
Let’s start with an example:
const { src, dest } = require('gulp')
exports.default = () = > {
return src('./src/init.css')
.pipe(dest('dist'))}Copy the code
After executing the task, you’ll see a dist folder in the root directory with an init.css file init. This means that the read and write streams of the task work properly.
The SRC method also uses wildcards to match bulk files:
src('./src/*.css')
Copy the code
This will read all CSS files in the SRC folder. If you want to compress CSS, you can install gulp-clean-css.
const { src, dest } = require('gulp')
const cleanCss = require('gulp-clean-css')
exports.default = () = > {
return src('./src/init.css')
.pipe(cleanCss())
.pipe(dest('dist'))}Copy the code
This way, all the compressed code will be in the dist folder.
And if you want to use another transform flow plug-in, you can add more PIPE operations in between.
Gulp automated build example
Let’s start with the basic structure of the project
Gulp: yarn add gulp –dev
Compile style
Compile the style file in SRC /assets/styles to a normal CSS file:
const { src, dest } = require('gulp')
// define a private method
const style = () = > {
// Reads all the SCSS files in the styles folder
return src('./src/assets/styles/*.scss')
.pipe(dest('dist'))}// exports on demand through modules.exports
module.exports = {
style,
}
Copy the code
After executing the YARN gulp Style task, you will find a dist folder in the root directory, which contains all the style files.
But the file does not output as it was, so I have to add:
const { src, dest } = require('gulp')
const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(dest('dist'))}module.exports = {
style,
}
Copy the code
That will preserve the original structure. Yarn add gulp-sass –dev Use this transformation flow in a task:
const { src, dest } = require('gulp')
const sass = require('gulp-sass')
const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(sass())
.pipe(dest('dist'))}module.exports = {
style,
}
Copy the code
When the file name is specified by
_
At the beginning, will not be converted. And by default, the parentheses following the selector are folded to the previous line:This can be done by givingsass
Specify an option to modify this phenomenon:
sass({ outputStyle: 'expanded' }) Copy the code
The script compiler
Yarn add gulp-babel@babel/core@babel /preset-env –dev, this Babel can convert some advanced syntax to ES5 for compatibility issues of browsers.
The code read and written is the same as for style compilation:
const { src, dest } = require('gulp')
const script = () = > {
return src('./src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))}module.exports = {
script,
}
Copy the code
The js file has been translated by executing the following tasks:
Template compilation
To abstract out reusable parts of an HTML file, use a template engine such as SWIg
Swig: yarn add gulp — swig –dev
const swig = require('gulp-swig')
const page = () = > {
return src('./src/**/*.html', { base: 'src' })
.pipe(swig())
.pipe(dest('dist'))}Copy the code
The template tags have been replaced with HTML structures:
There are also interpolations in which data can be passed in via the data property of the configuration item in the swig method:
swig({ data: data })
Copy the code
The parallel method can be used to process the above three tasks in parallel. The overall result is:
const { src, dest, parallel } = require('gulp')
const sass = require('gulp-sass')
const babel = require('gulp-babel')
const swig = require('gulp-swig')
const data = {
menus: [{name: 'Home'.icon: 'aperture'.link: 'index.html'
},
{
name: 'Features'.link: 'features.html'
},
{
name: 'About'.link: 'about.html'
},
{
name: 'Contact'.link: The '#'.children: [{name: 'Twitter'.link: 'https://twitter.com/w_zce'
},
{
name: 'About'.link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About'.link: 'https://github.com/zce'}}]].pkg: require('./package.json'),
date: new Date()}const page = () = > {
return src('./src/**/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'))}const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))}const script = () = > {
return src('./src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))}const compile = parallel(style, script, page)
module.exports = {
compile
}
Copy the code
After this processing, you only need to run YARN gulp compile to execute the style, script, and page tasks in parallel.
Image and font files
Yarn add gulp-imagemin –dev, and create a task in gulpfile.js:
const imagemin = require('gulp-imagemin')
const image = () = > {
return src('./src/assets/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))}Copy the code
The plugin will compress the image by performing the following task:
Font files in SVG format can also be processed using Imagemin:
const font = () = > {
return src('./src/assets/fonts/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))}Copy the code
Finally, drop both tasks into the parallel method as well:
Other files and file removal
For other files, you just need to read and write directly:
const extra = () = > {
return src('./public/**', { base: 'src' })
.pipe(dest('dist'))}Copy the code
To clear files, you can use the del plug-in: yarn add del –dev
const del = require('del')
const clean = () = > {
// the del method passes in the directory to be cleared
return del('dist')}Copy the code
Then use the series method to process the clean method and other methods in sequence. The overall code is as follows:
const { src, dest, parallel, series } = require('gulp')
const sass = require('gulp-sass')
const babel = require('gulp-babel')
const swig = require('gulp-swig')
const imagemin = require('gulp-imagemin')
const del = require('del')
const clean = () = > {
return del('dist')}const font = () = > {
return src('./src/assets/fonts/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))}const image = () = > {
return src('./src/assets/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))}const data = {
menus: [{name: 'Home'.icon: 'aperture'.link: 'index.html'
},
{
name: 'Features'.link: 'features.html'
},
{
name: 'About'.link: 'about.html'
},
{
name: 'Contact'.link: The '#'.children: [{name: 'Twitter'.link: 'https://twitter.com/w_zce'
},
{
name: 'About'.link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About'.link: 'https://github.com/zce'}}]].pkg: require('./package.json'),
date: new Date()}const page = () = > {
return src('./src/**/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'))}const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))}const script = () = > {
return src('./src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))}const extra = () = > {
return src('./public/**', { base: 'src' })
.pipe(dest('dist'))}const compile = parallel(style, script, page, image, font)
const build = series(clean, parallel(compile, extra))
module.exports = {
build,
}
Copy the code
This way, all you need to do is execute YARN Gulp Build to get the whole build going.
Autoloading plug-in
Gulp-load-plugins –dev: yarn add gulp-load-plugins –dev: yarn add gulp-load-plugins
Do something about the contents of gulpfile.js:
const { src, dest, parallel, series } = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const plugins = loadPlugins()
const clean = () = > {
return del('dist')}const font = () = > {
return src('./src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))}const image = () = > {
return src('./src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))}const data = {
menus: [{name: 'Home'.icon: 'aperture'.link: 'index.html'
},
{
name: 'Features'.link: 'features.html'
},
{
name: 'About'.link: 'about.html'
},
{
name: 'Contact'.link: The '#'.children: [{name: 'Twitter'.link: 'https://twitter.com/w_zce'
},
{
name: 'About'.link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About'.link: 'https://github.com/zce'}}]].pkg: require('./package.json'),
date: new Date()}const page = () = > {
return src('./src/**/*.html', { base: 'src' })
.pipe(plugins.swig({ data }))
.pipe(dest('dist'))}const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))}const script = () = > {
return src('./src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))}const extra = () = > {
return src('./public/**', { base: 'src' })
.pipe(dest('dist'))}const compile = parallel(style, script, page, image, font)
const build = series(clean, parallel(compile, extra))
module.exports = {
build,
}
Copy the code
Hot update the development server
In the previous task, you had to start the build task again every time you modified the source code in the SRC directory to refresh it. So you need a module to implement hot update. The browser-sync command is yarn add browser-sync
Use it in your project:
const browserSync = require('browser-sync')
const bs = browserSync.create()
const serve = () = > {
bs.init({
server: {
// The browser will be re-rendered after the files in the specified directory are sent
files: 'dist/**'.// Specify the project directory to open
baseDir: 'dist'}})}module.exports = {
build,
serve,
}
Copy the code
Perform the serve task and you’ll see that the project is automatically opened in the browser and can be re-rendered if the files in the dist directory change. However, the dependencies on node_modules and the inability to update the project source code when it changes have not been resolved.
Monitor file changes
Monitoring file changes can be handled using the Watch method provided in gulp.
const { src, dest, parallel, series, watch } = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const browserSync = require('browser-sync')
const plugins = loadPlugins()
const bs = browserSync.create()
const clean = () = > {
return del('dist')}const font = () = > {
return src('./src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))}const image = () = > {
return src('./src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))}const data = {
menus: [{name: 'Home'.icon: 'aperture'.link: 'index.html'
},
{
name: 'Features'.link: 'features.html'
},
{
name: 'About'.link: 'about.html'
},
{
name: 'Contact'.link: The '#'.children: [{name: 'Twitter'.link: 'https://twitter.com/w_zce'
},
{
name: 'About'.link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About'.link: 'https://github.com/zce'}}]].pkg: require('./package.json'),
date: new Date()}const page = () = > {
return src('./src/**/*.html', { base: 'src' })
.pipe(plugins.swig({
data,
cache: false
}))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true}}))const style = () = > {
return src('./src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true}}))const script = () = > {
return src('./src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
.pipe(bs.reload({ stream: true}}))const extra = () = > {
return src('./public/**', { base: 'src' })
.pipe(dest('dist'))}const compile = parallel(style, script, page, image, font)
const build = series(clean, parallel(compile, extra))
const serve = () = > {
// watch('./src/assets/fonts/**', font)
watch('./src/assets/scripts/*.js', script)
watch('./src/assets/styles/*.scss', style)
watch('./src/**/*.html', page)
// watch('./src/assets/images/**', image)
// watch('./public/**', extra)
watch([
'./src/assets/fonts/**'.'./src/assets/images/**'.'./public/**'
], bs.reload)
bs.init({
files: 'dist/**'.server: {
baseDir: ['dist'.'src'.'public'].routes: {
'/node_modules': 'node_modules'}}})}module.exports = {
build,
serve,
}
Copy the code
useref
To handle node_modules, run gulp-useref: yarn add gulp-useref,
const useref = () = > {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist'.'. '] }))
.pipe(dest('dist'))}Copy the code
After testing, references in index.html in the dist directory will also be packaged into dist: