Gulp是自动化构建的一款工具
基本使用
yarn init
yarn add gulp –dev
code gulpfile.js // 创建一个gulpfile.js文件
// gulp的入口文件
// 最新的gulp中取消了同步任务,每一个任务都是异步任务
// 当我们的任务执行完成后需要调用回调函数或其他方式去标记任务已完成
exports.foo = done => {
console.log('foo task working~')
done() // 标识任务完成
}
// 默认导出
exports.default = done =>{
console.log('foo task working~')
done() // 标识任务完成
}
yarn gulp foo // 调用foo任务
yarn gulp // 调用默认任务
组合任务
串行任务(按顺序依次执行,例如部署)
并行任务(同时执行,例如:编译css和js时,因为2则是互不干扰的,可以用并行任务去执行,来提高构建效率)
const { series,parallel } = require('gulp')
const task1 = done =>{
setTimeout(()=>{
console.log('foo task working~')
done()
},1000)
}
const task2 = done =>{
setTimeout(()=>{
console.log('foo task working~')
done()
},1000)
}
const task3 = done =>{
setTimeout(()=>{
console.log('foo task working~')
done()
},1000)
}
// 串行任务
exprots.foo = series(task1, task2, task3 )
// 并行任务
exprots.bar = parallel(task1, task2, task3 )
yarn gulp foo
yarn gulp bar
异步任务的三种方式
1. 回调函数形式
exports.callback = done =>{
console.log('callback task~')
done()
}
// 错误优先回调函数
// 多个任务同时执行,如果这个任务出错后续的任务就不会执行了
exports.callback_error = done =>{
console.log('callback task~')
done(new Error('task failed!'))
}
2. Promise
exports.promise = () =>{
console.log('promise task~')
return Promise.resolve() // 不需要返回值,gulp会忽略这个值
}
exports.promise_error = () =>{
console.log('promise task~')
return Promise.reject(new Error('task failed~'))
}
async await
const timeout = time =>{
return new Promise(res=>{
setTimeout(res,time)
})
}
exports.async = async() =>{
await timeout(1000)
console.log('async task~')
}
3. stream
const fs = require('fs')
exports.stream = () =>{
const readStream = fs.createReadStream('packge.json') // 文件流对象
const writeStream = fs.createWriteStream('temp.txt')
readStram.pipe(writeStream) // 将readStream导到writeStream
retunrn readStream
}
// 相当于
// exports.stream = done =>{
// const readStream = //fs.createReadStream('packge.json')
// const writeStream = fs.createWriteStream('temp.txt')
// readStram.pipe(writeStream)
// readStram.on('end',()=>{
// done()
//})
//}
构建过程核心工作原理
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)
const input = chunk.toString()
const output = input.replace(/\s+/g,'').replace(/\/\*.+?\*\//g,'') // 替换空白;注释
callback(null,output) // 错误优先,第一个是一个对象
}
})
// 把读取出来的文件流导入写入文件流
read
.pipe(transform) // 转换
.pipe(write) // 写入
// gulp根据流的状态来判定任务是否完成
retrun read
yarn gulp
案例
yarn add gulp --dev
// yarn add gulp-sass --dev
// yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev // 全部的es新特性
// yarn add gulp-swig --dev // 模板引擎
// yarn add gulp-imagemin --dev
yarn add del --dev
yarn add gulp-load-plugins --dev
yarn add browser-sync --dev
yarn add gulp-useref --dev
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
yarn add gulp-if --dev
const { src, dest, parallel, series, watch } = require('gulp')
const del = require('del')
const browser-sync = require('browser-sync')
const plugins = loadPlugins() // 自定义加载所有的gulp插件,名字过程则驼峰命名
const bs = browserSync.create() // 创建browser-sync开发服务器
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')
const data = {
menus:[],
pkg: require('./package.json'),
date: new Date()
}
// 返回一个promise
const clean = () = {
retrun del(['dist','temp'])
}
// 样式编译
const style = () => {
return src('src/assets/styles/*.scss', {base:'src'} ) // 读取流, 保留基准路径
.pipe(plugins.sass({outputStyle: 'expanded' })) // {outputStyle: 'expanded' } css属性展开的形势显示
.pipe(dest('temp')), // 写入流
.pipe(bs.reload({stream:true})) // 以流的方式往浏览器推
}
// es6编译
const script = () => {
return src('src/assets/script/*.js', {base:'src'} ) // 读取流, 保留基准路径
.pipe(babel({ presets: ['@babel/preset-env'] })) // preset对象里的方法
.pipe(dest('temp')), // 写入流
.pipe(bs.reload({stream:true})) // 以流的方式往浏览器推
}
// 页面模板编译
const page = () => {
return src('src/*.js', {base:'src'} ) // 读取流, 保留基准路径;如果是src目录下的文件的html路径需要src/**/*.html
.pipe( plugins.swig({ data:data })) // 可简写data
.pipe(dest('temp')), // 写入流
.pipe(bs.reload({stream:true})) // 以流的方式往浏览器推
}
// 编译图片和字体转换(压缩图片、svg)
const image = () => {
return src('src/assets/images/**', {base:'src'}) // images下的所有文件
.pipe( plugins.imagemin())
.pipe(dest('dist')) // 写入流
}
const font = () => {
return src('src/assets/fonts/**', {base:'src'}) // images下的所有文件
.pipe(imagemin())
.pipe(dest('dist')) // 写入流
}
// 其他文件处理
const extra = () => {
return src('public/**', {base:'public'}) // images下的所有文件
.pipe(dest('dist')) // 写入流
}
const serve = () => {
// 这几个文件修改后,就会执行相应的任务,覆盖dist下的文件
watch('src/assets/styles/*.scss',style)
watch('src/assets/script/*.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, // 关闭提示
port: 3000, // 端口
open: false, // 自动打开
// files:'dist/**', // 监听dist文件的变化,同步到浏览器
server:{
baseDir: ['temp','src','public'], // 依次请求文件,减少构建次数(可为单个字符串)
routes:{ // 路由映射,优先baseDir
'/node_modules':'node_modules'
}
}
})
}
// 文件(依赖)引入处理
const useref = () => {
retrun src('temp/*.html',{base:'dist'})
.pipe(plugins.useref({searchPath:['dist','.']})) // 将构建注释做转换,查找路径(会创建一些新的文件)
// 压缩新生成的文件
.pipe(plugins.if(/\.js$/,plugins.uglify()))
.pipe(plugins.if(/\.cc$/,plugins.cleanCss()))
.pipe(plugins.if(/\.html$/,plugins.htmlmin({
collapseWhitespace:true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest('dist'))
}
// src下的任务并行执行
const compile = parallel(style, script, page)
// 上线之前的任务(先删除再bulid)
const build = series(
clean,
parallel(
series(compile,useref), // 串行
extra, image, font
)
)
// 开发环境
const develop = series(compile, serve)
module.exports = {
// style,
// script,
// page,
// image,
// font
clean,
// compile,
build,
develop,
// useref
}
在package.json中
"scripts":{
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
}
在.gitignore中添加dist,temp目录
// yarn gulp style
// yarn gulp script
// yarn gulp page
yarn gulp compile
封装自动化构建工作流
模拟npm构建:yarn link
yarn name
封装
将package.json文件中的生产环境放入开发环境中
新建lib目录下的index.js
const cwd = process.cwd() 获取工作下的根目录
let config = {
build:{
src: 'src',
dist: 'dist',
temp:'temp',
public: 'public',
paths: {
styles: 'assets/styles/*.scss',
script: 'assets/script/*.js',
pages: '*.html',
image: 'asset/images/**',
fonts: 'assets/fonts/**'
}
}
} // 默认 config
try {
const loadConfig = require(`${cwd}/pages.config.js`)
config = Object.assign({}, config, loadConfig)
} catch (e) {}
// .pipe( plugins.swig({ data:config.data }))
// retrun src( config.build.paths.styles,{ base: config.build.src,cwd: config.build.src })
// watch('**',{cwd:config.build.public},bs.reload) //因为寻找的目录不一致,需要分开写
.pipe(babel({ presets: [require('@babel/preset-env')] })) // 本目录下没有就会向上去寻找到node_modules
新建bin/name.js(cli,会先去找node_modules下的bin文件cmd node运行, 指向该文件)
#!/usr/bin/env node
// 获取命令行输入的内容(数组)
process.argv.push('--cwd')
process.argv.push(process.cwd())
process.argv.push('--fulpfile')
process.argv.push(require.resolve('..')) // lib/index.js
require('gulp/bin/gulp')
在package.json中配置
"files":[
"lib",
"bin" // 发布的时候添加的新目录
],
"bin": "bin/name.js"
yarn unlink ;
yarn link
引用
新建gulpfile.js文件(没封装前的2种方式)
module.exports = require('name') // 引入,导出
用指令的方式来代替创建gulpfile.js
yarn gulp --gulpfile ./node_modules/name/lib/index.js
yarn gulp build--gulpfile ./node_modules/name/lib/index.js --cwd .
新建pages.config.js(配置文件)
module.exports = {
build:{ // 可配置目录
src: 'src',
dist: 'dist',
temp:'temp',
public: 'public',
paths: {
styles: 'assets/styles/*.scss',
script: 'assets/script/*.js',
pages: '*.html',
image: 'asset/images/**',
fonts: 'assets/fonts/**'
},
data: { // 模板数据
menus: []
}
}
yarn add name --dev
"script":{
"clean": "name clean",
"build": "name build",
"develop": "name develop",
}