自动化构建的学习应用—Gulp
Gulp的基本使用
通过导出函数成员的方式定义任务, 默认执行default 任务
yarn add gulp –dev
//gulp的入口文件
exports.foo = done => {
console.log('foo task working~')
done() //标识任务完成
}
exports.default = done => {
console.log("default~~~~~");
done()
};
// gulp4.0之前的写法
// const gulp=reguire('gulp');
// gulp.task('bar',done=>{
// console.log('bar working~')
// done()
// })
Gulp创建组合任务
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);
foo任务会依次执行task1,task2,task3.
Series是串行的任务
Parallel是并行的任务
Gulp异步任务的三种方式
返回promise
返回流的方式
当从任务(task)中返回 stream、promise、event emitter、child process 或 observable 时,成功或错误值将通知 gulp 是否继续执行或结束。如果任务(task)出错,gulp 将立即结束执行并显示该错误。
当使用 series() 组合多个任务(task)时,任何一个任务(task)的错误将导致整个任务组合结束,并且不会进一步执行其他任务。当使用 parallel() 组合多个任务(task)时,一个任务的错误将结束整个任务组合的结束,但是其他并行的任务(task)可能会执行完,也可能没有执行完。
const { series, parallel } = require("gulp");
const fs = require('fs')
exports.task_error = (done) => {
console.log("task1...");
// 任务出错 后续的任务不会执行
// 如果任务出错,就将错误放在回调函数 done 中
done(new Error('任务出错...'));
};
const time = (time) => {
return new Promise(resolve =>{
setTimeout(resolve, time)
})
}
// 异步任务
exports.promise = () => {
// 执行 一个promise 的 resolve 状态也可以结束任务
return time(1000)
};
// node 8+ 可以使用 async/await async 会隐式返回 promise 所以可以不用写return
exports.promise_async = async () => {
await time(4000)
console.log('promise_async 任务完成...');
};
exports.promise_error = () => {
console.log('promise_error.........');
// 返回 一个promise 的 reject 代表任务出错
return Promise.reject(new Error('promise_error...'))
};
exports.stream = () => {
const readSteam = fs.createReadStream('./package.json')
const writeSteam = fs.createWriteStream('./tem.txt')
readSteam.pipe(writeSteam)
// 一定要返回 流 不然会报错(任务未完成): The following tasks did not complete:
return readSteam
};
// 流的异步原理: 它自己监听的了流的 end 事件
exports.streamFunc = (done) => {
const readSteam = fs.createReadStream('./package.json')
const writeSteam = fs.createWriteStream('./tem.txt')
readSteam.pipe(writeSteam)
readSteam.on('end', ()=>{
done()
})
return readSteam
};
Gulp 构建过程核心工作原理
读取出来, 转换, 写入其他位置
const fs = require("fs");
const { Transform } = require("stream");
// 模拟 gulp 的原理: 读取流 => 转换流 => 写入流
exports.default = () => {
const read = fs.createReadStream("normalize.css");
// 转换流
const transform = new Transform({
transform(chunk, encoding, callback) {
const input = chunk.toString();
// 所有换行去除, 所有注释去除
const output = input.replace(/\s+/g, "").replace(/\/\*.+?\*\//g, "");
callback(null, output)
},
});
const write = fs.createWriteStream("normalize.min.css");
read.pipe(transform).pipe(write);
return read;
};
文件操作API + 插件的使用
yarn add gulp-clean-css --dev
yarn add gulp-rename --dev
const { src, dest } = require("gulp");
const cleanCss = require("gulp-clean-css");
const rename = require("gulp-rename");
// 模拟 gulp 的原理: 读取流 => 转换流 => 写入流
exports.default = () => {
return (
src("src/*.css")
// 压缩 css
.pipe(cleanCss())
// 后缀重命名
.pipe(rename({ extname: ".min.css" }))
.pipe(dest("dist"))
);
};
Gulp 案例
ps: 如果你yarn 就是下载不了包的话 ,可以换用npm \ cnpm
yarn add gulp --dev
// 处理 sass
yarn add gulp-sass --dev
// 处理 es6+
yarn add gulp-babel @babel/core @babel/preset-env --dev
// 模版引擎
yarn add gulp-swig --dev
// 图片处理
yarn add gulp-imagemin --dev
// 自动加载gulp插件 解决文件顶部插件引入很多,导致代码比较繁杂的问题
yarn add gulp-load-plugins --dev
// 清除文件
yarn add del --dev
// 开发服务器 支持HMR
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
项目启动的其他依赖
yarn add bootstrap jquery popper.js
grupfile.js
样式编译
scss 转 css
转 css 时 保留文件路径
输出到 dist 目录
const { src, dest } = require("gulp");
const sass = require("gulp-sass");
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
);
};
module.exports = {
style,
};
测试:
yarn gulp style
脚本编译
const { src, dest } = require("gulp");
const sass = require("gulp-sass");
const babel = require('gulp-babel')
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
);
};
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest("dist"))
);
};
module.exports = {
style,
script
};
测试:
yarn gulp script
页面模板编译
const { src, dest, parallel } = require("gulp");
const sass = require("gulp-sass");
const babel = require("gulp-babel");
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(),
};
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
);
};
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("dist"))
);
};
const page = () => {
// src/**/*.html 匹配任意子目录
return src("src/*.html", { base: "src" })
.pipe(swig({ data }))
.pipe(dest("dist"));
};
// 并行执行 样式-脚本-页面 编译
const compile = parallel(style, script, page)
module.exports = {
compile
};
测试:
yarn gulp compile
图片和字体文件的转换
const { src, dest, parallel } = require("gulp");
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
// const sass = require("gulp-sass");
// const babel = require("gulp-babel");
// const swig = require("gulp-swig");
// const imagemin = require("gulp-imagemin");
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(),
};
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(plugins.sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
);
};
// 编译脚本文件
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(plugins.babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("dist"))
);
};
// 编译模板文件
const page = () => {
// src/**/*.html 匹配任意子目录
return src("src/*.html", { base: "src" })
.pipe(plugins.swig({ data }))
.pipe(dest("dist"));
};
// 转换图片文件
const image = () => {
// 匹配文件下的所有文件
return src("src/assets/images/**").pipe(plugins.imagemin()).pipe(dest("dist"));
};
// 转换字体文件, 一般的字体文件不需要压缩等处理, 但是有些svg格式的文件,还是可以稍微处理一下
const font = () => {
// 匹配文件下的所有文件
return src("src/assets/fonts/**").pipe(plugins.imagemin()).pipe(dest("dist"));
};
// 并行执行 样式-脚本-页面 编译
const compile = parallel(style, script, page, image, font);
module.exports = {
compile,
};
其他文件清除
const { src, dest, parallel, series, watch } = require("gulp");
const del = require("del");
const loadPlugins = require("gulp-load-plugins");
const bs = require("browser-sync");
const plugins = loadPlugins();
// const sass = require("gulp-sass");
// const babel = require("gulp-babel");
// const swig = require("gulp-swig");
// const imagemin = require("gulp-imagemin");
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 clean = () => {
// del 返回的是个 promise
return del(["dist"]);
};
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(plugins.sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译脚本文件
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(plugins.babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译模板文件
const page = () => {
// src/**/*.html 匹配任意子目录
return src("src/*.html", { base: "src" })
.pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }));
};
// 转换图片文件
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" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
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/**', public)
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
bs.reload
);
bs.init({
notify: false, // browser-sync 连接提示
port: 2080,
open: true, // 自动打开浏览器窗口
// files: "dist/**", // 监听dist文件变化
server: {
baseDir: ["dist", "src", "public"],
routes: {
"/node_modules": "node_modules",
},
},
});
};
// 将public 原样输出到 dist
const public = () => {
return src("public/**", { base: "public" }).pipe(dest("dist"));
};
// 并行执行 样式-脚本-页面 编译
const compile = parallel(style, script, page);
// 先清空文件夹, 再编译+复制public文件夹 正式打开再处理 静态资源文件
const build = series(clean, parallel(compile, public, image, font));
const develop = series(compile, serve);
module.exports = {
clean,
build,
serve,
develop,
compile,
};
文件压缩
useref
const { src, dest, parallel, series, watch } = require("gulp");
const del = require("del");
const loadPlugins = require("gulp-load-plugins");
const bs = require("browser-sync");
const plugins = loadPlugins();
// const sass = require("gulp-sass");
// const babel = require("gulp-babel");
// const swig = require("gulp-swig");
// const imagemin = require("gulp-imagemin");
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 clean = () => {
// del 返回的是个 promise
return del(["dist"]);
};
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(plugins.sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译脚本文件
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(plugins.babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译模板文件
const page = () => {
// src/**/*.html 匹配任意子目录
return src("src/*.html", { base: "src" })
.pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
.pipe(dest("dist"))
.pipe(bs.reload({ stream: true }));
};
// 转换图片文件
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" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
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/**', public)
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
bs.reload
);
bs.init({
notify: false, // browser-sync 连接提示
port: 2080,
open: true, // 自动打开浏览器窗口
// files: "dist/**", // 监听dist文件变化
server: {
baseDir: ["dist", "src", "public"],
routes: {
"/node_modules": "node_modules",
},
},
});
};
// 将public 原样输出到 dist
const public = () => {
return src("public/**", { base: "public" }).pipe(dest("dist"));
};
const useref = () => {
return src("dist/*.html", { base: "dist" })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCss: true,
minifyJs: true
})))
.pipe(dest("release"));
};
// 并行执行 样式-脚本-页面 编译
const compile = parallel(style, script, page);
// 先清空文件夹, 再编译+复制public文件夹 正式打开再处理 静态资源文件
const build = series(clean, parallel(compile, public, image, font));
const develop = series(compile, serve);
module.exports = {
clean,
build,
serve,
develop,
compile,
useref
};
测试
yarn gulp compile
yarn gulp useref
重新规划合理的构建过程
添加临时文件 tmp 避免读写流冲突(不能读写都是dist 文件)
const { src, dest, parallel, series, watch } = require("gulp");
const del = require("del");
const loadPlugins = require("gulp-load-plugins");
const bs = require("browser-sync");
const plugins = loadPlugins();
// const sass = require("gulp-sass");
// const babel = require("gulp-babel");
// const swig = require("gulp-swig");
// const imagemin = require("gulp-imagemin");
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 clean = () => {
// del 返回的是个 promise
return del(["tmp"]);
};
// scss 转 css
const style = () => {
// 指定从 src 下保留路径
return (
src("src/assets/styles/*.scss", { base: "src" })
// scss 处理 并保留花括号换行
.pipe(plugins.sass({ outputStyle: "expanded" }))
.pipe(dest("tmp"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译脚本文件
const script = () => {
// 指定从 src 下保留路径
return (
src("src/assets/scripts/*.js", { base: "src" })
// ES6+ 处理 注意这里要提供 babel 预设, 不然就不转换
.pipe(plugins.babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("tmp"))
.pipe(bs.reload({ stream: true }))
);
};
// 编译模板文件
const page = () => {
// src/**/*.html 匹配任意子目录
return src("src/*.html", { base: "src" })
.pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
.pipe(dest("tmp"))
.pipe(bs.reload({ stream: true }));
};
// 转换图片文件
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" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
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/**', public)
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
bs.reload
);
bs.init({
notify: false, // browser-sync 连接提示
port: 2080,
open: true, // 自动打开浏览器窗口
// files: "dist/**", // 监听dist文件变化
server: {
baseDir: ["tmp", "src", "public"],
routes: {
"/node_modules": "node_modules",
},
},
});
};
// 将public 原样输出到 dist
const public = () => {
return src("public/**", { base: "public" }).pipe(dest("dist"));
};
const useref = () => {
return src("tmp/*.html", { base: "tmp" })
.pipe(plugins.useref({ searchPath: ['tmp', '.'] }))
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCss: true,
minifyJs: true
})))
.pipe(dest("dist"));
};
// 并行执行 样式-脚本-页面 编译
const compile = parallel(style, script, page);
// 先清空文件夹, 再编译+复制public文件夹 正式打开再处理 静态资源文件
const build = series(clean, parallel(series(compile, useref), public, image, font));
const develop = series(compile, serve);
module.exports = {
build, // 正式打包
develop // 开发环境
};
配置一下 npm scripts 脚本
本文地址:https://blog.csdn.net/qinshengnan520/article/details/111133256