自动化构建工具
Grunt、Gulp、FIS是最常见的自动化构建工具。
- Grunt是最早的自动化构建工具,生态非常完善,缺点是构建过程是基于临时(磁盘)文件的,构建速度相对较慢。
- Gulp的构建是基于内存实现的,是现在最流行的自动化构建工具,简单易学,拥有完善的生态和各种丰富的插件,由全世界开发者共同维护。
- FIS是由百度前端团队研发的开源工具,主要由百度负责维护。相对来说,其插件不是很丰富而且已经多年未更新维护了。
所以,我们主要学习Gulp。
Gulp
Gulp 是基于 流(内存)的自动化构建系统。
Gulp 的特点:
- 任务化
- 所有的构建操作,在 gulp 中都称之为任务
- 基于流
- gulp 中所有的文件操作,都是基于 流 方式进行 ( Gulp有一个自己的内存,通过指定 API 将源文件流到内存中,完成相应的操作后再通过相应的 API 流出去)
Gulp的作用
就是将繁多的代码文件进行整理,使其变得整洁。
Gulp与npm scripts
- Gulp与npm scripts都能够实现自动化构建
- Gulp语法简单
- Gulp语法就是JavaScript语法
- npm scripts语法接近shell脚本
- Gulp生态完善,构建效率高
Gulp安装
使用 Gulp 之前,先在全局安装 gulp-cli ( Gulp 的命令行工具 )
# 全局安装 gulp 客户端
npm i -g gulp-cli
# 验证安装是否成功
gulp -v
复制代码
官网:gulpjs.com/
Gulp使用
Gulp 使用的基本逻辑是:先声明任务,再从命令行中执行任务;具体步骤如下:
-
初始化项目
# 创建项目目录 mkdir project-name # 进入项目目录 cd project-name # 初始化项目 npm init --yes 复制代码
-
安装 Gulp 包
# 安装 gulp 包,作为开发时依赖项 npm i gulp -D 复制代码
-
在项目根目录手动创建
gulpfile.js
文件gulpfile.js
文件,在运行gulp
命令时会被自动加载。在这个文件中,你经常会看到类似src()
、dest()
、series()
或parallel()
函数之类的 Gulp API,除此之外,纯 JavaScript 代码或 Node.js 模块也会被使用。任何导出(exports)的函数都将注册到 Gulp 的任务(task)系统中。
假如我们在gulpfile.js文件中,写入如下代码并保存:// 创建任务,任务结束后,需要通过回调函数去标记 exports.foo = () => { console.log('foo task is running') } 复制代码
在项目根目录下,命令行输入
gulp foo
,可能会看到如下报错:报错:
The following tasks did not complete: task
Did you forget to signal async completion?解释:在最新的 Gulp 中,取消了同步代码模式。约定每个任务都必须是一个异步任务
解决:在函数参数中,设定回调函数(回调函数是异步操作),下面的步骤就会使用
-
在 gulpfile.js 中注册 Gulp 任务
在
gulpfile.js
中写入如下代码:// 创建任务,并导出任务 exports.foo = cb => { console.log('foo task is running') cb() } // 旧版 Gulp 注册任务的语法(无需执行导出操作) //gulp.task('foo', function(cb) { // console.log('foo task is running') // // cb() //}); 复制代码
-
运行 Gulp 任务
在项目根目录下,命令行输入
gulp foo
,即可运行Gulp 任务:# 运行 foo 任务 # 如需运行多个任务(task),可以执行 gulp <task> <othertask> gulp foo 复制代码
-
创建默认任务
在gulpfile.js
中添加如下代码:// 默认任务的名称是 default exports.default = cb => { console.log('default task is running') cb() } 复制代码
在项目根目录下,直接在命令行后输入
gulp
,运行默认任务。无需指定任务名称。gulp # 效果等同于 gulp default 复制代码
Gulp组合任务
Gulp 提供了两个强大的组合方法: series()
和 parallel()
。
如果需要让任务(task)按顺序执行,请使用 series()
方法(相当于 npm scripts 中的 && )。
如果希望任务(tasks)并行执行,可以使用 parallel()
方法将它们组合起来(相当于 npm scripts 中的 & )。
在gulpfile.js
中写入如下代码:
const gulp = require('gulp')
const task1 = cb => {
setTimeout(() => {
console.log('Task 1 is running')
cb()
}, 1000)
}
const task2 = cb => {
setTimeout(() => {
console.log('Task 2 is running')
cb()
}, 1000)
}
const task3 = cb => {
setTimeout(() => {
console.log('Task 3 is running')
cb()
}, 1000)
}
// 串行方式执行任务,先执行task1, 然后执行task2, 然后执行task3
exports.foo = gulp.series(task1, task2, task3)
// 并行方式执行任务,同时执行task1,task2,task3
exports.bar = gulp.parallel(task1, task2, task3)
复制代码
命令行执行gulp:
# 执行命令
gulp foo # 串行执行
gulp bar # 并行执行
复制代码
series()
和 parallel()
可以被嵌套到任意深度。通过这两个函数,构建任务可以被任意排列组合,从而满足各种复杂的构建需求。
Gulp文件操作
gulp 自带了 src()
(源文件目录) 和 dest()
(目标目录)方法用于处理计算机上存放的文件。在代码构建过程中,需要将源文件,写入到目标目录。
// 通过 解构 的方式引入 gulp 中的函数
const { src, dest } = require('gulp')
exports.default = () => {
// 文件操作
// 将 src/styles 目录下的 main.css 文件,复制到 dist/styles 目录下
// { base: 'src' }表示保持src目录下的目录结构
return src('src/styles/main.less', { base: 'src' }).pipe(dest('dist'))
}
复制代码
cmd命令行执行
# 执行命令
gulp default
# 或直接执行
gulp
复制代码
Gulp构建样式文件
构建样式文件就是对样式文件进行转换、压缩、重命名。
- cmd命令行安装相关的 Gulp 插件
npm i gulp-less -D
npm i gulp-autoprefixer -D
npm i gulp-clean-css -D
npm i gulp-rename -D
复制代码
- 在 gulpfile.js 中写入样式编译内容。确保项目文件夹下有
src/styles/*.less
目录及任意less文件
const { src, dest } = require('gulp')
const less = require('gulp-less')
// 给 CSS 属性添加前缀的插件(详情请看下方 CSS Hack)
const autoprefixer = require('gulp-autoprefixer')
// 压缩 CSS 的插件
const cleanCss = require('gulp-clean-css')
// 重命名转换文件的插件
const rename = require('gulp-rename')
const style = () => {
return src('src/styles/*.less', { base: 'src' })
.pipe(less())
.pipe(autoprefixer())// 保证css兼容性
.pipe(cleanCss())
.pipe(rename({extname: '.min.css'}))// 修改扩展名
.pipe(dest('dist'))
}
module.exports = {
style
}
复制代码
- 运行命令
gulp style
复制代码
通过样式文件的构建,我们可以更清晰的理解文件操作。
CSS Hack 和 autoprefixer 处理兼容问题
CSS Hack
由于不同浏览器中的渲染引擎不同,这导致了同一段 CSS 代码,在不同的浏览器上解析效果不同(即 CSS 代码具有兼容性问题)。
针对不同浏览器,写不同 CSS 代码的过程称为 CSS Hack。
CSS Hack 有三种形式:CSS 属性 Hack、CSS选择符 Hack 和 IE条件注释 Hack( Hack主要针对IE浏览器 )
属性级 Hack
比如 IE6 能识别下划线“
_
”和星号“*
”,IE7 能识别星号“*
”,但不能识别下划线”_
”选择符级 Hack
IE6 能识别
*html .class{}
IE7 能识别
*+html .class{}
或者*:first-child+html .class{}
IE 条件注释 Hack:
# 针对 IE6 及以下版本: <!--[if lt IE 6]>您的代码<![endif]--> 复制代码
这类 Hack 不仅对 CSS 生效,对写在判断语句里面的所有代码都会生效。
本小节,只讨论属性级 Hack
不同浏览器的 CSS 属性前缀:
例如:user-select 存在兼容性问题。CSS 属性 Hack 的解决方案如下:
上述添加 CSS 属性前缀的操作,之前是通过程序员手动添加的。这类重复性操作,我们可以通过插件完成。
在 Gulp 中 gulp-autoprefixer 插件,可以根据 caniuse.com 上提供的 CSS 兼容性数据,自动地给 CSS 属性加前缀,以保证 CSS 代码的兼容性问题。
Gulp构建脚本文件
对 JS 代码进行 Babel 转换和压缩。
注意:因为不同 babel 版本对应的 gulp-babel 的安装命令不同,所以安装 gulp-babel 之前需要先确定本地 babel 版本 (通过 babel –version 查看)
# Babel 7 $ npm install --save-dev gulp-babel @babel/core @babel/preset-env # Babel 6 $ npm install --save-dev gulp-babel@7 babel-core babel-preset-env 复制代码
# 我本地的 babel 版本是 6,所以,执行 6 的安装命令
npm install --save-dev gulp-babel@7 babel-core babel-preset-env
# 安装压缩脚本的插件
npm i gulp-uglify -D
# 在 gulpfile.js 中添加脚本编译内容
const rename = require('gulp-rename')
const babel = require('gulp-babel')
const uglify = require('gulp-uglify')
const script = () => {
return src('src/scripts/*.js', { base: 'src' })
.pipe(babel({
presets: [ 'babel-preset-env' ] // 不同版本的 babel,其转换规则写法也不同
}))
.pipe(uglify())
.pipe(rename({ "extname": ".min.js" }))
.pipe(dest('dist'))
}
module.exports = {
style,
script
}
# 运行命令
gulp script
复制代码
页面模板构建
对 html 文件的构建,主要指压缩 html 文件。其中 gulp-htmlmin 插件可以完成压缩任务。
gulp-htmlmin 插件的解析器是:github.com/kangax/html…
想要查看 htmlmin 的使用参数,可以查看上述链接。
# 添加 htmlmin 插件
npm i gulp-htmlmin -D
# 在 gulpfile.js 中添加页面处理内容
const htmlmin = require('gulp-htmlmin')
const html = () => {
return src('src/*.html', { base: 'src' })
.pipe(htmlmin({
collapseWhitespace: true, // 去除标签之间多余的空行和空白
minifyCSS: true, // 压缩HTML中的CSS代码
minifyJS: true // 压缩HTML中的JS代码
}))
.pipe(dest('temp'))
}
module.exports = {
style,
script,
html
}
# 运行命令
gulp html
复制代码
完成上述三个构建任务后,我们可以将 style,script 和 html 任务组合起来。因为 style,script 和 html 之间没有明确的前后顺序,所以,可以进行并行执行,并行执行可以提升构建效率。
# 引入 parallel 函数
const { src, dest, parallel } = require('gulp')
// 任务的并行执行
const build = parallel(style, script, html)
module.exports = {
build
}
# 运行命令
gulp build
复制代码
图片(字体)文件转换
对图片文件的构建,主要是指图片的压缩。通过 gulp-imagemin 插件可以完成图片的压缩任务。
# 安装 imagemin 插件
npm i gulp-imagemin -D
# 在 gulpfile.js 中引入图片压缩插件
const imagemin = require('gulp-imagemin')
// 图片构建任务
const image = () => {
return src('src/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
// 图片构建任务,也可以与以上三个任务一起,并行执行
const build = parallel(style, script, html, image)
module.exports = {
image,
build
}
# 运行命令
gulp build
复制代码
报错处理:
gulp-imagemin: Couldn’t load default plugin “gifsicle”
gulp-imagemin: Couldn’t load default plugin “optipng”
原因:npm 安装依赖失败
解决:
- 配置 hosts (详情参考本节最后的附录部分)
- 重新安装 npm i gulp-imagemin -D或cnpm i gulp-imagemin -D
文件清除
有时候,我们需要删除一些构建的历史文件,然后再重新构建。删除文件操作,可以通过 del 插件完成。
# 通过del插件来删除指定文件
npm i del -D
const del = require('del')
// 声明清除任务
const clean = () => {
return del(['dist'])
}
// 编译之前,先执行clean,删除历史文件
const build = parallel(style, script, html, image)
const dev = series(clean, build)
module.exports = {
clean,
dev
}
# 运行命令,查看文件是否删除
gulp clean
# 或者
gulp dev
复制代码
开发服务器
通过web服务器插件,将 dist 下的代码,发布到浏览器查看效果。发布web服务的插件有很多。这里,我们推荐功能强大的 browser-sync。
# 安装 browser-sync 插件
npm i browser-sync -D
# 在 gulpfile.js 中添加开发服务器的内容
const browserSync = require('browser-sync')
const bs = browserSync.create()
// 声明 web 服务构建任务
const serve = () => {
bs.init({
server: {
baseDir: './dist' // 指定服务启动的目录
routes: {
'/node_modules': 'node_modules' // 引入 Bootstrap 是设置路径映射
}
}
})
}
module.exports = {
clean,
build,
serve
}
# 运行命令,然后在浏览器查看效果
gulp serve
复制代码
服务发布成功后,之前学习的内容,也可以拿过来使用,例如:Bootstrap
下载插件
# 因为 Bootstrap 和 jQuery 上线之后还要使用,所以,采用 -S 参数(上线依赖) npm i bootstrap@3.4.1 jquery -S 复制代码
引入文件
Bootstrap 和 jQuery 下载后,文件位于当前目录的 node_modules 下
# 在 HTML 中引入 <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"> ...... <script src="/node_modules/jquery/dist/jquery.min.js"></script> <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script> 复制代码
引入路径,需要在 browser-sync 中,通过 routes 参数映射后,才能正确引入(详情查看上述代码)
使用 Bootstrap
之前学习的 Bootstrap 的代码,都可以在当前代码中使用。
监视变化(热更新)
监视 src 下文件变化的页面更新,代码一旦更新,浏览器上的页面效果也随之更新。
此时,我们需要监视两个目录的变化,一个是 dist 目录,一个是 src 目录。
-
监视 dist 目录下代码的变化
# 通过 browser-sync 中的 files 字段 files: 'dist/**' 复制代码
-
监视 src 目录下代码的变化
# 通过 gulp 中的 watch 函数 watch(被监视的文件,对应的任务) 复制代码
最终的代码如下:
# 在 gulpfile.js 中添加监视文件变化的代码
const serve = () => {
// watch(被监视的文件,对应的任务)
watch('src/index.html', html)
watch('src/styles/*.less', style)
watch('src/js/*.js', script)
watch('src/images/**', image)
// 初始化服务
bs.init({
notify: false, // 禁用浏览器右上角的 browserSync connected 提示框
files: 'dist/**', // 监视 dist 下 文件的变化,然后在浏览器上实时更新
server: {
baseDir: './dist', // 指定服务启动的目录
routes: {
'/node_modules': 'node_modules'
}
}
})
}
// 组合任务
const build = parallel(style, script, html, image)
const dev = series(clean, build, serve)
// 导出任务
module.exports = {
build,
dev,
serve
}
# 运行命令,更新代码文件,查看页面变化
gulp dev
复制代码
此时,不管是 dist 目录下,还是 src 目录下。只要代码发生变化,我们就可以在浏览器上实时看到效果。
附录
1. Win10 配置 hosts
-
通过vscode 打开 hosts 文件
# hosts 文件的路径 C:\Windows\System32\Drivers\etc 复制代码
-
添加 Guthub 相关的内容
将下面的内容复制,然后追加到 hosts 文件的尾部
# GitHub Start (chinaz.com) ================================================= 13.229.188.59 github.com 54.169.195.247 api.github.com 140.82.113.25 live.github.com 8.7.198.45 gist.github.com # 185.199.108.154 github.githubassets.com # 185.199.109.154 github.githubassets.com 185.199.110.154 github.githubassets.com # 185.199.111.154 github.githubassets.com 34.196.247.240 collector.githubapp.com # 52.7.232.208 collector.githubapp.com 52.216.92.163 github-cloud.s3.amazonaws.com 199.232.96.133 raw.githubusercontent.com 151.101.108.133 user-images.githubusercontent.com 151.101.108.133 avatars.githubusercontent.com 151.101.108.133 avatars0.githubusercontent.com 151.101.108.133 avatars1.githubusercontent.com 151.101.108.133 avatars2.githubusercontent.com 151.101.108.133 avatars3.githubusercontent.com 151.101.108.133 avatars4.githubusercontent.com 151.101.108.133 avatars5.githubusercontent.com 151.101.108.133 avatars6.githubusercontent.com 151.101.108.133 avatars7.githubusercontent.com 151.101.108.133 avatars8.githubusercontent.com 151.101.108.133 avatars9.githubusercontent.com 151.101.108.133 avatars10.githubusercontent.com 151.101.108.133 avatars11.githubusercontent.com 151.101.108.133 avatars12.githubusercontent.com 151.101.108.133 avatars13.githubusercontent.com 151.101.108.133 avatars14.githubusercontent.com 151.101.108.133 avatars15.githubusercontent.com 151.101.108.133 avatars16.githubusercontent.com 151.101.108.133 avatars17.githubusercontent.com 151.101.108.133 avatars18.githubusercontent.com 151.101.108.133 avatars19.githubusercontent.com 151.101.108.133 avatars20.githubusercontent.com # GitHub End =================================================================== 复制代码
-
保存文件
ctrl+s 保存,此时如果报:没有权限,点击以管理员身份重试
2. Mac 配置 hosts
-
打开终端
-
输入 sudo vi /etc/hosts
-
输入密码
-
进入文件 hosts,然后按 “i”,进入编辑模式
-
把你的内容添加到最后
# GitHub Start (chinaz.com) ================================================= 13.229.188.59 github.com 54.169.195.247 api.github.com 140.82.113.25 live.github.com 8.7.198.45 gist.github.com # 185.199.108.154 github.githubassets.com # 185.199.109.154 github.githubassets.com 185.199.110.154 github.githubassets.com # 185.199.111.154 github.githubassets.com 34.196.247.240 collector.githubapp.com # 52.7.232.208 collector.githubapp.com 52.216.92.163 github-cloud.s3.amazonaws.com 151.101.108.133 raw.githubusercontent.com 151.101.108.133 user-images.githubusercontent.com 151.101.108.133 avatars.githubusercontent.com 151.101.108.133 avatars0.githubusercontent.com 151.101.108.133 avatars1.githubusercontent.com 151.101.108.133 avatars2.githubusercontent.com 151.101.108.133 avatars3.githubusercontent.com 151.101.108.133 avatars4.githubusercontent.com 151.101.108.133 avatars5.githubusercontent.com 151.101.108.133 avatars6.githubusercontent.com 151.101.108.133 avatars7.githubusercontent.com 151.101.108.133 avatars8.githubusercontent.com 151.101.108.133 avatars9.githubusercontent.com 151.101.108.133 avatars10.githubusercontent.com 151.101.108.133 avatars11.githubusercontent.com 151.101.108.133 avatars12.githubusercontent.com 151.101.108.133 avatars13.githubusercontent.com 151.101.108.133 avatars14.githubusercontent.com 151.101.108.133 avatars15.githubusercontent.com 151.101.108.133 avatars16.githubusercontent.com 151.101.108.133 avatars17.githubusercontent.com 151.101.108.133 avatars18.githubusercontent.com 151.101.108.133 avatars19.githubusercontent.com 151.101.108.133 avatars20.githubusercontent.com # GitHub End =================================================================== 复制代码
-
control+c 退出编辑模式
-
输入 :wq,保存退出