◆

Gulp 基础:安装、gulpfile 编写与文件复制

2023-02-02 01:370

gulp 和 webpack、rollup 不是同一类东西,容易混。webpack 是打包器,核心是从入口出发分析模块依赖图,把一堆模块合并成 bundle。gulp 是任务运行器,它不理解模块依赖,只做「读一批文件 → 过几道处理 → 写到别处」这件事,靠流(stream)串起来。

所以现在的项目里 gulp 通常不负责打包 JS,而是处理打包器不擅长的边角活:批量压图、复制静态资源到指定位置、构建前后的文件清理、给多个目录跑同一套预处理器。这些活儿写 shell 脚本也能干,但跨平台(Windows 和 Unix 的路径、命令不一致)就麻烦了,gulp 用 Node API 实现,一份代码到处能跑。

安装使用

gulp 需要装两份:全局的命令行工具负责提供 gulp 命令,项目里的 gulp 本体负责实际执行。这样设计是为了让不同项目能用不同版本的 gulp,而命令行入口只有一个。

  1. 首先安装 gulp 命令行工具
npm install --global gulp-cli
  1. 安装 gulp,作为开发时依赖项
npm install --save-dev gulp

3.安装成功后 查看 gulp 版本

这条命令会分两行打印 CLI 版本和 Local 版本。如果 Local 显示 Unknown,说明当前目录下没装 gulp 本体,光有命令行工具是跑不起来任务的:

gulp --version

如何使用

gulp 的配置就是一个普通的 JS 文件,没有声明式的配置对象,任务都是自己写的函数。

创建一个名为 gulpfile.js 的文件,内容如下。

const gulp = require('gulp');

gulp.task(
  'build',
  gulp.series(done => {
    gulp
      .src(getProjectPath('./img/**')) // 源文件 可以传数组 gulp.src(['src/js/*.js', '!src/js/**/{test1,test2}.js'])
      .pipe(gulp.dest(getProjectPath('../eat/img'))); // 设定输出目录
  }),
);

然后执行 gulp build 则会把 ./img 下的所有文件复制粘贴到 ../eat/img 文件下面

拆开看这几个 API 各管什么:

gulp.src(glob) 按 glob 匹配文件,读成一个虚拟文件流。glob 支持数组,前面加 ! 表示排除,注释里那个例子就是「取 src/js 下所有 js,但排除 test1 和 test2」。

.pipe(插件) 是流的串接,每个插件接收上游的文件对象、处理后传给下游。压缩、加前缀、重命名这些都是这一环。上面的例子没有中间处理,直接从 src 接到 dest,效果就是纯复制。

gulp.dest(dir) 把流里的文件写到目标目录。它会保留 src 匹配时的相对目录结构——这点是靠 glob 里 ** 之前的部分作为「基准路径」算出来的,所以 ./img/** 会让 img 下的层级关系原样搬到目标目录。

gulp.series 是串行执行多个任务,对应的还有 gulp.parallel 并行执行。任务多了之后用这两个组合出执行顺序,比如 gulp.series('clean', gulp.parallel('css', 'js'), 'html')。

一个必须注意的坑:上面代码里传给 series 的函数拿了 done 参数,但函数体里从没调用它。gulp 4 判断任务结束有三种方式——调用 done 回调、返回 stream、返回 Promise,三者都没有的话 gulp 会一直等,最后报 Did you forget to signal async completion?。这段代码里 gulp.src(...) 的返回值也没有 return,所以 gulp 无从知道任务何时完成。正确写法是把 stream 返回出去:

gulp.task('build', () =>
  gulp
    .src(getProjectPath('./img/**'))
    .pipe(gulp.dest(getProjectPath('../eat/img'))),
);

返回了 stream 之后 done 参数就不需要了,gulp 会自己监听流的 end 事件。

另外 gulp.task() 这种字符串注册任务的写法在 gulp 4 里已经不推荐了,官方建议直接导出函数,任务名就是导出名,exports.default 对应 gulp 无参数时执行的默认任务:

const { src, dest } = require('gulp');

const copyImg = () => src('./img/**').pipe(dest('../eat/img'));

exports.build = copyImg;
exports.default = copyImg;

这么写的好处是任务就是普通函数,可以随意组合、复用,也不用记字符串名字。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEGulp 基础:安装、gulpfile 编写与文件复制加载中