Gulp 基础任务编写,并整合 webpack 转译 typescript

812 阅读8分钟

在了解 webpack 的时候,也想到了之前有用到过的一个小工具 gulp。诧异一看,两者其实能够做到的事情竟然是如此相似。同样是 css,js,ts 转译,以及 监听文件修改,两个工具能够做到的事情好像是差不多的。

两者在一定程度上可以做到相同的事情,但也不是没有区别。

webpack 与 gulp 两者,一个是基于配置文件运行,一个是自定义流程运行。一个被定义成 打包工具,一个被定义成 任务运行器(Task Runner)。

难度:初级

更新时间:

    - 2019-11-30

    - 2020-11-14 主要是调整格式和紧接上一篇文章描述怎么同时运行 webpack 和 tsc 类型检查

一、前置条件

条目版本
OSWindows 10 20H2 Enterprise
node -vv14.15.0
npm -v6.14.8
vscodev1.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 适配方案的实现,我使用到了 postcsspostcss-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 错误,能够提示错误

前置条件:

先安装好必要的依赖 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,各种框架库。这个时代前端空前繁荣,新技术层出不穷。给人带来相当大的心智负担。这到底是提高了效率还是降低了效率真心难以讲明白。

如果觉得有帮助的话,我非常感谢你的点赞。有过文内有错误和浅薄之处,我也非常欢迎您的指正。