官網(wǎng):Gulp
代碼塊中的省略號晃财,代表相較于上次代碼未改動部分
github完整項目: pages-boilerplate
準(zhǔn)備內(nèi)容:
- 首先初始化項目,目錄如下圖
- 安裝 gulp蔓姚,作為開發(fā)時依賴項
npm install --save-dev gulp
-
根目錄下新增gulpfile.js文件,此文件中構(gòu)建任務(wù)
構(gòu)建任務(wù):
-
樣式文件編譯
首先安裝gulp-sass到開發(fā)依賴
//gulpfile.js文件
const { src, dest } = require("gulp");
const sass = require("gulp-sass")(require("sass"));
const style = () => {
return src("src/assets/styles/*.scss", { base: "src" })
.pipe(sass())
.pipe(dest("dist"));
};
module.exports = {
style,
};
根目錄命令行執(zhí)行yarn gulp style
驗證
-
腳本編譯
首先安裝gulp-babel到開發(fā)依賴
const { src, dest } = require("gulp");
const babel = require("gulp-babel");
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(
babel({
presets: ["@babel/env"],
})
)
.pipe(dest("dist"));
};
module.exports = {
script,
};
-
頁面模版編譯
首先安裝gulp-swig到開發(fā)依賴
const { src, dest } = require("gulp");
const swig = require("gulp-swig");
//假數(shù)據(jù)
const data = {
menus: [
{
name: "Home",
icon: "aperture",
link: "index.html",
},
{
name: "Features",
link: "features.html",
},
{
name: "About",
link: "about.html",
},
],
pkg: require("./package.json"),
date: new Date(),
};
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
swig({
data,
})
)
.pipe(dest("dist"));
};
module.exports = {
page,
};
根目錄命令行執(zhí)行yarn gulp compile
驗證
-
圖片和字體文件轉(zhuǎn)換
首先安裝gulp-imagemin到開發(fā)依賴
const imgage = () => {
return src("src/assets/images/**", { base: "src" })
.pipe(imagemin())
.pipe(dest("dist"));
};
const font = () => {
return src("src/assets/fonts/**", { base: "src" })
.pipe(imagemin())
.pipe(dest("dist"));
};
因為以上任務(wù)都是可以異步進行的,所有通過gulp提供的parallel方法轧拄,將以上任務(wù)組合起來
// gulpfile.js文件
const { src, dest, parallel } = require("gulp");
const sass = require("gulp-sass")(require("sass"));
const babel = require("gulp-babel");
const swig = require("gulp-swig");
const imagemin = require("gulp-imagemin");
const data = {
menus: [
{
name: "Home",
icon: "aperture",
link: "index.html",
},
{
name: "Features",
link: "features.html",
},
{
name: "About",
link: "about.html",
},
],
pkg: require("./package.json"),
date: new Date(),
};
const style = () => {
return src("src/assets/styles/*.scss", { base: "src" })
.pipe(sass())
.pipe(dest("dist"));
};
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(
babel({
presets: ["@babel/env"],
})
)
.pipe(dest("dist"));
};
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
swig({
data,
})
)
.pipe(dest("dist"));
};
const imgage = () => {
return src("src/assets/images/**", { base: "src" })
.pipe(imagemin())
.pipe(dest("dist"));
};
const font = () => {
return src("src/assets/fonts/**", { base: "src" })
.pipe(imagemin())
.pipe(dest("dist"));
};
const compile = parallel(style, script, page, imgage, font);
module.exports = {
compile,
};
以上,src目錄下文件處理完畢讽膏;接下來處理public
-
public處理及自動刪除dist目錄
首先安裝del到開發(fā)依賴
// gulpfile.js文件
const { src, dest, parallel, series } = require("gulp");
const sass = require("gulp-sass")(require("sass"));
const babel = require("gulp-babel");
const swig = require("gulp-swig");
const imagemin = require("gulp-imagemin");
const del = require("del");
...
const extra = () => {
return src("public/**", { base: "public" }).pipe(dest("dist"));
};
const compile = parallel(style, script, page, image, font);
//因為要在編譯之前檩电,把dist目錄清除,所有用series再次組合
const build = series(clean, parallel(compile, extra));
module.exports = {
compile,
build,
clean,
};
-
自動加載插件
首先安裝gulp-load-plugins到開發(fā)依賴府树,然后只需要把所有g(shù)ulp-開頭的插件引用更改為plugins.+插件不包括gulp-的內(nèi)容
例:(gulp-babel改為plugins.babel)
// gulpfile.js
const { src, dest, parallel, series } = require("gulp");
const sass = require("gulp-sass")(require("sass"));
// 刪除即可
// const plugins.babel = require("gulp-babel");
// const plugins.swig = require("gulp-swig");
// const plugins.imagemin = require("gulp-imagemin");
const del = require("del");
const plugins = require("gulp-load-plugins")();
...
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(
plugins.babel({
presets: ["@babel/env"],
})
)
.pipe(dest("dist"));
};
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
plugins.swig({
data,
})
)
.pipe(dest("dist"));
};
const image = () => {
return src("src/assets/images/**", { base: "src" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
const font = () => {
return src("src/assets/fonts/**", { base: "src" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
...
-
開發(fā)服務(wù)器
首先安裝browser-sync到開發(fā)依賴
這里只記錄簡單用法是嗜,具體可參考官網(wǎng)^
// 實現(xiàn)這個項目的構(gòu)建任務(wù)
const { src, dest, parallel, series } = require("gulp");
...
const browserSync = require("browser-sync").create();
...
const serve = () => {
browserSync.init({
files: "dist/**", //files指定文件,監(jiān)聽到變化就自動刷新(注:此配置只會監(jiān)聽dist目錄挺尾,而src不會鹅搪,因為src修改后需要重新編譯,下面會處理)
server: "dist",
});
};
...
module.exports = {
serve,
};
-
監(jiān)聽文件變化以及構(gòu)建優(yōu)化
注意:可能因為swig模版引擎的緩存機制導(dǎo)致頁面不會變化遭铺,此時需要配置swig中的cache為false
- 圖片和字體等文件在開發(fā)階段沒必要構(gòu)建丽柿,因為這些文件可能只是做了壓縮,并不影響頁面上的呈現(xiàn)效果魂挂,所以為了減小開發(fā)階段的開銷甫题,這些文件只在發(fā)布上線之前構(gòu)建
- 所以對于圖片和public中的文件,在此直接請求源文件(非dist)
//gulpfile.js
const { src, dest, parallel, series, watch } = require("gulp");
...
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
plugins.swig({
data,
defaults: { cache: false }, //配置成false涂召,防止緩存機制導(dǎo)致頁面不會變化
})
)
.pipe(dest("dist"));
};
...
const serve = () => {
watch("src/assets/styles/*.scss", script);
watch("src/assets/scripts/*.js", script);
watch("src/*.html", page);
// watch("src/assets/images/**", image);
// watch("src/assets/fonts/**", font);
// watch("public/**", extra);
// 想要監(jiān)聽public或者imgags變化坠非,可以利用browserSync提供的reload方法
// 該 reload 方法會通知所有的瀏覽器相關(guān)文件被改動,要么導(dǎo)致瀏覽器刷新果正,要么注入文件炎码,實時更新改動盟迟。
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
browserSync.reload()
);
browserSync.init({
files: "dist/**", //files指定文件,監(jiān)聽到變化就自動刷新(注:此配置只會監(jiān)聽dist目錄潦闲,而src不會攒菠,因為src修改后需要重新編譯,下面會處理)
server: {
baseDir: ["dist", "src", "public"], //多個基目錄歉闰,在dist目錄下找不到就去src找辖众,否則就去public找,以此類推
},
});
};
...
// 構(gòu)建任務(wù)優(yōu)化
const compile = parallel(style, script, page);
// 上線之前執(zhí)行的任務(wù)
const build = series(clean, parallel(compile, extra, image, font));
// 開發(fā)階段執(zhí)行的任務(wù)
const develop = series(compile, serve);
module.exports = {
build,
clean,
serve,
develop,
};
-
useref文件引用及文件壓縮
針對html文件中和敬,會有一些node_modules中的引用文件凹炸,在開發(fā)階段,我們可以通過Browsersync模塊中server的routes做一個映射來解決
// index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
...
<!-- build:css assets/styles/vendor.css -->
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<!-- endbuild -->
...
// gulpfile.js文件
...
const serve = () => {
...
browserSync.init({
files: "dist/**", //files指定文件昼弟,監(jiān)聽到變化就自動刷新(注:此配置只會監(jiān)聽dist目錄啤它,而src不會,因為src修改后需要重新編譯私杜,下面會處理)
server: {
baseDir: ["dist", "src", "public"], //多個基目錄蚕键,在dist目錄下找不到就去src找,否則就去public找衰粹,以此類推
routes: {
"/node_modules": "node_modules", //通過映射锣光,獲取node_modules下的引用
},
},
});
};
...
但是線上環(huán)境此方法行不通了,useref插件便可以解決這個問題(注:它只負責(zé)合并铝耻,不負責(zé)壓縮誊爹,配合gulp-if插件可實現(xiàn)壓縮
)
gulp-useref這是一款可以將html引用的多個css和js合并起來,減小依賴的文件個數(shù)瓢捉,從而減少瀏覽器發(fā)起的請求次數(shù)频丘。gulp-useref根據(jù)注釋將html中需要合并壓縮的區(qū)塊找出來,對區(qū)塊內(nèi)的所有文件進行合并
useref插件會自動處理html中的構(gòu)建注釋泡态,構(gòu)建注釋模塊由build:開始搂漠,endbulid結(jié)束,中間內(nèi)容都是引入模塊某弦,build:后會跟標(biāo)記桐汤,說明引入的是js或css,最后再指定一個路徑靶壮,最終注釋模塊內(nèi)的引入都是打包到這一個路徑中
// 構(gòu)建注釋
<!-- build:css assets/styles/vendor.css -->
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<!-- endbuild -->
使用前后對比
-
vs
接下來開始壓縮文件
要壓縮的文件有html css js怔毛,所有分別安裝gulp-clean-css
gulp-htmlmin
gulp-uglify
到開發(fā)依賴,另外我們需要針對不同文件做不同壓縮腾降,所以還需安裝gulp-if
...
const useref = () => {
// 為啥不是src下的文件呢拣度,因為src下的html是模版,沒有意義,必須得是生成后的dist目錄才有意義
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()))
.pipe(
plugins.if(
/\.html$/,
plugins.htmlmin({
collapseWhitespace: true, //壓縮html的空白行
minifyCSS: true, //壓縮html中的css
minifyJS: true, //壓縮html中的js
})
)
)
.pipe(dest("release")) //因為放到dist目錄抗果,可能導(dǎo)致讀寫沖突筋帖,所以臨時寫一個目錄
);
};
...
-
重新規(guī)劃構(gòu)建過程
原本打包上線的目錄應(yīng)該是dist目錄,但是因為上述打包過程防止讀寫沖突窖张,臨時把文件放在了release目錄幕随,這個時候蚁滋,我們需要上線的應(yīng)該是release目錄宿接,而release目錄又沒有圖片和字體文件,所以需要重新調(diào)整辕录;
其實睦霎,在useref之前生成的文件算是一個中間產(chǎn)物,所以應(yīng)該把script走诞,page副女,style這些任務(wù)生成的文件放在一個臨時目錄,然后useref拿到臨時目錄文件蚣旱,轉(zhuǎn)換后再放到最終目錄dist碑幅;(因為image,font塞绿,extra這三個任務(wù)在打包上線之前才會做沟涨,不會影響useref,所以直接放到dist)异吻;
所以修改后代碼如下:
//gulpfile.js文件
...
const clean = () => {
return del(["dist", "temp"]);
};
const style = () => {
return src("src/assets/styles/*.scss", { base: "src" })
.pipe(sass())
.pipe(dest("temp"));
};
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(
plugins.babel({
presets: ["@babel/env"],
})
)
.pipe(dest("temp"));
};
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
plugins.swig({
data,
defaults: { cache: false },
})
)
.pipe(dest("temp"));
};
...
const serve = () => {
watch("src/assets/styles/*.scss", script);
watch("src/assets/scripts/*.js", script);
watch("src/*.html", page);
// 想要監(jiān)聽public或者imgags變化裹赴,可以利用browserSync提供的reload方法
// 該 reload 方法會通知所有的瀏覽器相關(guān)文件被改動,要么導(dǎo)致瀏覽器刷新诀浪,要么注入文件棋返,實時更新改動。
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
browserSync.reload()
);
browserSync.init({
files: "dist/**", //files指定文件雷猪,監(jiān)聽到變化就自動刷新(注:此配置只會監(jiān)聽dist目錄睛竣,而src不會,因為src修改后需要重新編譯求摇,下面會處理)
server: {
baseDir: ["temp", "src", "public"], //多個基目錄射沟,在dist目錄下找不到就去src找,否則就去public找月帝,以此類推
routes: {
"/node_modules": "node_modules", //通過映射躏惋,獲取node_modules下的引用
},
},
});
};
const useref = () => {
// 為啥不是src下的文件呢,因為src下的html是模版嚷辅,沒有意義簿姨,必須得是生成后的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()))
.pipe(
plugins.if(
/\.html$/,
plugins.htmlmin({
collapseWhitespace: true, //壓縮html的空白行
minifyCSS: true, //壓縮html中的css
minifyJS: true, //壓縮html中的js
})
)
)
.pipe(dest("dist")) //因為放到dist目錄,可能導(dǎo)致讀寫沖突,所以臨時寫一個目錄
);
};
const compile = parallel(style, script, page);
// 上線之前執(zhí)行的任務(wù)
// 因為useref依賴compile任務(wù)扁位,所以兩者同步組合准潭,然后和其他任務(wù)異步組合
const build = series(
clean,
parallel(series(compile, useref), extra, image, font)
);
// 開發(fā)階段執(zhí)行的任務(wù)
const develop = series(compile, serve);
module.exports = {
build,
clean,
serve,
compile,
develop,
useref,
};
如下圖:命令行構(gòu)建日志可以驗證任務(wù)配置沒問題
-
完整版gulpfile.js
只暴露必要的任務(wù)
// 實現(xiàn)這個項目的構(gòu)建任務(wù)
const { src, dest, parallel, series, watch } = require("gulp");
const sass = require("gulp-sass")(require("sass"));
const del = require("del");
const plugins = require("gulp-load-plugins")();
const browserSync = require("browser-sync").create();
const data = {
menus: [
{
name: "Home",
icon: "aperture",
link: "index.html",
},
{
name: "Features",
link: "features.html",
},
{
name: "About",
link: "about.html",
},
],
pkg: require("./package.json"),
date: new Date(),
};
const clean = () => {
return del(["dist", "temp"]);
};
const style = () => {
return src("src/assets/styles/*.scss", { base: "src" })
.pipe(sass())
.pipe(dest("temp"));
};
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(
plugins.babel({
presets: ["@babel/env"],
})
)
.pipe(dest("temp"));
};
const page = () => {
return src("src/*.html", { base: "src" })
.pipe(
plugins.swig({
data,
defaults: { cache: false },
})
)
.pipe(dest("temp"));
};
const image = () => {
return src("src/assets/images/**", { base: "src" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
const font = () => {
return src("src/assets/fonts/**", { base: "src" })
.pipe(plugins.imagemin())
.pipe(dest("dist"));
};
const extra = () => {
return src("public/**", { base: "public" }).pipe(dest("dist"));
};
const serve = () => {
watch("src/assets/styles/*.scss", script);
watch("src/assets/scripts/*.js", script);
watch("src/*.html", page);
// 想要監(jiān)聽public或者imgags變化,可以利用browserSync提供的reload方法
// 該 reload 方法會通知所有的瀏覽器相關(guān)文件被改動域仇,要么導(dǎo)致瀏覽器刷新刑然,要么注入文件,實時更新改動暇务。
watch(
["src/assets/images/**", "src/assets/fonts/**", "public/**"],
browserSync.reload()
);
browserSync.init({
files: "dist/**", //files指定文件泼掠,監(jiān)聽到變化就自動刷新(注:此配置只會監(jiān)聽dist目錄,而src不會垦细,因為src修改后需要重新編譯择镇,下面會處理)
server: {
baseDir: ["temp", "src", "public"], //多個基目錄,在dist目錄下找不到就去src找,否則就去public找,以此類推
routes: {
"/node_modules": "node_modules", //通過映射厦画,獲取node_modules下的引用
},
},
});
};
const useref = () => {
// 為啥不是src下的文件呢,因為src下的html是模版吝梅,沒有意義,必須得是生成后的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()))
.pipe(
plugins.if(
/\.html$/,
plugins.htmlmin({
collapseWhitespace: true, //壓縮html的空白行
minifyCSS: true, //壓縮html中的css
minifyJS: true, //壓縮html中的js
})
)
)
.pipe(dest("dist")) //因為放到dist目錄惹骂,可能導(dǎo)致讀寫沖突苏携,所以臨時寫一個目錄
);
};
const compile = parallel(style, script, page);
// 上線之前執(zhí)行的任務(wù)
// 因為useref依賴compile任務(wù),所以兩者同步組合析苫,然后和其他任務(wù)異步組合
const build = series(
clean,
parallel(series(compile, useref), extra, image, font)
);
// 開發(fā)階段執(zhí)行的任務(wù)
const develop = series(compile, serve);
module.exports = {
build,
clean,
develop,
};
也可在package.json中配置腳本兜叨,方便執(zhí)行
//package.json文件
{
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
},
}