1、yarn init 初始化 package.json
2、yarn add gulp --dev 将gulp作为开发依赖安装
3、项目根目录创建gulpfile.js,作为gulp的入口文件
//gulp 的入口文件
exports.foo = () => {
console.log("foo task working!")
}
4、命令行通过yarn gulp foo
运行这个任务
最新的gulp当中取消了同步代码模式,约定了每个任务都是异步的,每个任务结束需要调用回调函数或者其他方式来标记任务已完成
exports.foo = done => {
console.log("foo task working!")
done() //标识任务完成
}
5、如果任务名称为default,会作为gulp的默认任务出现
exports.foo = done => {
console.log("foo task working!")
done() //标识任务完成
}
exports.default = done => {
console.log("default task working!")
done()
}
执行命令时不填任务名称,会默认执行default任务
6、gulp4.0以前注册任务需要通过gulp模块里的task方法去实现
const gulp = require('gulp')
gulp.task('bar', done => {
console.log("bar task working!")
done()
})
虽然该方法仍然可以使用,但是不被推荐,更推荐使用导出函数成员的方式
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) //并行任务
// parallel:创建一个并行的构建任务,可并行执行多个构建任务 parallel(‘任务1’,‘任务2’,‘任务3’,…)
// series:创建一个串行的构建任务,会按照顺序依次执行多个任务 series(‘任务1’,‘任务2’,‘任务3’,…)
1、回调函数方式(done)
exports.callback = done=>{
console.log("callback task")
done()
}
exports.callback_error = done=>{
console.log("callback_error task")
done(new Error('task failed!'))
}
错误优先,多个任务同时执行时,后面的任务不会执行
2、promise方式
exports.promise = () => {
console.log("promise task")
return Promise.resolve()
}
exports.promise_error = () => {
console.log("promise_error task")
return Promise.reject(new Error('task failed'))
}
3、async await方式(node环境8以上的可以使用)
const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
exports.async = async () => {
await timeout(1000)
console.log("async task")
}
4、stream方式(最常见,因为构建系统大都是在处理文件)
const fs = require('fs')
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
return readStream
}
//结束的时机就是当readStream end的时候,文件读取完成会触发end事件,gulp就知道任务已经完成
const fs = require('fs')
exports.stream = done => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
//用done模拟结束任务
readStream.on('end', () => {
done()
})
}
//gulp 常规核心工作过程
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
//文件读取流
const read = fs.createReadStream('normalize.css')
//文件写入流
const write = fs.createWriteStream('normalize.min.css')
//文件转换流
const transform = new Transform({
transform: (chunk, encoding, callback) => {
//核心转换过程实现
//chunk => 读取流中读取到的内容 (Buffer) toString拿到文本内容
const input = chunk.toString()
//去掉空白字符及css注释
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
//callback是错误优先的回调函数,第一个参数应该传错误对象,没有则传null
callback(null, output)
}
})
read
.pipe(transform) //转换
.pipe(write) //写入
return read
}
const { src, dest } = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () => {
//文件读取流
const read = src("src/*.css")
//文件写入流
const write = dest('dist')
read
.pipe(cleanCss())
.pipe(rename({extname:'.min.css'}))
.pipe(write) //写入
return read
}
// src:创建读取流,可直接src(‘源文件路径’) 来读取文件流
// dest:创建写入流,可直接dest(‘目标文件路径’) 来将文件流写入目标文件中
// gulp-clean-css插件用于压缩css代码
// gulp-rename插件用于文件重命名
这是项目的基本目录结构,assets主要用于存放样式文件,脚本文件,图片,字体等
最终的构建目标是
yarn add gulp-sass --dev
yarn add sass --dev
const sass = require('gulp-sass')(require('sass'))
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
提示:在我们每次构建完一个任务可以通过module.exports
将任务名暴露出去,然后运行命令行yarn gulp "任务名"
(例如:yarn gulp style
)来确保我们当前写完的任务是没有问题的,然后再进行其他任务的构建
gulp-sass
时,可能里面的node-sass
下载不下来导致运行时报错,要另外安装sass
src
第二个参数可以传一个{ base: 'src' }
选项参数,用于指定基准路径,会保留src
后面的文件目录sass
模块工作时,会把下划线 “_” 开头的文件当做主文件依赖的文件,因此不会被转换,会被忽略掉sass
模块工作生成的样式代码中,css
结束的括号可能默认是跟在最后的属性后面,可以通过指定选项outputStyle: 'expanded'
将括号换行1、只安装gulp-babel
模块运行可能会报错,因为gulp-babel
这个插件只是帮助唤起@babel/core
,真正执行转换的模块是@babel/core
,同时还需要安装@babel/preset-env
这个模块,用于转换ES6的一些新特性
yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev
2、同时要在babel
方法里面添加presets
的配置
const babel = require('gulp-babel')
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({presets:['@babel/preset-env']}))
.pipe(dest('dist'))
}
如果忘记添加presets:['@babel/preset-env']
,可能会导致转换没有效果
原因:babel
只是ECMAScript
的转换平台,只是提供一个环境,具体实现转换的是babel
里面的插件
yarn add gulp-swig --dev
const swig = require('gulp-swig')
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data: data }))
.pipe(dest('dist'))
}
src
下面的html
文件,同时有些子目录下面的html
也需要转换,路径匹配规则要写成src('src/**/*.html')
,表示src
下面任意子目录下的html
文件swig({data:myData})
将数据传递到页面模板上yarn add gulp-imagemin --dev
const imagemin = require('gulp-imagemin')
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'))
}
yarn add del --dev //安装 del 模块
const del = require('del')
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'))
}
const clean = () => {
return del(['dist'])
}
使用gulp-load-plugins
后,无需在gulpfile.js
使用require
关键字一个一个的把依赖包导入进去 ,使用到的插件要在package.json
里面找到对应的依赖,否则可能报错
yarn add gulp-load-plugins --dev
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
所有使用的gulp
插件都会成为plugins
的属性,命名方式就是'gulp-'
后面的名称,如果插件名有'-'
拼接,则改为驼峰,例如:plugins.babel
、plugins.imagemin
、plugins.swig
等等
默认配置项(知识拓展)官方文档:https://www.npmjs.com/package/gulp-load-plugins
gulpLoadPlugins({
DEBUG: false, // 设置为true时,该插件会将信息记录到控制台。 对于错误报告和问题调试很有用
pattern: ['gulp-*', 'gulp.*', '@*/gulp{-,.}*'], // 将会去搜索的数据
overridePattern: true, // 如果为true,则覆盖内置模式。 否则,扩展内置模式匹配器列表。
config: 'package.json', // 定义从哪里搜索插件
scope: ['dependencies', 'devDependencies', 'peerDependencies'], //在被搜索的文件中要去查看哪些键值
replaceString: /^gulp(-|\.)/, // 将模块添加到上下文时要从模块名称中删除的内容,例如gulp-rename在使用是为$.rename()即可,无需前缀
camelize: true, //如果为true,则将带连字符的插件名称转换为驼峰式
lazy: true, // 插件是否应按需延迟加载
rename: {}, //重命名插件的映射
renameFn: function (name) { ... }, //处理插件重命名的功能(默认有效)
postRequireTransforms: {}, // see documentation below
maintainScope: true // 切换加载所有npm范围,如非作用域包
});
可以结合其他任务实现自动编译,自动刷新浏览器页面,大大提高开发效率
yarn add browser-sync --dev
const browserSync = require('browser-sync')
const bs = browserSync.create()
const serve = () => {
bs.init({
notify: false, //设置每次启动预览时右上角的提示是否显示
port: 2080, //端口 默认3000
open:true, //启动时自动打开浏览器,默认true
files:'dist/**', //启动后监听的文件
server: {
baseDir: 'dist', //网站的根目录
routes: { //routes 优先于 baseDir,即请求发生以后,会先看 routes 里面有没有,否则走 baseDir 下面的目录
'/node_modules': 'node_modules'
}
}
})
}
const { watch } = require('gulp')
const serve = () => {
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)
bs.init({
//......
})
}
监听到里面的文件修改之后,就会执行相应的任务,任务一旦触发就会把dist
文件覆盖掉,同时brower-sync
监听到dist
变化,自动同步到浏览器
注意:这里可能会因为swig
模板引擎缓存的机制导致页面不会变化,此时需要额外将swig
选项中的cache
设置为false
const serve = () => {
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)
// 图片、字体、public文件开发阶段不参与构建,可以提高开发阶段的工作效率
// 让图片、字体、public文件变化后可以更新浏览器,浏览器就会重新发起资源请求,就可以拿到变化后的文件
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
bs.init({
notify: false,
port: 2080,
open: true,
files: 'dist/**', // 这里也可以不使用files监听,可以在上面的每个watch监听执行的对应任务里面加上一个pipe(bs.reload({stream:true})),表示以流的方式往浏览器推入,这种方式更加常见
server: {
baseDir: ['dist', 'src', 'public'],
// 当请求过来,找不到图片字体文件时,会依次向后找,找到源文件
routes: {
'/node_modules': 'node_modules'
}
}
})
}
const compile = parallel(style, script, page)
// build 上线之前执行的任务
// image、font任务可以从compile任务移出,因为开发阶段可以不需要构建,放到build任务
const build = series(clean, parallel(compile, image, font, extra))
const develop = series(compile, serve)
// 这样develop任务就可以在开发阶段以最小的代价把项目构建起来
yarn add gulp-useref --dev
// 必须要dist生成以后 执行useref
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
.pipe(dest('dist'))
}
处理引用文件的合并,首先要先找到这些文件,可以添加选项参数{searchPath:['dist', '.']}
用来指定从哪些文件路径查找,这里指定dist和根目录
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
// 安装压缩html、js、css的插件
由于需要判断引用的文件类型,来使用对应的插件对其进行压缩,这里还需要用到gulp-if
插件
yarn add gulp-if --dev
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
// 判断引用的文件的类型 html/css/js
.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('release'))
}
plugins.htmlmin()
默认只是去除属性里面的空白字符,可根据自己的项目需求自定义添加选项参数
html
文件里style
标签内的css
代码html
文件里script
标签内的js
代码由于构建过程中需要useref
对html
中的引用文件进行编译压缩处理,所以中间需要有一个临时目录temp
用于存放已编译但是还没有开始被useref
处理的文件
执行的大概过程:
temp
,其他的资源文件编译完直接写入最终目录dist
useref
会对编译后文件内的引用文件进行合并和压缩处理,处理完成写入最终目录dist
1、最终需要将外部需要执行的任务暴露出去,同时可以将暴露出去的任务定义到package.json
的scripts
当中,更容易让人理解
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"develop":"gulp develop"
},
//设置过后可以直接通过 yarn develop 来启动项目
2、.gitignore
文件中添加忽略文件dist
和temp