自動化構(gòu)建
什么是構(gòu)建
- 構(gòu)建是將源代碼轉(zhuǎn)換成生產(chǎn)代碼的過程
為什么構(gòu)建
- 一些代碼需要編譯(CSS,JS), 保證瀏覽器的兼容性
將 Less 或 Sass 轉(zhuǎn)換成 CSS
將 ES6+ 的新語法轉(zhuǎn)成 ES5 - 有些代碼需要壓縮(CSS,JS,HTML,圖片等)
壓縮之后的代碼體積更小,加載更快,節(jié)省帶寬 - 有些代碼需要做格式化校驗,統(tǒng)一代碼風(fēng)格
- 構(gòu)建過程:源代碼通過編譯、壓縮、格式化等等轉(zhuǎn)化成生成代碼
什么是自動化構(gòu)建
- 不管是代碼壓縮還是 less 轉(zhuǎn)換,通過手動方式進(jìn)行工作量巨大,自動化構(gòu)建是指將手動構(gòu)建任務(wù),進(jìn)行排列組合,然后通過命令(或工具)自動執(zhí)行的過程
- 實現(xiàn)自動化構(gòu)建最簡單的方式是 npm scripts (npm 腳本)
npm scripts
- npm 在
package.json文件里面,使用scripts字段定義腳本命令
{
"scripts": {
// 命令名稱: 任務(wù)
"foo": "node bar.js"
}
}
# `scripts` 字段是一個對象。它的每一個屬性,對應(yīng)一段腳本。比如,`foo` 命令對應(yīng)的腳本是`node bar.js`。
# 命令行下使用 npm run <命令>,就可以執(zhí)行這段腳本。
$ npm run foo
# 等同于執(zhí)行
$ node bar.js
- npm 腳本就是 Shell 腳本,因為可以使用 Shell 通配符
"style": "lessc *.less"
"style": "lessc **/*.less"
* 表示任意文件名,** 表示任意一層子目錄
- 執(zhí)行順序
如果是并行執(zhí)行(即同時的平行執(zhí)行),可以使用&符號
{
"scripts": {
"parallel": "node task1.js & node task2.js & node task3.js"
}
}
如果是串行執(zhí)行(前一個任務(wù)成功后,才執(zhí)行下一個任務(wù)),可以使用 && 符號
{
"scripts": {
"series": "node task1.js && node task2.js && node task3.js"
}
}
但是,& 符號在 Windows 操作系統(tǒng)下不起作用。此時,我們可以借助插件,在 Windows 下實現(xiàn)并行操作:npm-run-all
構(gòu)建樣式文件
構(gòu)建樣式文件就是將開發(fā)環(huán)境下的 JavaScript 源代碼,轉(zhuǎn)成線上環(huán)境使用的代碼。這里的構(gòu)建任務(wù)可能有多個。
在開發(fā)過程中,經(jīng)常使用 ES6+ 新特性時,一些舊的瀏覽器,不支持 JS 的新語法。所以,在項目上線之前,就需要將新的語法特性解析成兼容性更好的 ES5 。最常用的編譯工具是 Babel
Babel https://babeljs.io/
# 安裝 babel核心,Babel客戶端
npm i -g babel-core babel-cli
# 安裝轉(zhuǎn)碼規(guī)則
npm i -g babel-preset-env
# 在項目根目錄下,新建 .babelrc 文件(注意文件名前有一個點(diǎn)),并添加轉(zhuǎn)換規(guī)則
{
"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",
}
# 執(zhí)行命令(自動編譯)
npm run script
# 如果 Babel 是局部安裝。則babel 的可執(zhí)行路徑是:./node_modules/.bin/babel命令需要做相應(yīng)的調(diào)整
# 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 代碼
# 配置檢測規(guī)則
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"indent": [ "error", 2 ], # 使用兩個空格縮進(jìn)
"quotes": [ "error", "double" ] # 使用雙引號包裹字符串
}
}
- 檢測 JS 代碼風(fēng)格的工具有多個,除了 ESLint, 另一個常用的是 Standard:
https://github.com/standard/standard
StyleLint
StyleLint 是檢測 CSS 代碼格式的插件
官網(wǎng):https://stylelint.io/
- 在項目的根目錄下,創(chuàng)建 .stylelintrc.json 文件,添加如下配置
{
"extends": "stylelint-config-standard",
"rules": {
# 除了使用 stylelint-config-standard,我們還可以在 rules 字段中自定義校驗規(guī)則
"block-no-empty": true # 代碼塊不能為空
}
}
自動化構(gòu)建工具Gulp
自動化構(gòu)建工具,可以幫我們又快又好的完成自動化構(gòu)建任務(wù)。相比有 npm scripts,自動化構(gòu)建工具,功能更為強(qiáng)大。更簡單易學(xué)
- Gulp 的構(gòu)建是基于內(nèi)存實現(xiàn)的,其構(gòu)建速度比 Grunt 快,而且,Gulp 的生態(tài)也很完善,插件質(zhì)量很高。目前最為流行
Gulp
Gulp 是基于 流 的自動化構(gòu)建系統(tǒng)。
- Gulp 的特點(diǎn):
- 任務(wù)化
所有的構(gòu)建操作,在 gulp 中都稱之為任務(wù) - 基于流
gulp 中所有的文件操作,都是基于 流 方式進(jìn)行 ( Gulp有一個自己的內(nèi)存,通過指定 API 將源文件流到內(nèi)存中,完成相應(yīng)的操作后再通過相應(yīng)的 API 流出去)
Gulp 使用
- 使用 Gulp 之前,先在全局安裝 gulp-cli
命令:npm i -g gulp-cli - 初始化項目
npm init --yes - 安裝 Gulp 包
# 安裝 gulp 包,作為開發(fā)時依賴項
npm i gulp -D
- 創(chuàng)建 gulpfile 文件
gulpfile 文件是項目根目錄下的gulpfile.js,在運(yùn)行gulp命令時會被自動加載。在這個文件中,你經(jīng)常會看到類似src()、dest()、series()或parallel()函數(shù)之類的 Gulp API,除此之外,純 JavaScript 代碼或 Node.js 模塊也會被使用。任何導(dǎo)出( exports )的函數(shù)都將注冊到 Gulp 的任務(wù)(task)系統(tǒng)中
報錯:The following tasks did not complete: task
Did you forget to signal async completion?
解釋:在最新的 Gulp 中,取消了同步代碼模式。約定每個任務(wù)都必須是一個異步任務(wù)
解決:再函數(shù)參數(shù)中,設(shè)定回調(diào)函數(shù)(回調(diào)函數(shù)是異步操作)
在 gulpfile.js 中注冊 Gulp 任務(wù)
運(yùn)行 Gulp 任務(wù)
# 運(yùn)行 foo 任務(wù)
# 如需運(yùn)行多個任務(wù)(task),可以執(zhí)行 gulp <task> <othertask>
gulp foo
- 創(chuàng)建默認(rèn)任務(wù)
# 默認(rèn)任務(wù)的名稱是 default
exports.default = cb => {
console.log('default task is running')
cb()
}
# 運(yùn)行默認(rèn)任務(wù), gulp 后無需指定任務(wù)名稱
gulp # 效果等同于 gulp default
組合任務(wù)
- 按順序執(zhí)行,請使用
series()方法 - 并行執(zhí)行,可以使用
parallel()方法將它們組合起來
const gulp = require('gulp')
# 串行方式執(zhí)行任務(wù),先執(zhí)行task1, 然后執(zhí)行task2, 然后執(zhí)行task3
exports.foo = gulp.series(task1, task2, task3)
# 并行方式執(zhí)行任務(wù),同時執(zhí)行task1,task2,task3
exports.bar = gulp.parallel(task1, task2, task3)
# 執(zhí)行命令
gulp foo # 串行執(zhí)行
gulp bar # 并行執(zhí)行
文件操作
gulp 暴露了 src() 和 dest() 方法用于處理計算機(jī)上存放的文件。在代碼構(gòu)建過程中,需要將源文件,寫入到目標(biāo)目錄。
# 通過 解構(gòu) 的方式引入 gulp 中的函數(shù)
const { src, dest } = require('gulp')
exports.default = () => {
// 文件操作
// 將 src/styles 目錄下的 main.css 文件,復(fù)制到 dist/styles 目錄下
return src('src/styles/main.less', { base: 'src' }).pipe(dest('dist'))
}
# 執(zhí)行命令
gulp default
# 或直接執(zhí)行
gulp
樣式文件構(gòu)建
- 對樣式文件進(jìn)行轉(zhuǎn)換、壓縮、重命名。
- 添加 CSS 屬性前綴的操作,之前是通過程序員手動添加的。這類重復(fù)性操作,我們可以通過插件完成。
- 在 Gulp 中 gulp-autoprefixer 插件,可以根據(jù) caniuse.com 上提供的 CSS 兼容性數(shù)據(jù),自動地給 CSS 屬性加前綴,以保證 CSS 代碼的兼容性問題。
腳本文件構(gòu)建
- 對 JS 代碼進(jìn)行 Babel 轉(zhuǎn)換和壓縮
頁面模板構(gòu)建
- 對 html 文件的構(gòu)建,主要指壓縮 html 文件。其中 gulp-htmlmin 插件可以完成壓縮任務(wù)
- gulp-htmlmin 插件的解析器是:https://github.com/kangax/html-minifier
- 我們可以將 style,script 和 html 任務(wù)組合起來。因為 style,script 和 html 之間沒有明確的前后順序,所以,可以進(jìn)行并行執(zhí)行,并行執(zhí)行可以提升構(gòu)建效率
# 引入 parallel 函數(shù)
const { src, dest, parallel } = require('gulp')
// 任務(wù)的并行執(zhí)行
const build = parallel(style, script, html)
module.exports = {
build
}
# 運(yùn)行命令
gulp build
圖片(字體)文件轉(zhuǎn)換
- 對圖片文件的構(gòu)建,主要是指圖片的壓縮。通過 gulp-imagemin 插件可以完成圖片的壓縮任務(wù)
報錯處理:
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
文件清除
- 有時候,我們需要刪除一些構(gòu)建的歷史文件,然后再重新構(gòu)建。刪除文件操作,可以通過 del 插件完成
開發(fā)服務(wù)器
- 通過web服務(wù)器插件,將 dist 下的代碼,發(fā)布到瀏覽器查看效果。發(fā)布web服務(wù)的插件有很多。推薦功能強(qiáng)大的 browser-sync
# 安裝 browser-sync 插件
npm i browser-sync -D
# 在 gulpfile.js 中添加開發(fā)服務(wù)器的內(nèi)容
const browserSync = require('browser-sync')
const bs = browserSync.create()
// 聲明 web 服務(wù)構(gòu)建任務(wù)
const serve = () => {
bs.init({
server: {
baseDir: './dist' // 指定服務(wù)啟動的目錄
routes: {
'/node_modules': 'node_modules' // 引入 Bootstrap 是設(shè)置路徑映射
}
}
})
}
module.exports = {
clean,
build,
serve
}
# 運(yùn)行命令,然后在瀏覽器查看效果
gulp serve
- 服務(wù)發(fā)布成功后,之前學(xué)習(xí)的內(nèi)容,也可以拿過來使用,例如:Bootstrap
- 下載插件
# 因為 Bootstrap 和 jQuery 上線之后還要使用,所以,采用 -S 參數(shù)(上線依賴)
npm i bootstrap@3.4.1 jquery -S
- 引入文件
Bootstrap 和 jQuery 下載后,文件位于當(dāng)前目錄的 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 參數(shù)映射后,才能正確引入
- 使用 Bootstrap
監(jiān)視變化(熱更新)
監(jiān)視 src 下文件變化的頁面更新,代碼一旦更新,瀏覽器上的頁面效果也隨之更新。
此時,我們需要監(jiān)視兩個目錄的變化,一個是 dist 目錄,一個是 src 目錄。
-
監(jiān)視 dist 目錄下代碼的變化
# 通過 browser-sync 中的 files 字段 files: 'dist/**' -
監(jiān)視 src 目錄下代碼的變化
# 通過 gulp 中的 watch 函數(shù) watch(被監(jiān)視的文件,對應(yīng)的任務(wù))
最終的代碼如下:
# 在 gulpfile.js 中添加監(jiān)視文件變化的代碼
const serve = () => {
// watch(被監(jiān)視的文件,對應(yīng)的任務(wù))
watch('src/index.html', html)
watch('src/styles/*.less', style)
watch('src/js/*.js', script)
watch('src/images/**', image)
// 初始化服務(wù)
bs.init({
notify: false, // 禁用瀏覽器右上角的 browserSync connected 提示框
files: 'dist/**', // 監(jiān)視 dist 下 文件的變化,然后在瀏覽器上實時更新
server: {
baseDir: './dist', // 指定服務(wù)啟動的目錄
routes: {
'/node_modules': 'node_modules'
}
}
})
}
// 組合任務(wù)
const build = parallel(style, script, html, image)
const dev = series(clean, build, serve)
// 導(dǎo)出任務(wù)
module.exports = {
build,
dev,
serve
}
# 運(yùn)行命令,更新代碼文件,查看頁面變化
gulp dev
此時,不管是 dist 目錄下,還是 src 目錄下。只要代碼發(fā)生變化,我們就可以在瀏覽器上實時看到效果。