一如输、大概思路
一般自動(dòng)化構(gòu)建做的主要是以下幾件事情:
(一)開發(fā)階段的構(gòu)建
- 將scss轉(zhuǎn)成css,并在臨時(shí)目錄下生成對(duì)應(yīng)的css文件(在開發(fā)階段不需要壓縮旭旭,所以將壓縮放在單獨(dú)的步驟里)谎脯。
- 將es6等瀏覽器不支持的新特性用babel轉(zhuǎn)成瀏覽器支持的js語(yǔ)法,并生成文件持寄。
- 根據(jù)提供的html模板(主要做的是數(shù)據(jù)填充)生成對(duì)應(yīng)的html代碼和文件源梭。
- 啟動(dòng)一個(gè)服務(wù)器打開頁(yè)面,并監(jiān)聽上面三步和圖片等文件變化稍味,若有變化則刷新頁(yè)面废麻。
(二)發(fā)布階段的構(gòu)建
- ...前三個(gè)步驟和開發(fā)階段一樣,都是處理html模庐、js烛愧、css。
- 清空構(gòu)建目錄下的所有舊文件掂碱。
- 壓縮字體文件和圖片文件資源到構(gòu)建目錄下屑彻。
- 將public里不需要處理的資源,直接拷貝生成到構(gòu)建目錄下顶吮。
- 將前三步臨時(shí)目錄里的css社牲、js、html文件做壓縮混淆合并處理悴了,并生成對(duì)應(yīng)文件到構(gòu)建目錄下搏恤,將處理后的生成css和js文件插入到html里。
注意前三步的編譯都是在臨時(shí)目錄下湃交,最后壓縮混淆合并才在構(gòu)建目錄下熟空。之所以分兩個(gè)目錄,是因?yàn)樵诘?步對(duì)同個(gè)目錄邊讀邊寫會(huì)產(chǎn)生沖突搞莺。
二息罗、“開發(fā)階段構(gòu)建任務(wù)”具體實(shí)現(xiàn)
(一)準(zhǔn)備工作
- 請(qǐng)確保自己本地已有npm或yarn等包管理工具,本文用yarn做演示才沧。
- 下載該演示項(xiàng)目迈喉,或者自行vue-cli創(chuàng)建一個(gè)項(xiàng)目。
- 在該文件夾下空白處“按shift+鼠標(biāo)右鍵”温圆,選中“在此處打開命令行/powershell窗口”打開命令行窗口挨摸,或者自行通過(guò)命令窗口cd到該文件目錄下。
- 命令行輸入
yarn init
回車岁歉,自行填寫信息一路回車得运,最后生成package.json配置文件。 - 命令行輸入
yarn add gulp --dev
安裝gulp自動(dòng)構(gòu)建工具。 - 在演示項(xiàng)目下的gulpfile.js輸入下面代碼(gulpfile.js是gulp的運(yùn)行文件)熔掺。
const { src, dest } = require("gulp")
const extra = () => {
// src是gulp中讀取文件的方法饱搏,dest是gulp中寫入文件到目標(biāo)位置的方法
// 將public目錄下,以public為根目錄的所有文件拷貝到dist目錄下
return src('public/**', {base: 'public'})
.pipe(dest('dist'))
}
module.exports = {
extra,
}
- 命令行輸入
yarn gulp extra
(extra是gulpfile.js對(duì)應(yīng)的函數(shù)任務(wù)名)置逻,如果能將public下文件拷貝到dist則證明成功推沸。
(二)css處理
- 由于需要依賴到node-sass國(guó)外源可能會(huì)出現(xiàn)安裝失敗的問題,建議先設(shè)置npm的安裝源為國(guó)內(nèi)的淘寶鏡像
npm config set registry http://registry.npm.taobao.org
诽偷。 - 命令行輸入
yarn add gulp-sass sass --dev
安裝gulp處理sass的插件。 - gulpfile.js文件輸入以下代碼疯坤,再執(zhí)行
yarn gulp style
成功生成temp對(duì)應(yīng)css文件則成功报慕。
const { src, dest } = require("gulp")
const sass = require('gulp-sass')
const style = () => {
return src('src/assets/styles/*.scss', {base: 'src'})
.pipe(sass({outputStyle: 'expanded'})) // _開頭的sass文件被認(rèn)為是依賴文件,不會(huì)被轉(zhuǎn)換压怠。expanded可以讓右括號(hào)換行而不是跟在分號(hào)后面
.pipe(dest('temp'))
}
module.exports = {
style,
}
由于后續(xù)需要加載比較多的gulp插件眠冈,需要頻繁寫require,此處采用一個(gè)gulp-load-plugins來(lái)自動(dòng)加載所需插件菌瘫,命令行輸入yarn add gulp-load-plugins --dev
蜗顽,代碼改寫為如下
const { src, dest } = require("gulp")
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const style = () => {
return src('src/assets/styles/*.scss', {base: 'src'})
.pipe(plugins.sass({outputStyle: 'expanded'})) // _開頭的sass文件被認(rèn)為是依賴文件,不會(huì)被轉(zhuǎn)換雨让。expanded可以讓右括號(hào)換行而不是跟在分號(hào)后面
.pipe(dest('temp'))
}
module.exports = {
style,
}
(三)js處理
- 命令行輸入
yarn add gulp-babel @babel/core @babel/preset-env --dev
安裝gulp轉(zhuǎn)碼js的插件雇盖。 - gulpfile.js文件增加以下代碼,再執(zhí)行
yarn gulp script
成功生成temp對(duì)應(yīng)已轉(zhuǎn)碼好的js文件則成功栖忠。
const script = () => {
return src('src/assets/scripts/*.js', {base: 'src'})
.pipe(plugins.babel({presets: ['@babel/preset-env']}))
.pipe(dest('temp'))
}
module.exports = {
...,
script,
}
(四)html處理
- 命令行輸入
yarn add gulp-swig --dev
安裝gulp啟動(dòng)服務(wù)器并監(jiān)聽文件變化的插件崔挖。 - gulpfile.js文件增加以下代碼,再執(zhí)行
yarn gulp page
成功生成temp對(duì)應(yīng)已插入好數(shù)據(jù)的html文件則成功庵寞。
const data = {
menu: [],
pkg: require('./package.json'),
date: new Date(),
}
const page = () => {
return src('src/*.html', {base: 'src'})
.pipe(plugins.swig({data, defaults: {cache: false}})) // 指定頁(yè)面中的插值data狸相,根據(jù)模板生成html。記得忽略緩存捐川,否則可能更新失敗
.pipe(dest('temp'))
.pipe(bs.reload({stream: true}))
}
module.exports = {
...,
page,
}
(五)服務(wù)器上啟動(dòng)頁(yè)面并監(jiān)聽變化
- 命令行輸入
yarn add browser-sync --dev
安裝gulp對(duì)頁(yè)面模板進(jìn)行編譯的插件脓鹃。 - gulpfile.js文件增加以下代碼,再執(zhí)行
yarn gulp serve
古沥,成功在瀏覽器啟動(dòng)頁(yè)面瘸右,并且修改sass/js/html時(shí)會(huì)同步刷新頁(yè)面變化則成功。
const { src, dest, watch } = require("gulp")
const browserSync = require('browser-sync') // 不是gulp插件所以要自己導(dǎo)入
const bs = browserSync.create() // 創(chuàng)建服務(wù)器
const serve = () => {
// 由于bs.init只監(jiān)聽了temp下已經(jīng)編譯好文件的變化岩齿,但我們真正編輯是在src下的未處理文件尊浓,所以要先監(jiān)聽src文件變化觸發(fā)編譯到temp觸發(fā)bs監(jiān)聽
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// 圖片等資源不在temp目錄下,監(jiān)聽到變化了要手動(dòng)調(diào)用bs.reload
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**',
], bs.reload)
bs.init({
notify: false, // 不提示刷新
port: 2000,
open: true, // 是否自動(dòng)打開瀏覽器
files: 'temp/**', // 哪些文件需要監(jiān)聽修改
server: { // 先根據(jù)routes配置去指定目錄找文件纯衍,找不到再根據(jù)baseDir配置去找
routes: { // 遇到/node_modules路徑時(shí)去node_modules目錄下找文件
'/node_modules': 'node_modules',
},
baseDir: ['temp', 'src', 'public'], // 需要尋找對(duì)應(yīng)文件時(shí)栋齿,可以先在temp目錄下找有沒有,沒有再找src再public
},
})
}
module.exports = {
...,
serve,
}
(六)整合為開發(fā)階段構(gòu)建任務(wù)
此處通過(guò)gulp的兩個(gè)方法parallel
和series
來(lái)整合上面四步任務(wù)。parallel
方法可以同時(shí)開始執(zhí)行任務(wù)瓦堵,series
方法是按順序先后執(zhí)行任務(wù)基协。整合代碼如下,命令行yarn gulp develop
即可運(yùn)行該任務(wù)菇用。
const compile = parallel(style, script, page) // 因?yàn)槿齻€(gè)任務(wù)互不影響所以用parallel
const develop = series(compile, serve) // 要先編譯才有temp目錄澜驮,才能監(jiān)聽變化,所以用series
module.exports = {
compile,
develop,
}
三惋鸥、“發(fā)布階段構(gòu)建任務(wù)”具體實(shí)現(xiàn)
(一)清空構(gòu)建目錄
- 命令行輸入
yarn add del --dev
安裝刪除文件的插件杂穷。 - gulpfile.js文件增加以下代碼,再執(zhí)行
yarn gulp clean
卦绣,成功刪除目錄下的指定文件則成功耐量。
const del = require('del')
const clean = () => {
return del(['dist']) // del返回的是一個(gè)promise
}
module.exports = {
clean,
}
(二)壓縮圖片和字體
- 命令行輸入
yarn add gulp-imagemin --dev
安裝壓縮圖片的插件。 - gulpfile.js文件增加以下代碼滤港,再執(zhí)行
yarn gulp image font
廊蜒,生成了對(duì)應(yīng)圖片并比原文件小則成功。
const image = () => {
return src('src/assets/images/**', {base: 'src'}) // **任意文件
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => { // 字體也有svg可以用同樣壓縮
return src('src/assets/fonts/**', {base: 'src'})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
module.exports = {
image,
font,
}
(三)拷貝public資源
這段在開頭準(zhǔn)備工作中其實(shí)已經(jīng)講過(guò)溅漾,直接復(fù)制代碼過(guò)來(lái)山叮,不再贅述。
const extra = () => {
// src是gulp中讀取文件的方法添履,dest是gulp中寫入文件到目標(biāo)位置的方法
// 將public目錄下屁倔,以public為根目錄的所有文件拷貝到dist目錄下
return src('public/**', {base: 'public'})
.pipe(dest('dist'))
}
(四)壓縮處理js/css/html,并將處理后的js/css插入html
- 命令行輸入
yarn add gulp-useref --dev
安裝處理引用關(guān)系的插件暮胧,可以將類似如下的特定注釋內(nèi)的引用文件打包合并到指定文件中汰现。(這段代碼表示把bootstrap.css和test.css兩個(gè)文件打包合并成assets/styles/目錄下一個(gè)vendor.css文件)
<!-- build:css assets/styles/vendor.css -->
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="/node_modules/test/test.css">
<!-- endbuild -->
- 命令行輸入
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
安裝壓縮處理html/js/css的gulp插件,yarn add gulp-if --dev
安裝用于判斷當(dāng)前文件類型并做對(duì)應(yīng)處理的gulp插件叔壤。 - gulpfile.js文件增加以下代碼瞎饲,再執(zhí)行
yarn gulp useref
,生成了對(duì)應(yīng)各壓縮文件并插入到html則成功炼绘。
const useref = () => {
return src('temp/*.html', {base: 'temp'})
.pipe(plugins.useref({searchPath: ['temp', '.']})) // 指定html里寫的引入文件去哪里找
// 找到的html嗅战、js、css文件分別做不同的處理
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true, // 對(duì)html里的css代碼做壓縮
minifyJS: true,
})))
.pipe(dest('dist'))
}
module.exports = {
...,
useref,
}
(五)整合為發(fā)布階段構(gòu)建任務(wù)
由于useref執(zhí)行完一次之后俺亮,temp里的html已經(jīng)被壓縮驮捍,特定注釋被刪除,所以再次運(yùn)行useref任務(wù)無(wú)法達(dá)到想要的效果脚曾,所以必須先編譯compile生成注釋代碼东且,再執(zhí)行useref才有效。整合代碼如下本讥,命令行yarn gulp build
即可運(yùn)行該任務(wù)珊泳。
// 先刪除舊目錄鲁冯,再執(zhí)行構(gòu)建任務(wù)生成對(duì)應(yīng)文件
const build = series(clean, parallel(series(compile, useref), image, font, extra))
module.exports = {
compile,
develop,
build,
}
四、最終gulpfile.js代碼
const { src, dest, watch, parallel, series } = require("gulp")
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const browserSync = require('browser-sync') // 不是gulp插件(gulp-開頭)所以要自己導(dǎo)入
const bs = browserSync.create() // 創(chuàng)建服務(wù)器
const del = require('del')
const data = {
menu: [],
pkg: require('./package.json'),
date: new Date(),
}
const style = () => {
return src('src/assets/styles/*.scss', {base: 'src'})
.pipe(plugins.sass({outputStyle: 'expanded'})) // _開頭的sass文件被認(rèn)為是依賴文件色查,不會(huì)被轉(zhuǎn)換薯演。expanded可以讓右括號(hào)換行而不是跟在分號(hào)后面
.pipe(dest('temp'))
// .pipe(bs.reload({stream: true})) // 如果bs初始化files項(xiàng)沒配置,也可以在watch到之后在任務(wù)里reload
}
const script = () => {
return src('src/assets/scripts/*.js', {base: 'src'})
.pipe(plugins.babel({presets: ['@babel/preset-env']}))
.pipe(dest('temp'))
// .pipe(bs.reload({stream: true}))
}
const page = () => {
return src('src/*.html', {base: 'src'})
.pipe(plugins.swig({data, defaults: {cache: false}})) // 指定頁(yè)面中的插值data秧了,根據(jù)模板生成html跨扮。記得忽略緩存,否則可能更新失敗
.pipe(dest('temp'))
// .pipe(bs.reload({stream: true}))
}
const serve = () => {
// 由于bs.init只監(jiān)聽了temp下已經(jīng)編譯好文件的變化验毡,但我們真正編輯是在src下的未處理文件衡创,所以要先監(jiān)聽src文件變化觸發(fā)編譯到temp觸發(fā)bs監(jiān)聽
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// 圖片等資源不在temp目錄下,監(jiān)聽到變化了要手動(dòng)調(diào)用bs.reload
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**',
], bs.reload)
bs.init({
notify: false, // 不提示刷新
port: 2000,
open: true, // 是否自動(dòng)打開瀏覽器
files: 'temp/**', // 哪些文件需要監(jiān)聽修改
server: { // 先根據(jù)routes配置去指定目錄找文件璃氢,找不到再根據(jù)baseDir配置去找
routes: { // 遇到/node_modules路徑時(shí)去node_modules目錄下找文件
'/node_modules': 'node_modules',
},
baseDir: ['temp', 'src', 'public'], // 需要尋找對(duì)應(yīng)文件時(shí),可以先在temp目錄下找有沒有录择,沒有再找src再public
},
})
}
const clean = () => {
return del(['dist']) // del返回的是一個(gè)promise
}
const image = () => {
return src('src/assets/images/**', {base: 'src'}) // **任意文件
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => { // 字體也有svg可以用同樣壓縮
return src('src/assets/fonts/**', {base: 'src'})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const extra = () => {
// src是gulp中讀取文件的方法拔莱,dest是gulp中寫入文件到目標(biāo)位置的方法
// 將public目錄下菱皆,以public為根目錄的所有文件拷貝到dist目錄下
return src('public/**', {base: 'public'})
.pipe(dest('dist'))
}
const useref = () => {
return src('temp/*.html', {base: 'temp'})
.pipe(plugins.useref({searchPath: ['temp', '.']})) // 指定html里寫的引入文件去哪里找
// 找到的html篷店、js、css文件分別做不同的處理
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true, // 對(duì)html里的css代碼做壓縮
minifyJS: true,
})))
.pipe(dest('dist'))
}
const compile = parallel(style, script, page) // 因?yàn)槿齻€(gè)任務(wù)互不影響所以用parallel
const develop = series(compile, serve) // 要先編譯才有temp目錄臭家,才能監(jiān)聽變化疲陕,所以用series
// 先刪除舊目錄,再執(zhí)行構(gòu)建任務(wù)生成對(duì)應(yīng)文件
const build = series(clean, parallel(series(compile, useref), image, font, extra))
module.exports = {
compile,
develop,
build,
}