自动化工具 Gulp

自动化工具 gulp


摘要

概念:gulp用于自动化开发流程。

理解:我们只需要编写任务,然后gulp帮我们执行

核心概念:

任务:通过定义不同的任务来组织你的构建流程。

管道:通过管道方式将文件从一个插件传递到下一个插件,从而进行一系列的处理。

插件:Gulp 社区提供了大量的插件,用于实现各种常见的任务,如文件压缩、CSS 预处理等。

Gulp和Webpack的区别:

gulp的核心理念是task runner:可以定义自己的一系列任务,等待任务被执行。

webpack的核心理念是module bundler:可以使用各种的loader来加载不同的模块,也可以使用各种各样的插件在webpack打包的生命周期完成其他的任务。




概念

**概念:**A toolkit to automate & enhance your workflow

一个工具包,可以帮你自动化和增加你的工作流。

解释:

Gulp 是一个流行的基于 JavaScript 的构建工具,用于自动化开发工作流程。它通过定义任务(tasks)来处理和转换项目中的文件,例如编译 Sass 或 LESS、压缩 JavaScript、优化图像等。Gulp 以其简单易用、插件丰富和速度快等特点受到许多前端开发者的欢迎。

核心概念:

任务(Tasks)

  • 在 Gulp 中,任务是执行特定操作的 JavaScript 函数,如编译代码、处理文件等。你可以通过定义不同的任务来组织你的构建流程。

管道(Pipes)

  • Gulp 利用 Node.js 的流(stream)机制,通过管道方式将文件从一个插件传递到下一个插件,从而进行一系列的处理。这种流式处理方式使得 Gulp 效率很高,不需要将文件全部加载到内存中。

插件(Plugins)

  • Gulp 社区提供了大量的插件,用于实现各种常见的任务,如文件压缩、CSS 预处理、文件合并等。每个插件通常只负责一种功能,这符合了 Unix 哲学"做好一件事"的原则。

Gulp和Webpack:

gulp的核心理念是task runner:可以定义自己的一系列任务,等待任务被执行。

基于文件Stream的构建流,我们可以使用gulp的插件体系来完成某些任务;

webpack的核心理念是module bundler

webpack是一个模块化的打包工具,可以使用各种各样的loader来加载不同的模块。

可以使用各种各样的插件在webpack打包的生命周期完成其他的任务。

gulp相对于webpack的优缺点:

  • gulp相对于webpack思想更加的简单、易用,更适合编写一些自动化的任务。

但是目前对于大型项目(Vue、React、Angular)并不会使用gulp来构建,比如默认gulp是不支持模块化的。




基本使用

安装

首先,安装gulp:

jsx 复制代码
# 全局安装 
npm install gulp -g

# 局部安装 
npm install gulp

其次,编写gulpfile.js文件,在其中创建一个任务:

gulpfile.js

jsx 复制代码
const foo = (cb) => {
  console.log('第一个 gulp 任务')
  cb()
}

module.exports = {
  foo
}

最后,执行gulp命令:

jsx 复制代码
npx gulp foo

结果:

jsx 复制代码
nathanchen@192 gulp % npx gulp foo
[10:00:14] Using gulpfile ~/Downloads/CoderWhy/17_Webpack工程化高级/code/Nathan/gulp/gulpfile.js
[10:00:14] Starting 'foo'...
第一个 gulp 任务
[10:00:14] Finished 'foo' after 1.25 ms


创建gulp的任务

每个gulp任务都是一个异步的JavaScript函数:

  • 此函数可以接受一个callback作为参数,调用callback函数那么任务会结束。
  • 或者是一个返回stream、promise、event emitter、child process或observable类型的函数;

任务可以是public或者private类型的:

公开任务(Public tasks):从 gulpfile 中被导出(export),可以通过 gulp 命令直接调用。

私有任务(Private tasks):被设计为在内部使用,通常作为 series() 或 parallel() 组合的组成部分。

补充:gulp4之前, 注册任务时通过gulp.task的方式进行注册的

