Appearance
Gulp4构建案例 案例
Gulp案例
模板下载 gulp-pro-demo
自动化构建诉求
- 所有的
scss
、less
文件编译成css
(scss
中下划线开头的不会被编译) - 将所有的js文件中的ES6语法进行适配
- html中的模板要进行编译
- 图片和文字要进行压缩
- 公共文件原封不动输出
- 在输出文件之前要对之前的输出目录进行删除
- 可以实现开发时浏览器热更新
- 第三方文件引用处理
- 对html/css/js文件进行压缩
- 导出内容发布到
dist
文件夹
样式编译
关于样式这里的操作我们需要将所有的scss
、less
文件编译成css
文件,所以下面进行具体操作。
gulp-sass
将sass
转化成css
- 下载模块
npm install gulp-sass
- 在
gulpfile.js
里面写
js
const { src, dest } = require('gulp')
// 引入sass模块
const sass = require('gulp-sass')
// 创建style任务
const style = () => {
// 输入scss的文件路径,并且指定根目录是src,在dist输出目录中,以src目录的结构输出
return src('src/assets/styles/*.scss', { base: 'src'})
// 进行sass向css转换,并且指定的样式是完全展开
// (如果不设置完全展开,那么默认css样式的右花括号不折行)
.pipe(sass({ outputStyle: 'expanded' }))
// 输出到dist文件夹
.pipe(dest('dist'))
}
module.exports = {
style
}
- 在命令行输入
gulp style
可以看到样式已经编译成css
文件
gulp-less
将less
转化成css
- 下载模块
npm install gulp-less
- 进行私有前缀匹配,下载less插件
npm install less-plugin-autoprefix
- 在
gulpfile.js
里面写
js
const { src, dest} = require('gulp')
// 引入less模块
const less = require('gulp-less')
// 引入less插件,私有前缀兼容
const LessAutoprefix = require('less-plugin-autoprefix')
const autoprefix = new LessAutoprefix({browsers: ["last 2 versions"]});
const lessStyle = () => {
return src('src/assets/styles/*.less', { base: 'src'})
// 进行less向css转换,并且引用插件私有前缀适配
.pipe(less({
plugins: [autoprefix]
}))
// 输出到dist文件夹
.pipe(dest('dist'))
}
module.exports = {
lessStyle
}
- 命令行写
gulp lessStyle
可以看到文件进行了编译
less
/* hello.less */
@bg : #000;
@fontColor: #fff;
.main{
background-color: @bg;
color: @fontColor;
transform: translateX(30px);
}
/* hello.css */
.main {
background-color: #000;
color: #fff;
-webkit-transform: translateX(30px);
transform: translateX(30px);
}
PS: less里面有很多别的插件可以参考:Pre-Loaded Plugins
脚本编译
我们还需要将ES6
的语法进行编译
gulp-babel
- 下载模块
npm install gulp-babel
- 使用还需要下载插件
npm install @babel/core @babel/preset-env
- 在
gulpfile.js
里面写
js
const { src, dest} = require('gulp')
// 引入babel模块
const babel = require('gulp-babel')
// 创建babel任务
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src'})
// 使用babel转换ES6语法
.pipe(babel({
// 插件集合,最新特性的全部打包,不写这个转换没有效果
presets: ['@babel/preset-env']
}))
// 输出到dist文件夹
.pipe(dest('dist'))
}
module.exports = {
script
}
- 命令行写
gulp script
可以看到文件进行了编译
js
/* 编译前main.js */
// TODO: site logics
$(($) => {
const $body = $('html, body')
$('#scroll_top').on('click', () => {
$body.animate({ scrollTop: 0 }, 600)
return false
})
})
/* 编译后main.js */
"use strict";
// TODO: site logics
$(function ($) {
var $body = $('html, body');
$('#scroll_top').on('click', function () {
$body.animate({
scrollTop: 0
}, 600);
return false;
});
});
页面模板编译
gulp-swig
- 下载模块
npm install gulp-swig
- 在
gulpfile.js
里面写
js
const { src, dest} = require('gulp')
// 引入swig模块
const swig = require('gulp-swig')
// 创建模板引擎任务
const page = () => {
// 通配符匹配src下main的所有子目录中的html
return src('src/**/*.html', { base: 'src' })
.pipe(swig())
.pipe(dest('dist'))
}
module.exports = {
page
}
- 命令行写
gulp page
可以看到文件进行了编译 - 可以添加固定的参数,也可以加一个
json
文件去写项目长用常数参数
js
const data = {
a: 1,
b: 2
}
const page = () => {
return src('src/**/*.html', { base: 'src' })
// 通过参数传递进去
.pipe(swig({data: data}))
.pipe(dest('dist'))
}
图片和文字转换
gulp-imagemin
这个插件可以压缩图片,字体文件如果是svg
也可以进行压缩
- 下载模块
npm i gulp-imagemin --save-dev
因为这个插件里面有用到
c++
二进制流的编译,所以如果上面这样安装不成功或者安装成功但是使用的时候显示缺少默认插件的时候,就这样做:
nrm use taobao
npm install -g cnpm
cnpm install gulp-imagemin --save-dev
等同于
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install gulp-imagemin --save-dev
- 在
gulpfile.js
里面写
js
const { src, dest} = require('gulp')
// 引入imagemin模块
const imagemin = require('gulp-imagemin');
// 图片压缩任务
const image = () => {
// 匹配images下面的所有文件
return src('src/assets/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
// 图片压缩任务
const font = () => {
// 匹配images下面的所有文件
return src('src/assets/fonts/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
module.exports = {
image,
font
}
- 命令行写
gulp image
可以看到文件进行了压缩,图片压缩26.5%
bash
gulp image
# Using gulpfile E:\professer\Gulp\gulpfile.js
# Starting 'image'...
# gulp-imagemin: Minified 2 images (saved 22.8 kB - 26.5%)
# Finished 'image' after 985 ms
- 命令行写
gulp font
可以看到文字的svg
进行了压缩,其他的不能被压缩的文件原封不动被输出
bash
gulp font
# Using gulpfile E:\professer\Gulp\gulpfile.js
# Starting 'font'...
# gulp-imagemin: Minified 1 image (saved 679 B - 5.9%)
# Finished 'font' after 503 ms
其他原本文件输出
项目中都有public
目录,这些存放一些静态的公共目录,直接输出到dist
文件即可。
js
// 将public的文件进行额外输出
const extra = () => {
return src('public/**', { base: 'pubilc' })
.pipe(dest('dist'))
}
清除文件
在编译输出文件之前,需要把之前的目录删掉,使用del
模块进行操作。这个不是gulp
插件,但是因为其返回一个promise
对象,所以在gulp
中也可以使用。
下载模块
npm i del --save-dev
在
gulpfile.js
里面写
js
const { src, dest, parallel, series } = require('gulp')
// 引入清除文件模块
const del = require('del')
const clean = () => {
return del(['dist'])
}
module.exports = {
clean
}
- 命令行写
gulp clean
可以把对应文件进行删除
自动加载插件
上面引用了那么多的模块,那么需要安装一个自动加载插件的模块,可以减少很多代码
- 下载模块
npm i gulp-load-plugins --save-dev
- 在
gulpfile.js
里面写
js
const { src, dest, parallel, series } = require('gulp')
// 引入加载文件模块
const loadPlugins = require('gulp-load-plugins')
// 调用方法,也可以引用的时候直接调用
// const plugins = require('gulp-load-plugins')()
const plugins = loadPlugins()
// 输出结构可以看到
console.log(plugins)
/*
默认匹配gulp-后面所有的插件
如果有gulp-clean-css的后面采用驼峰命名为键
这些模块在使用的时候必须都安装好
{
babel: [Getter],
cleanCss: [Getter],
imagemin: [Getter],
less: [Getter],
rename: [Getter],
sass: [Getter],
swig: [Getter]
}
*/
// 不是gulp开头的要单独安装,或者修改loadPlugins的匹配规则
const LessAutoprefix = require('less-plugin-autoprefix')
const autoprefix = new LessAutoprefix({browsers: ["last 2 versions"]});
// 创建sass任务
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src'})
// 之后使用的时候就用plugins点出来即可
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
// 创建less任务
const lessStyle = () => {
return src('src/assets/styles/*.less', { base: 'src'})
.pipe(plugins.less({
plugins: [autoprefix]
}))
.pipe(dest('dist'))
}
// 创建babel任务
const script = () => {
...
}
// 创建模板引擎任务
const page = () => {
...
}
// 图片压缩任务
const image = () => {
...
}
// 图片压缩任务
const font = () => {
...
}
热更新开发服务器browser-sync
当我们修改的时候,这个插件可以让我们直接看到浏览器刷新
- 下载模块
npm install browser-sync --save-dev
- 在
gulpfile.js
里面写
js
const { src, dest, parallel, series } = require('gulp')
// 引入开发服务器模块
const browserSync = require('browser-sync')
// 使用create方法会自动创建一个开发服务器
const bs = browserSync.create()
// 创建服务任务
const serve = () => {
// 进行初始化,里面可以指定一些配置
bs.init({
// 设置开屏右上角链接提示:false去掉
notify: false,
// 端口,默认3000
port: 2080,
// 是否会自动打开浏览器:false是关闭,默认是开启
// open: false,
// 启动过后监听的文件,如果文件有修改就主动刷新
files: 'dist/*',
// 核心配置
server: {
// 网站根目录
baseDir: 'dist',
// 优先于baseDir,会先匹配这个配置,没有就会去baseBir中获取,如果引用的css,js文件中有不在dist文件夹里面的,可以匹配这个。如果没有可以不用写
routes: {
'/node_modules': 'node_modules'
}
}
})
}
module.exports = {
serve
}
- 使用命令行执行
gulp serve
,可以看到浏览器自动执行,而且修改dist
下面的文件,会自动热更新。 - 是监听
dist
文件,但是我们需要在开发的目录下面,修改之后自动编译文件再更新到浏览器。需要用到gulp
的方法watch
- 再
gulpfile.js
中这样写
js
const { src, dest, parallel, series, watch } = require('gulp')
const browserSync = require('browser-sync')
const bs = browserSync.create()
const serve = () => {
// 监听文件变化并执行对应任务
watch('src/assets/styles/*.scss', style)
watch('src/assets/styles/*.less', lessStyle)
watch('src/assets/scripts/*.js', script)
watch('src/**/*.html', page)
// 开发阶段不需要每一次修改都压缩文件,这些只是修改的时候重新加载即可
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
bs.init({
notify: false,
port: 2080,
files: 'dist/*',
server: {
// 网站根目录,多个的时候写成数组,如果路径找不到会依此去路径中寻找
baseDir: ['dist', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
}
当然更多的是这么用的,在任务后面手动调用更新,就可不用写init
里面的files
js
// 创建sass任务
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src'})
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
// 以流的方式往浏览器推,每次任务执行完,都自动reload一下
.pipe(bs.reload({ stream: true }))
}
PS: 这里要说一下
gulp-swig
,因为模板缓存机制可能会导致无法热更新,需要进行配置jsconst page = () => { return src('src/**/*.html', { base: 'src' }) //swig因为模板缓存的关系无法热更新,所以需要默认设置里面关闭缓存 .pipe(plugins.swig({defaults: { cache: false }})) .pipe(dest('dist')) .pipe(bs.reload({ stream: true })) }
useref文件引用处理
如果项目中有引入dist
里面没有的文件,上线会有问题,所以需要进行处理
- 下载模块
npm i gulp-useref --save-dev
- 在
gulpfile.js
里面写
js
const { src, dest } = require('gulp')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const useref = () => {
// dist当中的所有文件注释,进行打包压缩
return src('dist/*.html', { base: 'dist' })
// 文件寻找路径依此进行查找,找到之后根据写的文件注释进行打包
.pipe(plugins.useref({ searchPath: ['dist', '.']}))
// 读和写如果是一个文件夹可能会有问题
.pipe(dest('release'))
}
module.exports = {
useref
}
- 命令行写
gulp useref
可以把对应注释文件进行了打包处理。并且在release
文件夹中出现了vender.js
和vender.css
的一系列打包文件
html
<!-- 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:js assets/scripts/vendor.js -->
<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/popper.js/dist/umd/popper.js"></script>
<script src="/node_modules/bootstrap/dist/js/bootstrap.js"></script>
<!-- endbuild -->
<!-- build:js assets/scripts/main.js -->
<script src="assets/scripts/main.js"></script>
<!-- endbuild -->
变成了
js
<link rel="stylesheet" href="assets/styles/vendor.css">
<link rel="stylesheet" href="assets/styles/main.css">
<script src="assets/scripts/vendor.js"></script>
<script src="assets/scripts/main.js"></script>
但是这些都是未压缩过的文件,还需要进行压缩操作
html/css/js文件压缩
上面生成的文件都是未压缩过的,所以下面要进行压缩
- 安装压缩插件压缩
html/css/js
文件,npm i gulp-htmlmin gulp-uglify gulp-clean-css --save-dev
- 为了进行区分文件的不同,还需要安装
npm i gulp-if --save-dev
- 在
gulpfile.js
里面写
js
const { src, dest } = require('gulp')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.']}))
// 在这里会生成html js css三种类型的文件,需要对这三种文件进行压缩操作
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
// 不加collapseWhitespace只是压缩一些空格,加上会把这行等空白字符都压缩
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
// 行内样式里面的css和js用这个参数可以进行压缩
minifyCSS: true,
minifyJS: true
})))
.pipe(dest('release'))
}
module.exports = {
useref
}
- 命令行写
gulp useref
可以把所有的文件都压缩完成
对所有的插件和目录进行调整
- 我们的目标是在
src
目录下开发,使用dist
目录上线,中间的转换利用临时目录temp
保存 - 有一个开发环境的任务
develop
,有一个生产环境的任务build
develop
任务需要编译css
,js
和template
,还需要本地起服务进行热更新build
任务需要编译css
,js
和template
,需要压缩css,js,template,image,font
和其他文件,需要外联文件进行打包压缩- 最后在
package.json
中注册script
,直接用命令行就可以运行
下面是完整的代码
js
// gulpfile.js
// src 输入、dest 输出、parallel 并行、series 串行、watch 监听
const { src, dest, parallel, series, watch } = require('gulp')
// 引入清除文件模块
const del = require('del')
// if有less引入兼容适配
const LessAutoprefix = require('less-plugin-autoprefix')
// 引入浏览器监听服务模块
const browserSync = require('browser-sync')
// 引入gulp插件管理模块
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
// 使用create方法会自动创建一个开发服务器
const bs = browserSync.create()
const autoprefix = new LessAutoprefix({browsers: ["last 2 versions"]});
// 创建清除文件任务
const clean = () => {
// 每次执行的时候,先把之前的dist目录删除,再删除临时目录temp
return del(['dist', 'temp'])
}
// 创建sass任务
const style = () => {
// 输入scss的文件路径,并且指定根目录是src,在dist输出目录中,以src目录的结构输出
return src('src/assets/styles/*.scss', { base: 'src'})
// 进行sass向css转换,并且指定的样式是完全展开
// (如果不设置完全展开,那么默认css样式的右花括号不折行)
.pipe(plugins.sass({ outputStyle: 'expanded' }))
// 输出到temp临时文件夹
.pipe(dest('temp'))
// 以流的方式往浏览器推
.pipe(bs.reload({ stream: true }))
}
// 创建less任务
const lessStyle = () => {
return src('src/assets/styles/*.less', { base: 'src'})
// 进行sass向css转换,并且指定的样式是完全展开
// (如果不设置完全展开,那么默认css样式的右花括号不折行)
.pipe(plugins.less({
plugins: [autoprefix]
}))
// 输出到temp临时文件夹
.pipe(dest('temp'))
// 以流的方式往浏览器推
.pipe(bs.reload({ stream: true }))
}
// 创建babel任务
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
// 使用babel转换ES6语法
.pipe(plugins.babel({
// 插件集合,最新特性的全部打包,不写这个转换没有效果
presets: ['@babel/preset-env']
}))
// 输出到temp临时文件夹
.pipe(dest('temp'))
// 以流的方式往浏览器推
.pipe(bs.reload({ stream: true }))
}
// 创建模板引擎任务
const page = () => {
// 通配符匹配src下main的所有子目录中的html
return src('src/**/*.html', { base: 'src' })
//swig因为模板缓存的关系无法热更新,所以需要默认设置里面关闭缓存
.pipe(plugins.swig({defaults: { cache: false }}))
// 不需要进行临时文件夹中操作
.pipe(dest('temp'))
// 以流的方式往浏览器推
.pipe(bs.reload({ stream: true }))
}
// 图片压缩任务
const image = () => {
// 匹配images下面的所有文件
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
// 图片压缩任务
const font = () => {
// 匹配images下面的所有文件
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
// 将public的任务进行额外输出
const extra = () => {
return src('public/**', { base: 'pubilc' })
.pipe(dest('dist'))
}
// 创建服务任务
const serve = () => {
// 监听文件变化并执行对应任务,写这个就不用写bs.reload()了
watch('src/assets/styles/*.scss', style)
watch('src/assets/styles/*.less', lessStyle)
watch('src/assets/scripts/*.js', script)
watch('src/**/*.html', page)
// 开发阶段不需要每一次修改都压缩文件,这些只是修改的时候重新加载即可
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
// 进行初始化
bs.init({
// 设置开屏右上角链接提示:false去掉
notify: false,
// 端口
port: 2080,
// 是否会自动打开浏览器:false是关闭
// open: false,
// 启动过后监听的文件,如果有修改就主动刷新,有reload就不需要这个files配置
// files: 'temp/*',
// 核心配置
server: {
// 网站根目录,多个的时候写成数组,如果路径找不到会依此去路径中寻找
// 文件先从temp中寻找
baseDir: ['temp', 'src', 'public'],
// 优先于baseDir,会先匹配这个配置,没有就会去baseBir中获取
routes: {
'/node_modules': 'node_modules'
}
}
})
}
// 根据文件注释打包外联文件并且产出代码进行压缩任务
const useref = () => {
// dist当中的所有文件注释,进行打包压缩
return src('temp/*.html', { base: 'temp' })
// 文件寻找路径依此进行查找
.pipe(plugins.useref({ searchPath: ['temp', '.']}))
// 在这里会生成html js css三种类型的文件,需要对这三种文件进行压缩操作
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
// 不加collapseWhitespace只是压缩一些空格,加上会把这行等空白字符都压缩
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
// 行内样式里面的css和js用这个参数可以进行压缩
minifyCSS: true,
minifyJS: true
})))
// 不要和dist一个目录,边读边写会有问题
.pipe(dest('dist'))
}
// 执行编译的组合任务
const compile = parallel(style, lessStyle, script, page)
// 生产环境时候的构建任务
/* - 先删除所有的文件夹
- 下面的东西都可以并行,但是其中应该先编译sass,less,es6,template到temp临时目录,然后再压缩到dist目录
- 图片、文字和其他的东西可以直接放到dist目录下
*/
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra
)
)
// 开发时候的构建任务
// 只需要编译之后发布到本地服务器上即可
const develop = series(compile, serve)
// 暴露开发和生产打包的任务命令
module.exports = {
clean, // 删除文件可以暴露
build,
develop
}
在package.json
中注册一下命令
js
//package.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
}
}
在命令行中直接运行npm run build
和npm run develop
即可。然后在.gitignore
中把dist
和temp
目录加上提交忽略。