本文正在参加「」
现在构建工具盛行,但是gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp处理,比如单独打包css文件等等。gulp是基于任务流的。类似于jquery,找到一个或者一类文件,对其进行链式操作,更新流上的数据,整一条链条构成一个任务,多个任务就构成web的构建流程。
webpack需要开发者找到入口,并且需要清楚对于不同的资源应该使用什么loader做何种解释和加工。
glup官网
Gulp vs Webpack/其他构建工具
- Gulp 不具备任何具体功能,完全自主,自定义性强。gulp需要开发者将整个前端流程构建拆分为多个任务,并合理控制所有任务的调用关系。
- 需要开发者自己实现各种功能
- 对 Node.js 储备要求高
- 强调任务的概念,Gulp 本身实际上是一个任务调度工具(tasks runner)
- Webpack 从模块打包出发,通过插件实现一部分 Web 项目的自动化任务。webpack是基于入口的。webpack会自动递归解析入口所需要的加载的所有资源文件,然后用不同的loader去处理不同的文件,用plugin来扩展webpack的功能。
- 开箱即用,门槛更低
- 主要应对 SPA 类应用的模块打包
以往我们使用 Gulp 去实现的常用自动化工作现在都可以使用 Webpack 实现。
Gulp 常见场景
-
如果只是传统的静态页面开发(多页应用MPA),注重的是页面结构与样式,建议采用 Gulp。
-
小程序项目中使用 Sass / Less。
-
再者就是日常的综合事务:文件重命名 / 前后缀。
最佳实践
-
工具层面没有唯一标准答案
-
充分掌握 Gulp 与 Webpack,因地制宜
-
SPA 类使用 Webpack
-
MPA 类使用 Gulp
-
如果只是个别的需求直接使用 npm scripts 配合个别工具就好
- 例如 只需要校验代码,单独使用 ESLint
- npm scripts 就是小型 tasks runner
- start prestart
gulp的任务函数
在gulpfile.js文件中导出的函数都是一个gulp函数。并且每个任务都是异步任务,需要指定结束时机。
exports.default = done => {
console.log("default task working")
done()
}
未指定结束时机,那么就会抛出错误。
串行和并行任务
gulp提供series
,parallel
来帮助我们一次性执行多个任务函数。这在构建我们的代码时非常有用,当我们编译两个不相干的任务时,就可以使用parallel
来帮助我们编译。比如js和css编译。
执行串行任务
const { series, parallel } = require('gulp')
const task1 = done => {
setTimeout(() => {
console.log('task1 working~')
done()
}, 1000)
}
const task2 = done => {
setTimeout(() => {
console.log('task2 working~')
done()
}, 1000)
}
const task3 = done => {
setTimeout(() => {
console.log('task3 working~')
done()
}, 1000)
}
exports.seriesTask = series(task1, task2, task3)
执行并行任务
exports.parallelTask = parallel(task1, task2, task3)
如何结束异步任务
通过gulp的done回调
exports.fn1 = done => {
console.log("fn1")
done() // 可以传入错误对象,来阻止后续任务执行
}
返回一个promise
// 可以返回一个失败的promise对象来阻止后续任务的执行
exports.fn2 = () => {
console.log("fn2")
return Promise.resolve()
}
exports.fn2Err = () => {
console.log("fn2")
return Promise.reject(new Error("err"))
}
exports.fn3 = async () => {
await new Promise(resolve => {
setTimeout(resolve, 1000)
})
console.log("fn3")
}
返回一个Stream对象,当stream调用end事件时该任务将结束。
// 返回一个stream对象
exports.fn4 = () => {
const rs = fs.createReadStream("package.json")
const ws = fs.createWriteStream("temp.json")
rs.pipe(ws)
return rs
}
gulp的工作流程
基于流的构建系统。 下面一个例子是将css压缩并输出。
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
// 文件读取流
const readStream = fs.createReadStream('normalize.css')
// 文件写入流
const writeStream = fs.createWriteStream('normalize.min.css')
// 文件转换流
const transformStream = new Transform({
// 核心转换过程
transform: (chunk, encoding, callback) => {
const input = chunk.toString()
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
callback(null, output)
}
})
return readStream
.pipe(transformStream) // 转换
.pipe(writeStream) // 写入
}
文件操作
gulp提供了src
,dest
api来供我们读取和写入文件。并返回Stream对象。
const { src, dest } = require('gulp')
// 压缩css
const cleanCSS = require('gulp-clean-css')
// 重命名文件
const rename = require('gulp-rename')
exports.default = () => {
return src('src/*.css')
.pipe(cleanCSS())
.pipe(rename({ extname: '.min.css' }))
.pipe(dest('dist'))
}
gulp构建项目案例
一个案例
处理sass文件
需要安装gulp-sass
, sass
// 安装sass,gulp-sass
const sass = require('gulp-sass')(require('sass'));
const style = () => {
// 需要指定打包的base,来达到输出同样的目录
return src("src/assets/styles/*.scss", {base: "src"}).pipe(sass()).pipe(dest("dist"))
}
处理js文件
处理js语法,做降级处理。
// babel只是一个转换平台,我们还需要安装对应的插件来时间对应语法的转化功能。
// @babel/core @babel/preset-env
const babel = require("gulp-babel")
const script = () => {
return src("src/assets/scripts/*.js", {base: "src"}).pipe(babel({presets: ["@babel/preset-env"]})).pipe(dest("dist"))
}
处理html文件
需要更具不同的模板安装对应的插件处理,这里是swig模板。
// gulp-swig处理swig模板语法
const swig = require("gulp-swig")
const data = {
menus: [
{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [
{
name: 'Twitter',
link: 'https://twitter.com/w_zce'
},
{
name: 'About',
link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About',
link: 'https://github.com/zce'
}
]
}
],
pkg: require('./package.json'),
date: new Date()
}
const page = () => {
// 匹配到src下面的所有html文件
return src("src/**/*.html", {base: "src"}).pipe(swig({data})).pipe(dest("dist"))
}
处理图片字体等文件
注意我们安装时,需要安装指定版本的gulp-imagemin
, del
,因为高版本的包不支持require引入。
const {src, dest, parallel, series} = require("gulp")
// gulp-swig处理swig模板语法
const swig = require("gulp-swig")
// gulp-imagemin处理图片,我们需要安装6.1.0版本,否则模块化不兼容
const imagemin = require("gulp-imagemin")
// 删除上一次打的包 ,需要安装5.1.0版本
const del = require("del")
// 无损压缩
const image = () => {
return src("src/assets/images/**", {base: "src"}).pipe(imagemin()).pipe(dest("dist"))
}
// 无损压缩
const font = () => {
return src("src/assets/fonts/**", {base: "src"}).pipe(imagemin()).pipe(dest("dist"))
}
const extra = () => {
return src("public/**", {base: "public"}).pipe(dest("dist"))
}
// 删除上次打包文件
const clean = () => {
return del(["dist"])
}
// 并行处理js,css,html文件
const compile = parallel(style, script, page, image, font, extra)
const build = series(clean, compile)
module.exports = {
build,
compile
}
热更新开发服务器
我们通过browser-sync
来实现。
并且我们可以通过watch来做监听文件变化,然后进行相应的任务重新打包。由于图片等资源我们在开发环境无需处理,可以直接监听其变化,来刷新浏览器,增加构建效率。
const {src, dest, parallel, series, watch} = require("gulp")
// 开发服务器
const browserSync = require("browser-sync")
const bs = browserSync.create()
const server = () => {
// 监听src下的文件变化
watch("src/assets/styles/*.scss", style)
watch("src/assets/scripts/*.js", script)
watch("src/*.html", page)
// watch("src/assets/images/**", image)
// watch("src/assets/fonts/**", font)
// watch("public/**", extra)
// 当图片等资源发生变化,自动刷新浏览器
watch([
"src/assets/images/**",
"src/assets/fonts/**",
"public/**"
], bs.reload)
bs.init({
notify: false,
// 那些文件改变,可以热更新
// files: "dist/**",
server: {
// 一些静态资源,图片等在开发过程中我们不需要编译打包,所以在src,public中直接获取,提高构建效率
baseDir: ["dist", "src", "public"],
// 优先级高于baseDir
routes: {
"/node_modules": "node_modules"
}
}
})
}
处理html依赖引入问题和代码压缩
我们可以通过定义构建注释,来加载引入的依赖。通过useref
来解决。useref会读取类似于下面这种注释,然后将依赖替换成注释中指定的路径。
<!-- build:css assets/styles/vendor.css -->
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<!-- endbuild -->
<!-- build:css assets/styles/main.css -->
<link rel="stylesheet" href="assets/styles/main.css">
<!-- endbuild -->
在我们解析依赖的时候,我们还可以进行各个模块的代码压缩。
// 通过构建注释,来加载引入的依赖
// <!-- build:css assets/styles/main.css -->
// <link rel="stylesheet" href="assets/styles/main.css">
// <!-- endbuild -->
const useref = () => {
return src("temp/*.html", {base: "temp"})
.pipe(
plugins.useref({
searchPath: ["temp", "."]
})
)
// 压缩js,css,html
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest("dist"))
}
下面是完整的项目构建配置。
const {src, dest, parallel, series, watch} = require("gulp")
// 安装sass,gulp-sass
const sass = require('gulp-sass')(require('sass'));
// // babel只是一个转换平台,我们还需要安装对应的插件来时间对应语法的转化功能。
// // @babel/core @babel/preset-env
// const babel = require("gulp-babel")
// // gulp-swig处理swig模板语法
// const swig = require("gulp-swig")
// // gulp-imagemin处理图片,我们需要安装6.1.0版本,否则模块化不兼容
// const imagemin = require("gulp-imagemin")
// 删除上一次打的包 ,需要安装5.1.0版本
const del = require("del")
// gulp-load-plugins该插件将所有gulp相关插件都挂载到该对象上,方便了我们使用
const plugins = require("gulp-load-plugins")()
// 开发服务器
const browserSync = require("browser-sync")
const bs = browserSync.create()
const data = {
menus: [
{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [
{
name: 'About',
link: 'https:///user/2225067267204935/columns'
},
{
name: 'About',
link: 'https://github.com/zhang-glitch'
}
]
}
],
pkg: require('./package.json'),
date: new Date()
}
const style = () => {
// 需要指定打包的base,来达到输出同样的目录
return src("src/assets/styles/*.scss", {base: "src"}).pipe(sass()).pipe(dest("temp")).pipe(bs.reload({stream: true}))
}
const script = () => {
return src("src/assets/scripts/*.js", {base: "src"}).pipe(plugins.babel({presets: ["@babel/preset-env"]})).pipe(dest("temp")).pipe(bs.reload({stream: true}))
}
const page = () => {
// 匹配到src下面的所有html文件
// cache: false防止模板缓存导致页面不能及时更新
return src("src/*.html", {base: "src"}).pipe(plugins.swig({data, defaults: {
cache: false}})).pipe(dest("temp")).pipe(bs.reload({stream: true}))
}
// 无损压缩
const image = () => {
return src("src/assets/images/**", {base: "src"}).pipe(plugins.imagemin()).pipe(dest("dist"))
}
// 无损压缩
const font = () => {
return src("src/assets/fonts/**", {base: "src"}).pipe(plugins.imagemin()).pipe(dest("dist"))
}
const extra = () => {
return src("public/**", {base: "public"}).pipe(dest("dist"))
}
const clean = () => {
return del(["dist", "temp"])
}
const server = () => {
// 监听src下的文件变化
watch("src/assets/styles/*.scss", style)
watch("src/assets/scripts/*.js", script)
watch("src/*.html", page)
// watch("src/assets/images/**", image)
// watch("src/assets/fonts/**", font)
// watch("public/**", extra)
// 当图片等资源发生变化,自动刷新浏览器
watch([
"src/assets/images/**",
"src/assets/fonts/**",
"public/**"
], bs.reload)
bs.init({
notify: false,
// 那些文件改变,可以热更新
// files: "dist/**",
server: {
// 一些静态资源,图片等在开发过程中我们不需要编译打包,所以在src,public中直接获取,提高构建效率
baseDir: ["temp", "src", "public"],
// 优先级高于baseDir
routes: {
"/node_modules": "node_modules"
}
}
})
}
// 通过构建注释,来加载引入的依赖
// <!-- build:css assets/styles/main.css -->
// <link rel="stylesheet" href="assets/styles/main.css">
// <!-- endbuild -->
const useref = () => {
return src("temp/*.html", {base: "temp"})
.pipe(
plugins.useref({
searchPath: ["temp", "."]
})
)
// 压缩js,css,html
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest("dist"))
}
// 并行处理js,css,html文件
const compile = parallel(style, script, page)
// 构建,【删除,编译html,js,css,处理依赖引入,压缩代码】【其他资源处理】
const build = series(clean, parallel(series(compile, useref), image, font, extra))
const dev = series(clean, compile, server)
module.exports = {
build,
compile,
dev,
useref
}
该项目的演示在这里,如有需要自取
为了更好地以后使用,通过gulp封装了一个构建工具,已发布,hm-mpa-build
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!