jsx 复制代码
gulp.task("bar", cb => {
	console.Log("bar任务");
	cb();
}

默认任务

我们可以编写一个默认任务:

jsx 复制代码
module.exports.default = (cb) => {
  console.log('default task exec~')
  cb()
}

执行 gulp 命令:

jsx 复制代码
nathanchen@192 gulp % npx gulp    
[10:08:03] Using gulpfile ~/Downloads/CoderWhy/17_Webpack工程化高级/code/Nathan/gulp/gulpfile.js
[10:08:03] Starting 'default'...
default task exec~
[10:08:03] Finished 'default' after 1.07 ms



gulp的任务组合

任务组合series和parallel:

通常一个函数中能完成的任务是有限的(放到一个函数中也不方便代码的维护),所以我们会将任务进行组合。gulp提供了两个强大的组合方法:

**series():**串行任务组合。 依次执行任务。

**parallel():**并行任务组合。同时执行任务。

Code:

jsx 复制代码
//多个任务串行执行
const seriesTask = series(taskl, task2, task3)
//多个任务并行执行
const parallelTask = parallel(task1, task2, task3)

Case:

jsx 复制代码
const { series, parallel } = require('gulp')

const foo1 = (cb) => {
  setTimeout(() => {
    console.log('foo1 task exec~')
    cb()
  }, 1000)
}

const foo2 = (cb) => {
  setTimeout(() => {
    console.log('foo1 task exec~')
    cb()
  }, 2000)
}

const foo3 = (cb) => {
  setTimeout(() => {
    console.log('foo1 task exec~')
    cb()
  }, 3000)
}

const seriesFoo = series(foo1, foo2, foo3)
const parallelFoo = parallel(foo1, foo2, foo3)

module.exports = {
  seriesFoo,
  parallelFoo
}



gulp的文件操作

读取和写入文件

gulp 暴露了 src() 和 dest() 方法用于处理计算机上存放的文件。

src()

src() 接受参数,并从文件系统中读取文件然后生成一个Node流(Stream),它将所有匹配的文件读取到内存中并通过流 (Stream)进行处理。由 src() 产生的流(stream)应当从任务(task函数)中返回并发出异步完成的信号。

dest()

dest() 接受一个输出目录作为参数,并且它还会产生一个 Node流(stream),通过该流将内容输出到文件中。

pipe()

流(stream)所提供的主要的 API 是 .pipe() 方法,pipe方法的原理是什么呢?

pipe方法接受一个 转换流(Transform streams)或 可写流(Writable streams)。

转换流或者可写流,在拿到数据后可以对数据进行处理,再次传递给下一个转换流或者可写流。

gulpfile.js

jsx 复制代码
const { src, dest } = require('gulp')
const copyFile = () => {
  // 读取文件,写入文件
  // gulp 需要回调函数,而 pipe 返回的是一个 stream,所以这里直接return即可
  return src("./src/main.js").pipe(dest("./dist"))
}

module.exports = {
  copyFile
}

res:

jsx 复制代码
nathanchen@192 gulp % npx gulp copyFile
[10:33:57] Using gulpfile ~/Downloads/gulp/gulpfile.js
[10:33:57] Starting 'copyFile'...
[10:33:57] Finished 'copyFile' after 14 ms


glob文件匹配

src() 方法接受一个 glob 字符串或由多个 glob 字符串组成的数组作为参数,用于确定哪些文件需要被操作。glob 或 glob 数组必须至少匹配到一个匹配项,否则 src() 将报错。

glob的匹配规则如下:

  • (一个星号*):在一个字符串中,匹配任意数量的字符,包括零个匹配;

    比如 '*.js'

  • (两个星号**):在多个字符串匹配中匹配任意数量的字符串,通常用在匹配目录下的文件;

    比如 'scripts/**/*.js'**表示匹配任意多个字符串,可以匹配 /scirpts/a/b/c.js

  • (取反!):

    由于 glob 匹配时是按照每个 glob 在数组中的位置依次进行匹配操作的。

    所以 glob 数组中的取反(negative)glob 必须跟在一个非取反(non-negative)的 glob 后面。

    第一个 glob 匹配到一组匹配项,然后后面的取反 glob 删除这些匹配项中的一部分。

    ['script/**/*.js', '!scripts/vendor/']



对文件进行转换

**需求:**如果在这个过程中,我们希望对文件进行某些处理,可以使用社区给我们提供的插件。

比如我们希望ES6转换成ES5,那么可以使用babel插件。

如果我们希望对代码进行压缩和丑化,那么可以使用uglify或者terser插件。

Case: babel 转换

jsx 复制代码
pnpm add @babel/core gulp-babel @babel/preset-env -D

gulpfile.js

jsx 复制代码
const { src, dest } = require('gulp')
const babel = require('gulp-babel')

const copyFile = () => {
  // 读取文件,写入文件
  // gulp 需要回调函数,而 pipe 返回的是一个 stream,所以这里直接return即可
  return src("./src/**/*.js")
  .pipe(babel({ presets: ["@babel/preset-env"] }))
  .pipe(dest("./dist"))
}

module.exports = {
  copyFile
}

结果:

转换前 main.js

jsx 复制代码
const test = () => {
  console.log('test')
}

test()

转换后main.js

jsx 复制代码
"use strict";

var test = function test() {
  console.log('test');
};
test();



Gulp的文件监听

watch

gulp api 中的 watch() 方法利用文件系统的监控程序(file system watcher)将 与进行关联。

Case: 监听内容的变化

gulpfile.js

jsx 复制代码
const { src, dest, watch } = require('gulp')
const babel = require('gulp-babel')

const copyFile = () => {
  return src("./src/**/*.js")
  .pipe(babel({ presets: ["@babel/preset-env"] }))
  .pipe(dest("./dist"))
}

watch("./src/**/*.js", copyFile) // 监听

module.exports = {
  copyFile
}

res: 修改 main.js ,会自动执行 copyFile

jsx 复制代码
nathanchen@192 gulp % npx gulp copyFile
[11:04:12] Using gulpfile ~/Downloads/CoderWhy/17_Webpack工程化高级/code/Nathan/gulp/gulpfile.js
[11:04:12] Starting 'copyFile'...
[11:04:12] Finished 'copyFile' after 415 ms
[11:04:28] Starting 'copyFile'...
[11:04:28] Finished 'copyFile' after 23 ms



gulp 案例

**需求:**通过gulp来开启本地服务和打包。

打包html文件

**打包html文件:**使用gulp-htmlmin插件。

jsx 复制代码
pnpm add gulp-htmlmin -D  

gulpfile.js

jsx 复制代码
const { src, dest, watch } = require('gulp')
const htmlmin = require('gulp-htmlmin')

const htmlTask = () => {
  // 读取文件,写入文件
  // gulp 需要回调函数,而 pipe 返回的是一个 stream,所以这里直接return即可
  return src("./src/**/*.html")
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(dest("./dist"))
}

watch("./src/**/*.html", htmlTask)

module.exports = {
  htmlTask
}

执行命令:

jsx 复制代码
nathanchen@192 gulp % npx gulp htmlTask
[13:26:01] Using gulpfile ~/Downloads/CoderWhy/17_Webpack工程化高级/code/Nathan/gulp/gulpfile.js
[13:26:01] Starting 'htmlTask'...
[13:26:01] Finished 'htmlTask' after 17 ms

产物如下:

jsx 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width,initial-scale=1" />
		<title>Document</title>
	</head>
	<body>
		<h1>Gulp</h1>
	</body>
</html>


打包JavaScript文件

**打包JavaScript文件:**使用gulp-babel,gulp-terser插件。

安装插件:

jsx 复制代码
pnpm add @babel/core gulp-babel @babel/preset-env -D
jsx 复制代码
pnpm add gulp-terser -D

gulpfile.js

jsx 复制代码
const babel = require('gulp-babel')
const terser = require('gulp-terser')

const jsTask = () => {
  return src("./src/**/*.js")
    .pipe(babel({ presets: ["@babel/preset-env"] }))
    .pipe(terser({ toplevel: true }))
    .pipe(dest('./dist'))
}

module.exports = {
  jsTask
}


打包less文件

**打包less文件:**使用gulp-less插件

jsx 复制代码
nathanchen@192 gulp % pnpm add gulp-less -D

gulpfile.js

jsx 复制代码
const less = require('gulp-less')

const lessTask = () => {
  return src("./src/**/*.less")
    .pipe(less())
    .pipe(dest('./dist'))
}

module.exports = {
  lessTask
}


html资源注入

  • 使用gulp-inject插件:将js和less注入到html文件中
jsx 复制代码
 pnpm add gulp-inject -D
  • 使用browser-sync插件:文件更新,浏览器同步更新
jsx 复制代码
 pnpm add browser-sync -D

操作:

执行 npm run build 进行打包,npm run serve开启服务器。

gulpfile.js

jsx 复制代码
const { src, dest, watch, parallel, series } = require('gulp')
const htmlmin = require('gulp-htmlmin')
const babel = require('gulp-babel')
const terser = require('gulp-terser')
const less = require('gulp-less')
const inject = require('gulp-inject')
const browserSync = require('browser-sync')

// 打包html
const htmlTask = () => {
	// 读取文件,写入文件
	// gulp 需要回调函数,而 pipe 返回的是一个 stream,所以这里直接return即可
	return src('./src/**/*.html')
		.pipe(htmlmin({ collapseWhitespace: true }))
		.pipe(dest('./dist'))
}

// 打包js
const jsTask = () => {
	return src('./src/**/*.js')
		.pipe(babel({ presets: ['@babel/preset-env'] }))
		.pipe(terser({ toplevel: true }))
		.pipe(dest('./dist'))
}

// 打包less
const lessTask = () => {
	return src('./src/**/*.less').pipe(less()).pipe(dest('./dist'))
}

// 在html中注入js和css
const injectTask = () => {
	return src('./dist/**/*.html')
		.pipe(
			inject(src(['./dist/**/*.css', './dist/**/*.js']), { relative: true })
		)
		.pipe(dest('./dist'))
}

// 开启本地服务
const bs = browserSync.create()
const serve = () => {
	// 进行监听
	watch('./src/**', buildTask)
	bs.init({
		port: 8080,
		open: true,
		files: './dist/*',
		server: {
			baseDir: './dist'
		}
	})
}

// 创建项目构建的任务
const buildTask = series(parallel(htmlTask, jsTask, lessTask), injectTask)
const serveTask = series(buildTask, serve)

module.exports = {
	buildTask,
	serveTask
}

package.json

jsx 复制代码
{
	...
  "scripts": {
    "build": "gulp buildTask",
    "serve": "gulp serveTask"
  },
  ...
}
相关推荐
struggle202510 小时前
AgenticSeek开源的完全本地的 Manus AI。无需 API,享受一个自主代理,它可以思考、浏览 Web 和编码,只需支付电费。
人工智能·开源·自动化
北漂老男孩10 小时前
ChromeDriver 技术生态与应用场景深度解析
java·爬虫·python·自动化
逸雨清风18 小时前
Chrome更新到136以后selenium等自动化浏览器失效
selenium·自动化
狮智先生21 小时前
【学习笔记】点云自动化聚类简要总结
笔记·学习·自动化
国科安芯21 小时前
高精度降压稳压技术在现代工业自动化中的应用
运维·自动化
水银嘻嘻1 天前
08 web 自动化之 PO 设计模式详解
前端·自动化
小冯的编程学习之路1 天前
【软件测试】:推荐一些接口与自动化测试学习练习网站(API测试与自动化学习全攻略)
c++·selenium·测试工具·jmeter·自动化·测试用例·postman
SoFlu软件机器人2 天前
Java 框架配置自动化:告别冗长的 XML 与 YAML 文件
xml·java·自动化
微刻时光2 天前
影刀RPA网页自动化总结
运维·人工智能·python·低代码·自动化·rpa·影刀rpa
2301_787552872 天前
Lightpanda开源浏览器:专为 AI 和自动化而设计的无界面浏览器
运维·自动化