gulp和webpack一样都是作为打包工具,但现在基本已经退出舞台了,但是只有知道他为什么现在会退出舞台,我们才可以更好理解到webpack等工具的理念,以及带来的便利之处。
Gulp和webpack的区别
gulp的核心理念是task runner,
* 可以定义自己的一系列任务,等待任务被执行
* 基于文件stream的构建流
* 使用gulp的插件体系来完成某些任务
Webpack的核心理念是模块化
- 模块化打包工具
- 使用loader对不同的文件进行解析
- 使用插件在webpack的生命周期进行其他任务
Gulp的基本使用
gulp的安装
npm install gulp
yarn add gulp
gulp的使用需要创建一个gulpfile或者Gulpfile的js文件,在该js文件中就可以创建任务,并且进行执行。
任务
以下是一个简单的例子
const gulp = require('gulp');
function func() {
console.log("func 执行")
}
module.exports = {
func
}
通过npx gulp func 进行执行名为func的任务,此时任务台会出现如下的打印
Using gulpfile ~/gulp/gulpfile.js
[12:01:31] Starting 'func'...
func 执行
[12:01:31] The following tasks did not complete: func
[12:01:31] Did you forget to signal async completion?
其中发现gulp给我们一个提示,是否忘记去完成一个异步函数的调用,那么很明显,我们只有一个func这么一个函数,那么明明他是同步的,gulp会在控制台中提示我们他是一个异步的函数呢?
其实在gulp的官方文档中有提到,在gulpfile中的task(任务)也就是我们的func函数,是异步的javascript函数,那么我们怎么解决这个一步函数未结束这个问题呢? 其实很简单,每一个任务都会有一个回调函数的入参,我们只需要调用这个函数就意味着这个任务的完成。
[12:34:05] Using gulpfile ~/gulp/gulpfile.js
[12:34:05] Starting 'func'...
func 执行
[12:34:05] Finished 'func' after 1.08 ms
此时,会控制台中打印的就是完成func这一任务,并且耗时未1.08s。
并行与串行
话不多说,直接上代码进行理解。
-
串行
Gulpfile.js
const gulp = require('gulp');
function func1(cb) {
console.log("func1 执行");
cb();
}
function func2(cb) {
console.log("func2 执行");
cb();
}
module.exports = {
func1,
func2,
default: gulp.series(func1,func2)
}
运行结果为:
[12:40:55] Using gulpfile ~/gulpfile.js
[12:40:55] Starting 'default'...
[12:40:55] Starting 'func1'...
func1 执行
[12:40:55] Finished 'func1' after 776 μs
[12:40:55] Starting 'func2'...
func2 执行
[12:40:55] Finished 'func2' after 212 μs
[12:40:55] Finished 'default' after 2.36 ms
此时,发现运行顺序为fun1,之后为func2。如果我们将func1中的c()去掉,运行结果会是什么样子呢?
[12:41:09] Using gulpfile ~/gulpfile.js
[12:41:09] Starting 'default'...
[12:41:09] Starting 'func1'...
func1 执行
[12:41:09] The following tasks did not complete: default, func1
[12:41:09] Did you forget to signal async completion?
我们就只执行了func1,而没有执行func2,并且default这一任务也没有结束,说明是gulp在发现我们没有结束某一异步任务后可能帮助我们直接结束了gulp的进程。
- 并行
[12:47:33] Starting 'default'...
[12:47:33] Starting 'func1'...
[12:47:33] Starting 'func2'...
func1 执行
[12:47:33] Finished 'func1' after 863 μs
func2 执行
[12:47:33] Finished 'func2' after 1.27 ms
[12:47:33] Finished 'default' after 2.24 ms
之后我们删除func1中的cb
[12:48:53] Starting 'default'...
[12:48:53] Starting 'func1'...
[12:48:53] Starting 'func2'...
func1 执行
func2 执行
[12:48:53] Finished 'func2' after 497 μs
[12:48:53] The following tasks did not complete: default, func1
[12:48:53] Did you forget to signal async completion?
发现func1和func2都执行,但是gulp提示我们需要给default和func1添加结束调用的标识。
-
同时使用
以上说明了gulp中的并行和串行的任务的调用,其实gulp在官方中说明串行和并行的任务是可以进行无限深度的调用的。
比如以下的代码
const gulp = require('gulp'); const { parallel, series } = gulp function commonFunc(cb){ console.log("commonFun"); cb(); } function sFunc1(cb) { console.log("sFunc1"); cb(); } function sFunc2(cb) { console.log("sFunc2"); cb(); } function pFunc1(cb) { console.log("pFunc1"); cb(); } module.exports = { default: series(pFunc1,commonFunc,parallel(sFunc1,sFunc2)) }运行结果为:
[12:59:19] Starting 'default'... [12:59:19] Starting 'pFunc1'... pFunc1 [12:59:19] Finished 'pFunc1' after 451 μs [12:59:19] Starting 'commonFunc'... commonFun [12:59:19] Finished 'commonFunc' after 181 μs [12:59:19] Starting 'sFunc1'... [12:59:19] Starting 'sFunc2'... sFunc1 [12:59:19] Finished 'sFunc1' after 214 μs sFunc2 [12:59:19] Finished 'sFunc2' after 295 μs [12:59:19] Finished 'default' after 2.22 ms公有任务和私有任务
gulp中还包括私有和公有任务的概念,其实简单来说公有就是我们利用cmd导出的任务,这种任务可以由其他文件进行导入使用,私有就是并未导出的任务。
gulp中的异步执行
以下是我从gulp的官方文档对gulp的异步执行的说明
Node 库以多种方式处理异步功能。最常见的模式是 error-first callbacks,但是你还可能会遇到 streams、promises、event emitters、child processes, 或 observables。gulp 任务(task)规范化了所有这些类型的异步功能。
-
返回stream流
const { src, dest } = require('gulp'); // src 其中第一个参数为glob,传入参数,将会把其中的文件转化为文件流 // dest 将可读流,传入到可写流中,并关闭流 function streamTask() { return src('*.js') .pipe(dest('output')); } exports.default = streamTask;其中有关node中的流的部分建议阅读以下的文章:
其中gulp还有一些其他的异步执行内容,之后有时间将会补上。
gulp 打包前端文件
可以根据以上的gulp对js文件的打包,我们可以做出对html,css,js文件不同的打包文件
简单打包
const { src, dest, parallel} = require('gulp');
function htmlTask (cb) {
return src("./src/*.html")
.pipe(dest("./dist"))
}
function jsTask (cb){
return src("./src/*.js")
.pipe(dest("./dist"))
}
function cssTask (cb){
return src("./src/*.css")
.pipe(dest("./dist"))
}
exports.default = parallel(htmlTask, jsTask, cssTask);
但是此时会发现打包出来的文件和原来的文件是一模一样的,如果我们需要对代码进行丑化或者进行语法的转换,这时,我们就需要进入官方插件地址来查找对应的插件来帮助我们处理数据。
配合插件进行打包
const { src, dest, parallel, series} = require('gulp');
const htmlMin = require("gulp-htmlmin");
const babel = require("gulp-babel");
const terser = require("gulp-terser");
const gulpInject = require('gulp-inject');
function htmlTask (cb) {
return src("./src/*.html")
.pipe(htmlMin({
collapseWhitespace: true
}))
.pipe(dest("./dist"))
}
function jsTask (cb){
return src("./src/**.js")
.pipe(babel({
presets: ['@babel/preset-env']
}))
.pipe(terser({
mangle: {
toplevel: true
}
}))
.pipe(dest("./dist"))
}
function cssTask (cb){
return src("./src/*.css")
.pipe(dest("./dist"))
}
function injectTask(cb){
return src("./dist/*.html")
.pipe(gulpInject([src(['./dist/*.js','./dist/*.css'])],{
relative: true
}))
.pipe(dest("./dist "))
}
exports.default = series(parallel(htmlTask, jsTask, cssTask), injectTask);
//index.html 使用gulp-inject插件需要配置一下html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--inject:css -->
<!--enject -->
</head>
<body>
<!--inject:js -->
<!--enject -->
</body>
</html>
这样我们就实现一个简易的打包任务。
总结
那么以上就是对gulp的学习,在敲过gulp的代码后,其实一个更加理解webpack的打包理念,还有就是又补学了node中流的概念,属实不亏。