自动化构建
什么是构建
- 构建是将源代码转换成生产代码的过程
为什么构建
- 一些代码需要编译(CSS,JS), 保证浏览器的兼容性
将 Less 或 Sass 转换成 CSS
将 ES6+ 的新语法转成 ES5 - 有些代码需要压缩(CSS,JS,HTML,图片等)
压缩之后的代码体积更小,加载更快,节省带宽 - 有些代码需要做格式化校验,统一代码风格
- 构建过程:源代码通过编译、压缩、格式化等等转化成生成代码
什么是自动化构建
- 不管是代码压缩还是 less 转换,通过手动方式进行工作量巨大,自动化构建是指将手动构建任务,进行排列组合,然后通过命令(或工具)自动执行的过程
- 实现自动化构建最简单的方式是 npm scripts (npm 脚本)
npm scripts
- npm 在
package.json
文件里面,使用scripts
字段定义脚本命令
{
"scripts": {
// 命令名称: 任务
"foo": "node bar.js"
}
}
# `scripts` 字段是一个对象。它的每一个属性,对应一段脚本。比如,`foo` 命令对应的脚本是`node bar.js`。
# 命令行下使用 npm run <命令>,就可以执行这段脚本。
$ npm run foo
# 等同于执行
$ node bar.js
- npm 脚本就是 Shell 脚本,因为可以使用 Shell 通配符
"style": "lessc *.less"
"style": "lessc **/*.less"
* 表示任意文件名,** 表示任意一层子目录
- 执行顺序
如果是并行执行(即同时的平行执行),可以使用&
符号
{
"scripts": {
"parallel": "node task1.js & node task2.js & node task3.js"
}
}
如果是串行执行(前一个任务成功后,才执行下一个任务),可以使用 &&
符号
{
"scripts": {
"series": "node task1.js && node task2.js && node task3.js"
}
}
但是,& 符号在 Windows 操作系统下不起作用。此时,我们可以借助插件,在 Windows 下实现并行操作:npm-run-all
构建样式文件
构建样式文件就是将开发环境下的 JavaScript 源代码,转成线上环境使用的代码。这里的构建任务可能有多个。
在开发过程中,经常使用 ES6+ 新特性时,一些旧的浏览器,不支持 JS 的新语法。所以,在项目上线之前,就需要将新的语法特性解析成兼容性更好的 ES5 。最常用的编译工具是 Babel
Babel https://babeljs.io/
# 安装 babel核心,Babel客户端
npm i -g babel-core babel-cli
# 安装转码规则
npm i -g babel-preset-env
# 在项目根目录下,新建 .babelrc 文件(注意文件名前有一个点),并添加转换规则
{
"presets": [
"env"
],
}
# 通过 babel 编译单个 j s文件
babel input.js --out-file output.js
# 或者
babel input.js -o output.js
# 通过 babel 编译整个目录
babel js --out-dir scripts
# 或者
babel js -d scripts
# 在 package.json 中,添加 babel 解析命令
"scripts": {
"script": "babel js -d scripts",
}
# 执行命令(自动编译)
npm run script
# 如果 Babel 是局部安装。则babel 的可执行路径是:./node_modules/.bin/babel命令需要做相应的调整
# babel js -d scripts ===> ./node_modules/.bin/babel js -d scripts
- 添加压缩命令
# 在 package.json 中,添加 babel 解析和压缩命令
"scripts": {
"script": "babel js -d scripts && minify scripts/main.js > scripts/script.min.js",
}
代码格式校验
使用 ESLint 来检测 JavaScript 代码
# 配置检测规则
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"indent": [ "error", 2 ], # 使用两个空格缩进
"quotes": [ "error", "double" ] # 使用双引号包裹字符串
}
}
- 检测 JS 代码风格的工具有多个,除了 ESLint, 另一个常用的是 Standard:
https://github.com/standard/standard
StyleLint
StyleLint 是检测 CSS 代码格式的插件
官网:https://stylelint.io/
- 在项目的根目录下,创建 .stylelintrc.json 文件,添加如下配置
{
"extends": "stylelint-config-standard",
"rules": {
# 除了使用 stylelint-config-standard,我们还可以在 rules 字段中自定义校验规则
"block-no-empty": true # 代码块不能为空
}
}
自动化构建工具Gulp
自动化构建工具,可以帮我们又快又好的完成自动化构建任务。相比有 npm scripts,自动化构建工具,功能更为强大。更简单易学
- Gulp 的构建是基于内存实现的,其构建速度比 Grunt 快,而且,Gulp 的生态也很完善,插件质量很高。目前最为流行
Gulp
Gulp 是基于 流 的自动化构建系统。
- Gulp 的特点:
- 任务化
所有的构建操作,在 gulp 中都称之为任务 - 基于流
gulp 中所有的文件操作,都是基于 流 方式进行 ( Gulp有一个自己的内存,通过指定 API 将源文件流到内存中,完成相应的操作后再通过相应的 API 流出去)
Gulp 使用
- 使用 Gulp 之前,先在全局安装 gulp-cli
命令:npm i -g gulp-cli - 初始化项目
npm init --yes - 安装 Gulp 包
# 安装 gulp 包,作为开发时依赖项
npm i gulp -D
- 创建 gulpfile 文件
gulpfile 文件是项目根目录下的gulpfile.js
,在运行gulp
命令时会被自动加载。在这个文件中,你经常会看到类似src()
、dest()
、series()
或parallel()
函数之类的 Gulp API,除此之外,纯 JavaScript 代码或 Node.js 模块也会被使用。任何导出( exports )的函数都将注册到 Gulp 的任务(task)系统中
报错:The following tasks did not complete: task
Did you forget to signal async completion?
解释:在最新的 Gulp 中,取消了同步代码模式。约定每个任务都必须是一个异步任务
解决:再函数参数中,设定回调函数(回调函数是异步操作)
在 gulpfile.js 中注册 Gulp 任务
运行 Gulp 任务
# 运行 foo 任务
# 如需运行多个任务(task),可以执行 gulp
gulp foo
- 创建默认任务
# 默认任务的名称是 default
exports.default = cb => {
console.log('default task is running')
cb()
}
# 运行默认任务, gulp 后无需指定任务名称
gulp # 效果等同于 gulp default
组合任务
- 按顺序执行,请使用
series()
方法 - 并行执行,可以使用
parallel()
方法将它们组合起来
const gulp = require('gulp')
# 串行方式执行任务,先执行task1, 然后执行task2, 然后执行task3
exports.foo = gulp.series(task1, task2, task3)
# 并行方式执行任务,同时执行task1,task2,task3
exports.bar = gulp.parallel(task1, task2, task3)
# 执行命令
gulp foo # 串行执行
gulp bar # 并行执行
文件操作
gulp 暴露了 src()
和 dest()
方法用于处理计算机上存放的文件。在代码构建过程中,需要将源文件,写入到目标目录。
# 通过 解构 的方式引入 gulp 中的函数
const { src, dest } = require('gulp')
exports.default = () => {
// 文件操作
// 将 src/styles 目录下的 main.css 文件,复制到 dist/styles 目录下
return src('src/styles/main.less', { base: 'src' }).pipe(dest('dist'))
}
# 执行命令
gulp default
# 或直接执行
gulp
样式文件构建
- 对样式文件进行转换、压缩、重命名。
- 添加 CSS 属性前缀的操作,之前是通过程序员手动添加的。这类重复性操作,我们可以通过插件完成。
- 在 Gulp 中 gulp-autoprefixer 插件,可以根据 caniuse.com 上提供的 CSS 兼容性数据,自动地给 CSS 属性加前缀,以保证 CSS 代码的兼容性问题。
脚本文件构建
- 对 JS 代码进行 Babel 转换和压缩
页面模板构建
- 对 html 文件的构建,主要指压缩 html 文件。其中 gulp-htmlmin 插件可以完成压缩任务
- gulp-htmlmin 插件的解析器是:https://github.com/kangax/html-minifier
- 我们可以将 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 插件可以完成图片的压缩任务
报错处理:
gulp-imagemin: Couldn't load default plugin "gifsicle"
gulp-imagemin: Couldn't load default plugin "optipng"
原因:npm 安装依赖失败
解决:
1. 配置 hosts
2. 重新安装 npm i gulp-imagemin -D
文件清除
- 有时候,我们需要删除一些构建的历史文件,然后再重新构建。删除文件操作,可以通过 del 插件完成
开发服务器
- 通过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 [email protected] jquery -S
- 引入文件
Bootstrap 和 jQuery 下载后,文件位于当前目录的 node_modules 下
# 在 HTML 中引入
......
- 引入路径,需要在 browser-sync 中,通过 routes 参数映射后,才能正确引入
- 使用 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 目录下。只要代码发生变化,我们就可以在浏览器上实时看到效果。