在了解 webpack 的时候,也想到了之前有用到过的一个小工具 gulp。诧异一看,两者其实能够做到的事情竟然是如此相似。同样是 css,js,ts 转译,以及 监听文件修改,两个工具能够做到的事情好像是差不多的。
两者在一定程度上可以做到相同的事情,但也不是没有区别。
webpack 与 gulp 两者,一个是基于配置文件运行,一个是自定义流程运行。一个被定义成 打包工具,一个被定义成 任务运行器(Task Runner)。
难度:初级
更新时间:
- 2019-11-30
- 2020-11-14 主要是调整格式和紧接上一篇文章描述怎么同时运行 webpack 和 tsc 类型检查
一、前置条件
条目 | 版本 |
---|---|
OS | Windows 10 20H2 Enterprise |
node -v | v14.15.0 |
npm -v | 6.14.8 |
vscode | v1.51.1(2020-11-10) |
先安装个 gulp
。
npm install --save-dev gulp
下面再开始写任务(Task),gulp 的任务一般会写在一个在根目录的文件名叫做 gulpfile.js
的文件内,如果拆分 gulp 任务到不同文件的话,也可以是同名的文件夹,根任务就在 /gulpfile.js/index.js
中。[API Concepts | gulp.js](%5BMenu%5D(gulpjs.com/docs/en/api…)
更新:今天去gulp github 仓库一看 gulpjs/gulp: A toolkit to automate & enhance your workflow (github.com),好家伙,截至 2020-11-14,这都一年半没有更新版本了。
二、Task 示例编写
Task 编写示例 一、CSS 移动端适配 px 转 vw
简单需求:
- 使用vw适配方案做移动端适配
- 压缩css
对于 vw 适配方案的实现,我使用到了 postcss
与 postcss-px-to-viewport
来实现。
对于压缩 css 的需求,使用到了 gulp-clean-css
来实现(网上也有说使用gulp-minify-css 拓展的,但是这个拓展现在已经弃用了,gulp-clean-css 更受推荐)。
Step 1: 首先安装依赖
npm i --save-dev gulp-clean-css, gulp-postcss, postcss-px-to-viewport
Step 2: 在 gulpfile.js
下编写 Gulp 任务
const {
src,
dest
} = require('gulp');
const postcss = require('gulp-postcss');
const pxtoviewport = require('postcss-px-to-viewport');
const cleanCSS = require('gulp-clean-css');
/**
* 转译css:px单位转成vw单位,压缩css文件
*/
const transpileCSS = () => {
const processors = [pxtoviewport({
// 视窗宽度,和设计图一致
viewportWidth: 750,
// 单位精度,小数点后四位
unitPrecision: 4,
// 视窗宽度
viewportUnit: 'vw'
})];
return src(['css/**/*.css'], {
sourcemaps: true
})
// 转译px单位至vw单位
.pipe(postcss(processors))
// 压缩css文件:去除注释与多余的空格空行等
.pipe(cleanCSS())
// 构建到build/css目录
.pipe(dest('build/css'));
};
// 导出css
exports.css = transpileCSS;
可以看到 gulp 的基本脚本编写就是写一个函数,返回一个构建管道。
pipe
这个概念学过 Linux 的应该不会陌生
其效果就是把前一条命令的输出,传到后一条命令中作为输入。这里也很相似,其流程就如下
src -> postcss -> cleancss -> dist
把 src 提取到对应的文件流,传给 postcss 进行处理,postcss 处理完后再传经自己处理后的文件流给 cleancss,最后 cleancss 也处理完后,交给 dest 方法去在指定位置生成文件。
其中也用到的流的概念。之后的几个 task 编写也都是这个节奏。
控制台执行命令
# npx gulp 后的参数就是任务到处的名字,gulp会通过这个名字来找到任务脚本
npx gulp css
Task 编写示例 二、JS的转译与压缩
需求:
- 适配旧版本浏览器
- 压缩 js 代码
对于 JS 的转译,我使用到了 babel
转译工具使得我们可以使用最新的语法,且不用担心适配旧版本的浏览器。使用了uglifyjs
来做js压缩。
Step 1: 首先安装需要的包
npm i --save-dev gulp-babel, @babel/preset-env, @babel/core, gulp-uglify
Step 2: 编写 gulp 命令
const {
src,
dest
} = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
/**
* 转译并压缩 js 代码
*/
const transpileJS = () => {
// 首先取得app/static/scripts下的所有为.js的文件(**/的意思是包含所有子文件夹)
return src(['js/**/*.js'], {
ignore: ['js/lib/**/*.js'],
sourcemaps: true
})
//将ES6代码转译为可执行的JS代码
.pipe(babel())
//js压缩
.pipe(uglify())
//将转译压缩后的文件输出到 build/js 下(假如没有dist目录则自动生成dist目录)
.pipe(dest('build/js'));
}
exports.js = transpileJS;
Step 3: 在项目根目录下新建一个 .babelrc
文件,对 babel 进行配置(babel 配置项很多,不做赘述)。下面使用了 babel 预设的配置
{
"presets": [
["@babel/preset-env", {
"loose": true,
"modules": false
}]
]
}
Step 4: 控制台运行脚本
npx gulp js
# 查看 build/js 目录下有没有生成的 js 文件
更新:可以看到 gulp 和 webpack 都可以调用 babel 来实现 js 的转译,功能有些相似。不同的只是在于 gulp 是流程式。
现在我们就可以转译 js 了。
Task 编写示例三、监听文件修改,并自动编译
监听文件修改需要用到 gulp 中的 watch()
方法,传入监听的文件和任务函数。
const watchModify = () => {
watch(['css/**/*.css'], transpileCSS);
watch(['js/**/*.js'], transpileJS);
}
exports.watch = watchModify;
命令行运行:
npx gulp watch
这时候我们如果修改 css/**/*.css
或者 js/**/*.js
文件,就会触发它的自动转译了。
Task 编写示例四、异步任务
const { watch, series } = require('gulp');
function clean(cb) {
// body omitted
cb();
}
function javascript(cb) {
// body omitted
cb();
}
function css(cb) {
// body omitted
cb();
}
exports.default = function() {
// You can use a single task
watch('src/*.css', css);
// Or a composed task
watch('src/*.js', series(clean, javascript));
};
异步任务,以一个回调函数作为返回值,调用这个回调,就表示任务执行成功。
Task 编写示例五、任务组合的顺序执行与并行执行
综合一下上面的示例,形成下面的内容(下面这一长串代码都不重要,重要的是最下面几行):
const {
parallel,
series,
src,
watch,
dest,
} = require('gulp');
// postcss-px-to-viewport
// https://github.com/evrone/postcss-px-to-viewport
const postcss = require('gulp-postcss');
const pxtoviewport = require('postcss-px-to-viewport');
const cleanCSS = require('gulp-clean-css');
// gulp-babel
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const replace = require('gulp-replace');
const processorsConfig = require('./postcss.config').plugins['postcss-px-to-viewport'];
const fs = require('fs');
/**
* 展示项目信息
* @param {function} done 表示任务结束
*/
const displayInfo = (done) => {
try {
const data = fs.readFileSync('./package.json');
const info = JSON.parse(data.toString());
console.log(`${info.name}@${info.version}`);
} catch (e) {
console.error(e);
}
done();
};
/**
* postcss转义css
* @param {function} cb callback function to be done
*/
const transpileCSS = () => {
const processors = [
pxtoviewport(processorsConfig),
];
return src(['css/**/*.css'], {
sourcemaps: true,
})
.pipe(postcss(processors))
.pipe(cleanCSS())
.pipe(dest('build/css'));
};
/**
* 创建一个名为js的任务
* @returns stream line
*/
const transpileJS = () => src(['js/**/*.js'], {
// 首先取得app/static/scripts下的所有为.js的文件(**/的意思是包含所有子文件夹)
ignore: ['js/lib/**/*.js'],
sourcemaps: true,
})
// 将ES6代码转译为可执行的JS代码
.pipe(babel())
// js压缩
.pipe(uglify())
// 将转译压缩后的文件输出到dist/static/scripts下(假如没有dist目录则自动生成dist目录)
.pipe(dest('build/js'));
const moveJsLib = () => src(['js/lib/**/*.js']).pipe(dest('build/js/lib'));
const watchModify = () => {
watch(['css/**/*.css'], transpileCSS);
watch(['js/**/*.js'], transpileJS);
};
const buildJS = () => src(['js/**/*.js'], {
ignore: ['js/lib/**/*.js'],
})
.pipe(babel()) // 将ES6代码转译为可执行的JS代码
.pipe(uglify()) // js压缩
.pipe(dest('dist/js')); // 将转译压缩后的文件输出到dist/static/scripts下(假如没有dist目录则自动生成dist目录)
const buildCSS = () => {
const processors = [
pxtoviewport(processorsConfig),
];
return src(['css/**/*.css'])
.pipe(replace('/images/', '/static/wap/images/'))
.pipe(postcss(processors))
.pipe(cleanCSS())
.pipe(dest('dist/css'));
};
const buildHtml = () => src(['view/**/*.html'])
// 中间过程省略
.pipe(dest('dist/view'));
const moveJsLib2Dist = () => src(['js/lib/**/*.js']).pipe(dest('dist/js/lib'));
const moveImgs = () => src('images/**/*.*')
// 中间过程省略
.pipe(dest('dist/images'));
exports.css = transpileCSS;
exports.js = transpileJS;
exports.watch = series(moveJsLib, transpileCSS, transpileJS, watchModify);
exports.build = series(
displayInfo,
parallel(buildCSS, buildJS, buildHtml, moveImgs, moveJsLib2Dist),
);
exports.default = parallel(transpileCSS, transpileCSS);
可以看到最下面几行中用到了之前还没有介绍过的函数
series() // 顺序执行,传入的任务
parallel() // 并行执行,传入的任务
有趣的是,这两个脚本是可以相互嵌套的,就如同倒数第4行中展示的那样。
更新:Task 编写示例六,结合 webpack 使用 babel 转译 ts
这一节与昨天的这条博客内讲的配置挂钩
webpack, babel, eslint,typescript,koa服务器应用简单入门配置 (juejin.im)
需求:
-
能够编译 typescript 代码
-
编译过程如果有 ts 错误,能够提示错误
前置条件:
-
已经配置好 typescript 的 tsconfig,已经设置了 tsconfig.json 中的
compilerOptions.noEmit
为true
-
已经配置好 babel 转译 ts 的预设和插件
-
已经可以用 webpack 的插件
babel-loader
来调用 babel,转译 typescript上述三个都可以在上一篇博客中找到 webpack, babel, eslint,typescript,koa服务器应用简单入门配置 (juejin.im)
先安装好必要的依赖 webpack-stream - npm (npmjs.com)
npm install --save-dev webpack-stream gulp-typescript
然后编写 gulp 任务:
/* eslint-disable global-require */
const {
src, dest, watch, parallel,
} = require('gulp');
const webpack = require('webpack-stream');
const ts = require('gulp-typescript');
const tsProject = ts.createProject('tsconfig.json', {
noEmit: true,
});
// 检查 typescript 类型
function checkTypes() {
return tsProject.src()
.pipe(tsProject())
.js
.pipe(dest('.'));
}
// 调用 webpack 进行编译项目代码,之后还可以进行操作
function runWebpack() {
return src('src/**/*.ts')
// 可以传入webpack配置对象,也可以直接传入配置文件
.pipe(webpack(require('./webpack.config.js')))
.pipe(dest('dist/'));
}
// 监听修改
function watchModify() {
watch('src/**/*.ts', parallel(checkTypes, runWebpack));
}
exports.default = runWebpack;
exports.dev = watchModify;
exports.checkTypes = checkTypes;
上面这几个函数就可以实现在一个命令行打开监听服务,同时检查 ts 类型并运行 webpack 打包
npx gulp dev
到最后,任务流大概就变成了这样
(mdzz [擦汗]) 哈哈哈哈。
三、其他
-
常用命令行参数可以通过输入
gulp --help
来查看。 -
我们可以通过 gulp + webpack 完成很多工作,而且配置好后,开发者只需要输入很少的命令,一定程度上可以提高开发者的效率。
-
至于效率提升多少真心见仁见智。原本的前端只需要学习三大金刚(html, css, js),现在的前端,要学的有 react, vue, webpack,babel,nodejs,各种框架库。这个时代前端空前繁荣,新技术层出不穷。给人带来相当大的心智负担。这到底是提高了效率还是降低了效率真心难以讲明白。
如果觉得有帮助的话,我非常感谢你的点赞。有过文内有错误和浅薄之处,我也非常欢迎您的指正。