Gulp的基本使用
1.创建一个新的package.json的文件
yarn init
2.安装gulp模块
yarn add gulp --dev
3.在文件根目录创建一个gulpfile.js的文件作为gulp的入口文件
4.gulp4.0版本以后以导出成员的方式定义任务,在最新的gulp版本中取消了同步代码模式全部采用异步代码模式,任务完成之后要以回调函数的方式对任务结束进行标记
exports.foo = done => {
// 传入的done作为回调函数标识任务完成
console.log('foo task working')
done() // 标识任务完成
}
默认任务不需要在yarn gulp后添加任务名称可以默认运行
exports.default = done => {
console.log('default task working')
done()
}
Gulp创建组合任务
1.未被导出的成员任务可以理解成私有的任务,gulp并不能直接运行他们但是可以用series和parallel这两个api使他们变成组合任务其中series是串行的任务结构(依次执行)parallel是并行的任务结构
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.foo = series(task1, task2, task3) // 传入任务名称可以依次执行
exports.bar = parallel(task1, task2, task3) // 传入任务可以同时执行
Gulp的异步任务
1.以回调函数的形式标识异步任务已完成,回调和node.js一样是错误优先的模式,在回调函数里传入错误信息运行时控制台会报出错误结束后续所有任务执行
exports.callback = done => {
console.log(`callback working`)
done()
}
exports.callback_error = done => {
console.log(`callback error`)
done(new Error(`task failed!`))
}
2.promise方式
exports.promise = done => {
console.log(`promise task`)
//一旦我们返回了Promise.resolve就标记任务结束了
//在里面我们不需要传任何值,因为gulp会忽略掉
return Promise.resolve()
}
exports.promise_error = done => {
console.log(`promise error task`)
// reject返回错误信息同样会结束后续所有任务的执行
return Promise.reject(new Error(`promise failed`))
}
3.async…awit方式,其实async…await就是promise的语法糖
const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve,time)
})
}
exports.async = async() => {
await timeout(1000)
console.log(`async task`)
}
4.通过stream的方式(最常用到的方式)因为构建一般都是对文件进行处理,需要引入fs模块
const fs = require('fs')
exports.stream = () => {
// 通过fs创建一个读取文件流 readStream此时就是一个文件流对象
const readStream = fs.createReadStream('package.json')
// 通过fs创建一个写入文件流
const writeStream = fs.createWriteStream('temp.txt')
//通过pipe方法可以将读取的文件流导入到你要写入的文件流
// 可以理解为从一个池子向另一个池子倒水 起到文件复制的作用
readStream.pipe(writeStream)
// 在stream文件流中在读取完毕后会有一个end事件这个end事件标识任务的结束
return readStream
}
Gulp构建过程核心工作原理
- 读取流:把需要转换的文件读出来(src方法)
- 转换流:通过转换流的转换逻辑转换成我们想要的结果(插件方法)
- 写入流:通过写入流写入到指定的文件位置(dest方法)
Gulp文件操作API
简单的用压缩css代码作为例子举例
压缩css转换流通过安装模块 gulp-clean-css实现,文件扩展名变更通过gulp-rename实现
const { series, parallel, src, dest } = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () => {
// 通过src读取这个路径中的文件
// *通配符可以取到所有的这个目录下的文件
return src('src/*.css')
//通过pipe导入到转换流中
.pipe(cleanCss())
// 可以通过连续的pipe进行不同的转换rename改变文件后缀名
.pipe(rename({
extname:'.min.css'
}))
//通过pipe导入到dest的写入流dist目录
.pipe(dest('dist'))
}
Gulp自动化构建例子–样式编译
首先先创建一个需要编译的目录工程结构
const { src, dest } = require('gulp')
// 转换流插件 吧sass转化成css
const sass = require('gulp-sass')
const style = () => {
// base是转换的基准路径在转换时会把src后面的路径都保留下来
return src('src/assets/styles/*.scss', { base: 'src'})
// 转换流把sass转化成css
// 在转化过程中这个模块会默认带下划线开头的scss文件依赖于不带下划线开头的scss就不会被编译
// 在里面传入一个outputStyle属性就可以把编译的文件输出成完全展开的形式
.pipe(sass({ outputStyle:'expanded'}))
.pipe(dest('dist'))
}
module.exports = {
style
}
Gulp例子–脚本编译
const { src, dest } = require('gulp')
// 转换流插件 把es6 转化成es5
const babel = require('gulp-babel')
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 通过babel中选项使用presets指定babel要转换的特性,用preset-env转换所有es6特性
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}
Gulp例子 – 页面模板编译
同时根据parallel任务并行执行样式,脚本,页面三个任务
const { src, dest, parallel } = require('gulp')
// 转换流插件 吧sass转化成css
const sass = require('gulp-sass')
// 转换流插件 把es6 转化成es5
const babel = require('gulp-babel')
// 转换流插件 把模板文件转换
const swig = require('gulp-swig')
//样式编译
const style = () => {
// base是转换的基准路径在转换时会把src后面的路径都保留下来
return src('src/assets/styles/*.scss', { base: 'src'})
// 转换流把sass转化成css
// 在转化过程中这个模块会默认带下划线开头的scss文件依赖于不带下划线开头的scss就不会被编译
// 在里面传入一个outputStyle属性就可以把编译的文件输出成完全展开的形式
.pipe(sass({ outputStyle:'expanded'}))
.pipe(dest('dist'))
}
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 通过babel中选项使用presets指定babel要转换的特性,用preset-env转换所有es6特性
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}
//页面编译
const page = () => {
// src/**/*.html 这个通配符的含义是寻找src目录下任意子文件夹下包含.html的文件
return src('src/*.html', { base:'src' })
.pipe(swig())
.pipe(dest('dist'))
}
// 编译任务
// 通过parallel并行编译这三个任务
const compile = parallel(style, script, page)
module.exports = {
compile
}
Gulp例子 – 图片和字体文件的转换
// 转换流插件 把图片压缩
const imagemin = require('gulp-imagemin')
// 图片转换
const image = () => {
// **通配符标识这个文件夹下所有的文件
return src('src/assets/images/**' , { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
// 字体文件拷贝以及讲一些svg格式压缩
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
// 不存在图片类型的文件imagemin就会跳过
.pipe(imagemin())
.pipe(dest('dist'))
}
Gulp例子 – 其他文件及文件清除
整合前面的代码形成一小部分完整工程编译
const { src, dest, parallel, series } = require('gulp')
// 转换流插件 吧sass转化成css
const sass = require('gulp-sass')
// 转换流插件 把es6 转化成es5
const babel = require('gulp-babel')
// 转换流插件 把模板文件转换
const swig = require('gulp-swig')
// 转换流插件 把图片压缩
const imagemin = require('gulp-imagemin')
// 小插件del 可以清除dist中的文件
const del = require('del')
const clean = () => {
// 里面是一个数组清除指定文件的内容
// 他是一个promise内容
return del(['dist'])
}
//样式编译
const style = () => {
// base是转换的基准路径在转换时会把src后面的路径都保留下来
return src('src/assets/styles/*.scss', { base: 'src'})
// 转换流把sass转化成css
// 在转化过程中这个模块会默认带下划线开头的scss文件依赖于不带下划线开头的scss就不会被编译
// 在里面传入一个outputStyle属性就可以把编译的文件输出成完全展开的形式
.pipe(sass({ outputStyle:'expanded'}))
.pipe(dest('dist'))
}
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 通过babel中选项使用presets指定babel要转换的特性,用preset-env转换所有es6特性
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}
//页面编译
const page = () => {
// src/**/*.html 这个通配符的含义是寻找src目录下任意子文件夹下包含.html的文件
return src('src/*.html', { base:'src' })
.pipe(swig())
.pipe(dest('dist'))
}
// 图片转换
const image = () => {
// **通配符标识这个文件夹下所有的文件
return src('src/assets/images/**' , { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
// 字体文件拷贝以及讲一些svg格式压缩
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
// 不存在图片类型的文件imagemin就会跳过
.pipe(imagemin())
.pipe(dest('dist'))
}
//额外文件处理 简单拷贝过去
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'))
}
// 编译任务
// 通过parallel并行编译这三个任务
const compile = parallel(style, script, page, image, font)
// 需要进行同步任务 先删除再编译
const build = series(clean, parallel(compile, extra))
module.exports = {
compile,
build
}
Gulp例子 – 自动加载插件
如果使用过多的插件就会导致页面出现很多的require,对于这种问题可以使用一个小插件gulp-load-plugins实现解决
const loadPlugins = require('gulp-load-plugins')
// 自动获取所有gulp里面的插件,下面所有运用插件转换流的都变为plugins.插件名
const plugins = loadPlugins()
自动加载所有插件后的gulp代码
const { src, dest, parallel, series } = require('gulp')
// 所有插件自动加载插件
const loadPlugins = require('gulp-load-plugins')
// 自动获取所有gulp里面的插件,下面所有运用插件转换流的都变为plugins.插件名
const plugins = loadPlugins()
// 小插件del 可以清除dist中的文件
const del = require('del')
const clean = () => {
// 里面是一个数组清除指定文件的内容
// 他是一个promise内容
return del(['dist'])
}
//样式编译
const style = () => {
// base是转换的基准路径在转换时会把src后面的路径都保留下来
return src('src/assets/styles/*.scss', { base: 'src'})
// 转换流把sass转化成css
// 在转化过程中这个模块会默认带下划线开头的scss文件依赖于不带下划线开头的scss就不会被编译
// 在里面传入一个outputStyle属性就可以把编译的文件输出成完全展开的形式
.pipe(plugins.sass({ outputStyle:'expanded'}))
.pipe(dest('dist'))
}
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 通过babel中选项使用presets指定babel要转换的特性,用preset-env转换所有es6特性
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'))
}
//页面编译
const page = () => {
// src/**/*.html 这个通配符的含义是寻找src目录下任意子文件夹下包含.html的文件
return src('src/*.html', { base:'src' })
.pipe(plugins.swig())
.pipe(dest('dist'))
}
// 图片转换
const image = () => {
// **通配符标识这个文件夹下所有的文件
return src('src/assets/images/**' , { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
// 字体文件拷贝以及讲一些svg格式压缩
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
// 不存在图片类型的文件imagemin就会跳过
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
//额外文件处理 简单拷贝过去
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'))
}
// 编译任务
// 通过parallel并行编译这三个任务
const compile = parallel(style, script, page, image, font)
// 需要进行同步任务 先删除再编译
const build = series(clean, parallel(compile, extra))
module.exports = {
compile,
build
}
Gulp例子 – 热更新开发服务器
安装browser-sync模块,这个模块提供给我们一个开发服务器,它支持我们再代码变化后热更新到浏览器,可以及时看到页面效果。它并不是一个gulp的插件,我们只是用gulp去管理
// 引用browserSync模块
const browserSync = require('browser-sync')
// browserSync的create方法用于创建一个开发服务器
const bs = browserSync.create()
// 开发服务器任务
const serve = () => {
bs.init({
// notify可以让web服务器启动时右上角的提示关闭
notify: false,
// port用于指定启动服务的端口号
port: 2080,
// open属性用于选择是否再启动服务后自行启动服务器
// open: false,
// files指定一个字符串 这里面是让browser监听哪个文件变化之后热更新
files: 'dist/**',
// server用于指定将哪个目录作为网站的根目录
server: {
// 浏览器运行的是加工过的dist文件
baseDir: 'dist',
// routes可以让打包文件中指向不在dist中的node——modules文件的资源,
//让他们映射到项目根目录下的node_modules从而可以获取
routes: {
'/node_modules' : '/node_modules'
}
}
})
}
Gulp例子 – 监视变化以及构建优化
// watch监视某一个文件的通配符判断是不是要重新执行某一个任务
const { src, dest, parallel, series, watch } = require('gulp')
// 开发服务器任务
const serve = () => {
// watch传入两个参数 一个是监听的文件 第二个是监听后重新执行的任务
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// 这三中对图片文字等进行修改后可以重新执行bs任务找到源码中的图片等进行热更新
watch([
'src/assets/styles/*.scss',
'src/assets/scripts/*.js',
'src/*.html'
], bs.reload)
bs.init({
// notify可以让web服务器启动时右上角的提示关闭
notify: false,
// port用于指定启动服务的端口号
port: 2080,
// open属性用于选择是否再启动服务后自行启动服务器
// open: false,
// files指定一个字符串 这里面是让browser监听哪个文件变化之后热更新
files: 'dist/**',
// server用于指定将哪个目录作为网站的根目录
server: {
// 浏览器运行的是加工过的dist文件,数组后面可以寻找其他的文件
// js css html的编译需要dist编译后的而图片字体等不需要监听他只是一个复制和压缩的作用 对于开发毫无影响
baseDir: ['dist', 'src', 'public'],
// routes可以让打包文件中指向不在dist中的node——modules文件的资源,
//让他们映射到项目根目录下的node_modules从而可以获取
routes: {
'/node_modules' : '/node_modules'
}
}
})
}
// 编译任务
// 通过parallel并行编译这三个任务
const compile = parallel(style, script, page)
// 需要进行同步任务 先删除再编译
// build加入图片字体压缩任务 可以理解为在上线之前只编译一次
const build = series(clean, parallel(compile, extra, image, font))
// 开发任务 先编译后面进行服务器热修改开发,在开发阶段字体图片压缩等不需要执行,对构建过程进行了优化
const develop = series(compile, serve)
Gulp例子 – useref文件引用处理及文件压缩
在构建的过程中,在构建完的html里面会引用一些node_modules的文件但是在打包过程中我们不会将其打包在dist文件下,在开发模式中我们可以用路由映射的方式可是在真实线上我们就需要用useref插件进行处理。
文件压缩所需要的几个插件
gulp-htmlmin 压缩html文件
gulp-uglify 压缩js文件
gulp-clean-css 压缩css文件
分别压缩三个文件时需要进行判断 使用插件gulp-if
const useref = () => {
//需要处理的文件是dist下的html 而不是src下的 src下的只是模板
return src('dist/*.html', { base: 'dist' })
//useref传入参数searchPath寻找要引用的文件 css文件都在dist中 node_modules文件都在根目录中
.pipe(plugins.useref({ searchPath:['dist', '.'] }))
// 分别压缩html css js文件 ,正则判断路径是否以js结尾
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
// 这个参数时折叠掉所有的空白字符,其余两个时压缩html中的css和js
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace:true,
minifyCss,
minifyJs
})))
//将他们导出到写入文件流 把找到要引用的文件全部合并到一个文件中vender.css
.pipe(dest('release'))
}
Gulp案例 – 重新规划构建过程
因为使用了useref所以我们打破了构建的过程因为我们不能读和写都在同一个文件dist中进行迫不得已将他们重新写入了release文件中,但是我们想要最后上线的release中却没有压缩的图片所以需要重新规划构建过程。所以整个的规划构建过程需要将js css html这些需要二次useref加工的文件放入到一个临时文件temp中
在重新规划完构建过程之后,整个gulp自动化构建就全部完成了。完整代码如下所示
// watch监视某一个文件的通配符判断是不是要重新执行某一个任务
const { src, dest, parallel, series, watch } = require('gulp')
// 所有插件自动加载插件
const loadPlugins = require('gulp-load-plugins')
// 引用browserSync模块
const browserSync = require('browser-sync')
// 自动获取所有gulp里面的插件,下面所有运用插件转换流的都变为plugins.插件名
const plugins = loadPlugins()
// 小插件del 可以清除dist中的文件
const del = require('del')
// browserSync的create方法用于创建一个开发服务器
const bs = browserSync.create()
const clean = () => {
// 里面是一个数组清除指定文件的内容
// 他是一个promise内容
// 清除 dist文件以及临时temp文件
return del(['dist', 'temp'])
}
//样式编译
const style = () => {
// base是转换的基准路径在转换时会把src后面的路径都保留下来
return src('src/assets/styles/*.scss', { base: 'src'})
// 转换流把sass转化成css
// 在转化过程中这个模块会默认带下划线开头的scss文件依赖于不带下划线开头的scss就不会被编译
// 在里面传入一个outputStyle属性就可以把编译的文件输出成完全展开的形式
.pipe(plugins.sass({ outputStyle:'expanded'}))
.pipe(dest('temp'))
}
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 通过babel中选项使用presets指定babel要转换的特性,用preset-env转换所有es6特性
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'))
}
//页面编译
const page = () => {
// src/**/*.html 这个通配符的含义是寻找src目录下任意子文件夹下包含.html的文件
return src('src/*.html', { base:'src' })
.pipe(plugins.swig())
.pipe(dest('temp'))
}
// 图片转换
const image = () => {
// **通配符标识这个文件夹下所有的文件
return src('src/assets/images/**' , { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
// 字体文件拷贝以及讲一些svg格式压缩
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
// 不存在图片类型的文件imagemin就会跳过
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
//额外文件处理 简单拷贝过去
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'))
}
// 开发服务器任务
const serve = () => {
// watch传入两个参数 一个是监听的文件 第二个是监听后重新执行的任务
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// 这三中对图片文字等进行修改后可以重新执行bs任务找到源码中的图片等进行热更新
watch([
'src/assets/styles/*.scss',
'src/assets/scripts/*.js',
'src/*.html'
], bs.reload)
bs.init({
// notify可以让web服务器启动时右上角的提示关闭
notify: false,
// port用于指定启动服务的端口号
port: 2080,
// open属性用于选择是否再启动服务后自行启动服务器
// open: false,
// files指定一个字符串 这里面是让browser监听哪个文件变化之后热更新
files: 'dist/**',
// server用于指定将哪个目录作为网站的根目录
server: {
// 浏览器运行的是加工过的dist文件,数组后面可以寻找其他的文件
// js css html的编译需要dist编译后的而图片字体等不需要监听他只是一个复制和压缩的作用 对于开发毫无影响
// 重新规划构建后 js css html是从临时文件temp中取的
baseDir: ['temp', 'src', 'public'],
// routes可以让打包文件中指向不在dist中的node——modules文件的资源,
//让他们映射到项目根目录下的node_modules从而可以获取
routes: {
'/node_modules' : '/node_modules'
}
}
})
}
//useref文件引用处理
const useref = () => {
//需要处理的文件是dist下的html 而不是src下的 src下的只是模板
// 重新规划构建后 需要处理的文件在临时文件temp中
return src('temp/*.html', { base: 'temp' })
//useref传入参数searchPath寻找要引用的文件 css文件都在dist中 node_modules文件都在根目录中
.pipe(plugins.useref({ searchPath:['temp', '.'] }))
// 分别压缩html css js文件 ,正则判断路径是否以js结尾
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
// 这个参数时折叠掉所有的空白字符,其余两个时压缩html中的css和js
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace:true,
minifyCss: true,
minifyJs: true,
})))
//将他们导出到写入文件流 把找到要引用的文件全部合并到一个文件中vender.css
.pipe(dest('dist'))
}
// 编译任务
// 通过parallel并行编译这三个任务
const compile = parallel(style, script, page)
// 需要进行同步任务 先删除再编译
// build加入图片字体压缩任务 可以理解为在上线之前只编译一次
// 在重新规划构建后 compile和useref是有依赖关系的所以他俩需要串行
const build = series(
clean,
parallel(
series(
compile,
useref
),
extra,
image,
font
)
)
// 开发任务 先编译后面进行服务器热修改开发,在开发阶段字体图片压缩等不需要执行,对构建过程进行了优化
const develop = series(compile, serve)
module.exports = {
clean,
build,
develop,
}
最后Gulp补充
在最后抛出的任务可以在package.json的scripts中指定运行的名称供大家快速理解使用,再次运行这些构建工作时就不需要再使用yarn gulp 。。。 了
直接可以使用 yarn build / yarn develp 等完成构建。
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
},
最后不要忘记在.gitignore中忽略掉生成的dist和temp文件