本文首發(fā)于Array_Huang的技術(shù)博客——
實用至上
茵休,非經(jīng)作者同意垂谢,請勿轉(zhuǎn)載臊岸。
原文地址:https://segmentfault.com/a/1190000007126268
如果您對本系列文章感興趣梗脾,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang
為什么要用webpack來生成HTML頁面
按照我們前面的十一篇的內(nèi)容來看尽楔,自己寫一個HTML頁面持钉,然后在上面加載webpack打包的js或其它類型的資源衡招,感覺不也用得好好的么?
是的沒錯每强,不用webpack用requireJs其實也可以啊始腾,甚至于,傳統(tǒng)那種人工管理模塊依賴的做法也沒有什么問題嘛空执。
但既然你都已經(jīng)看到這一篇了浪箭,想必早已和我一樣,追求著以下這幾點吧:
- 更懶辨绊,能自動化的事情絕不做第二遍山林。
- 更放心,調(diào)通的代碼比人靠譜邢羔,更不容易出錯驼抹。
- 代碼潔癖,什么東西該放哪拜鹤,一點都不能含糊框冀,混在一起我就要死了。
那么敏簿,廢話不多說明也,下面就來說說使用webpack生成HTML頁面有哪些好處吧。
對多個頁面共有的部分實現(xiàn)復(fù)用
在實際項目的開發(fā)過程中惯裕,我們會發(fā)現(xiàn)温数,雖然一個項目里會有很多個頁面,但這些頁面總有那么幾個部分是相同或相似的蜻势,尤其是頁頭頁尾撑刺,基本上是完全一致的。那我們要怎么處理這些共有的部分呢握玛?
復(fù)制粘貼流
不就是復(fù)制粘貼的事嘛够傍?寫好一份完整的HTML頁面甫菠,做下個頁面的時候,直接copy一份文件冕屯,然后直接在copy的文件上進(jìn)行修改不就好了嗎寂诱?
誰是這么想這么做的,放學(xué)留下來安聘,我保證不打死你痰洒!我曾經(jīng)接受過這么一套系統(tǒng),頂部欄菜單想加點東西浴韭,就要每個頁面都改一遍带迟,可維護(hù)性爛到爆啊。
Iframe流
Iframe流常見于管理后臺類項目囱桨,可維護(hù)性O(shè)K,就是缺陷比較多嗅绰,比如說:
- 點擊某個菜單舍肠,頁面是加載出來了但是瀏覽器地址欄上的URL沒變,刷新的話又回到首頁了窘面。
- 搜索引擎收錄完蛋翠语,前臺項目一般不能用Iframe來布局。
- 沒有逼格财边,Low爆了肌括,這是最重要的一點(大誤)。
SPA流
最近這幾年酣难,隨著移動互聯(lián)網(wǎng)的興起谍夭,SPA也變得非常常見了。不過SPA的局限性也非常大憨募,比如搜索引擎無法收錄紧索,但我個人最在意的,是它太復(fù)雜了菜谣,尤其是一些本來業(yè)務(wù)邏輯就多的系統(tǒng)珠漂,很容易懵圈。
后端模板渲染
這倒真是一個辦法尾膊,只是媳危,需要后端的配合,利用后端代碼把頁面的各個部分給拼合在一起冈敛,所以這方法對前端起家的程序員還是有點門檻的待笑。
利用前端模板引擎生成HTML頁面
所謂“用webpack生成HTML頁面”,其實也并不是webpack起的核心作用抓谴,實際上靠的還是前端的模板引擎將頁面的各個部分給拼合在一起來達(dá)到公共區(qū)域的復(fù)用滋觉。webpack更多的是組織統(tǒng)籌整個生成HTML頁面的過程签夭,并提供更大的控制力。最終椎侠,webpack生成的到底是完整的頁面第租,還是供后端渲染的模板,就全看你自己把控了我纪,非常靈活慎宾,外人甚至察覺不出來這到底是你自己寫的還是代碼統(tǒng)一生成的凯沪。
處理資源的動態(tài)路徑
如果你想用在文件名上加hash的方法作為緩存方案的話惧所,那么用webpack生成HTML頁面就成為你唯一的選擇了,因為隨著文件的變動埠对,它的hash也會變化术健,那么整個文件名都會改變汹碱,你總不能在每次編譯后都手動修改加載路徑吧?還是放心交給webpack吧荞估。
自動加載webpack生成的css咳促、less
如果你使用webpack來生成HTML頁面,那么勘伺,你可以配置好每個頁面加載的chunk(webpack打包后生成的js文件)跪腹,生成出來的頁面會自動用<script>
來加載這些chunk飞醉,路徑什么的你都不用管了哈(當(dāng)然前提是你配置好了output.publicPath)冲茸。另外,用extract-text-webpack-plugin
打包好的css文件,webpack也會幫你自動添加到<link>
里钦无,相當(dāng)方便。
徹底分離源文件目錄和生成文件目錄
使用webpack生成出來的HTML頁面可以很安心地跟webpack打包好的其它資源放到一起想罕,相對于另起一個目錄專門存放HTML頁面文件來說凄杯,整個文件目錄結(jié)構(gòu)更加合理:
build
- index
- index
- entry.js
- page.html
- login
- entry.js
- page.html
- styles.css
如何利用webpack生成HTML頁面
webpack生成HTML頁面主要是通過html-webpack-plugin
來實現(xiàn)的膊存,下面來介紹如何實現(xiàn)。
html-webpack-plugin
的配置項
每一個html-webpack-plugin的對象實例都只針對/生成一個頁面千康,因此携栋,我們做多頁應(yīng)用的話,就要配置多個html-webpack-plugin的對象實例:
pageArr.forEach((page) => {
const htmlPlugin = new HtmlWebpackPlugin({
filename: `${page}/page.html`,
template: path.resolve(dirVars.pagesDir, `./${page}/html.js`),
chunks: [page, 'commons'],
hash: true, // 為靜態(tài)資源生成hash值
minify: true,
xhtml: true,
});
configPlugins.push(htmlPlugin);
});
pageArr
實際上是各個chunk的name,由于我在output.filename設(shè)置的是'[name]/entry.js'
皆疹,因此也起到構(gòu)建文件目錄結(jié)構(gòu)的效果(具體請看這里)敲茄,附上pageArr
的定義:
module.exports = [
'index/login',
'index/index',
'alert/index',
'user/edit-password', 'user/modify-info',
];
html-webpack-plugin
的配置項真不少仅讽,這里僅列出多頁應(yīng)用常用到的配置:
- filename组哩,生成的網(wǎng)頁HTML文件的文件名伶贰,注意可以利用
/
來控制文件目錄結(jié)構(gòu)的蛛砰,其最終生成的路徑,是基于webpack配置中的output.path的黍衙。 - template泥畅,指定一個基于某種模板引擎語法的模板文件,
html-webpack-plugin
默認(rèn)支持ejs格式的模板文件琅翻,如果你想使用其它格式的模板文件位仁,那么需要在webpack配置里設(shè)置好相應(yīng)的loader,比如handlebars-loader
啊html-loader
啊之類的方椎。如果不指定這個參數(shù)聂抢,html-webpack-plugin
會使用一份默認(rèn)的ejs模板進(jìn)行渲染。如果你做的是簡單的SPA應(yīng)用棠众,那么這個參數(shù)不指定也行琳疏,但對于多頁應(yīng)用來說,我們就依賴模板引擎給我們拼裝頁面了闸拿,所以這個參數(shù)非常重要空盼。 - inject,指示把加載js文件用的
<script>
插入到哪里新荤,默認(rèn)是插到<body>
的末端揽趾,如果設(shè)置為'head',則把<script>
插入到<head>
里苛骨。 - minify篱瞎,生成壓縮后的HTML代碼。
- hash智袭,在由
html-webpack-plugin
負(fù)責(zé)加載的js/css文件的網(wǎng)址末尾加個URL參數(shù)奔缠,此URL參數(shù)的值是代表本次編譯的一個hash值掠抬,每次編譯后該hash值都會變化吼野,屬于緩存解決方案。 - chunks两波,以數(shù)組的形式指定由
html-webpack-plugin
負(fù)責(zé)加載的chunk文件(打包后生成的js文件)瞳步,不指定的話就會加載所有的chunk。
生成一個簡單的頁面
下面提供一份供生成簡單頁面(之所以說簡單腰奋,是因為不指定頁面模板单起,僅用默認(rèn)模板)的配置:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'index.js',
output: {
path: 'dist',
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin(
title: '簡單頁面',
filename: 'index.html',
)],
};
使用這份配置編譯后,會在dist目錄下生成一個index.html劣坊,內(nèi)容如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>簡單頁面</title>
</head>
<body>
<script src="index_bundle.js"></script>
</body>
</html>
由于沒有指定模板文件嘀倒,因此生成出來的HTML文件僅有最基本的HTML結(jié)構(gòu),并不帶實質(zhì)內(nèi)容〔饽ⅲ可以看出灌危,這更適合React這種把HTML藏js里的方案。
利用模板引擎獲取更大的控制力
接下來碳胳,我們演示如何通過制定模板文件來生成HTML的內(nèi)容勇蝙,由于html-webpack-plugin
原生支持ejs模板,因此這里也以ejs作為演示對象:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>這是一個用<b>html-webpack-plugin</b>生成的HTML頁面</h1>
<p>大家仔細(xì)瞧好了</p>
</body>
</html>
'html-webpack-plugin'的配置里也要指定template參數(shù):
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'index.js',
output: {
path: 'dist',
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin(
title: '按照ejs模板生成出來的頁面',
filename: 'index.html',
template: 'index.ejs',
)],
};
那么挨约,最后生成出來的HTML文件會是這樣的:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<title>按照ejs模板生成出來的頁面</title>
</head>
<body>
<h1>這是一個用<b>html-webpack-plugin</b>生成的HTML頁面</h1>
<p>大家仔細(xì)瞧好了</p>
<script src="index_bundle.js"></script>
</body>
</html>
到這里味混,我們已經(jīng)可以控制整個HTML文件的內(nèi)容了,那么生成后端渲染所需的模板也就不是什么難事了诫惭,以PHP的模板引擎smarty為例:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>這是一個用<b>html-webpack-plugin</b>生成的HTML頁面</h1>
<p>大家仔細(xì)瞧好了</p>
<p>這是用smarty生成的內(nèi)容:<b>{$articleContent}</b></p>
</body>
</html>
處理資源的動態(tài)路徑
接下來在上面例子的基礎(chǔ)上翁锡,我們演示如何處理資源的動態(tài)路徑:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'index.js',
output: {
path: 'dist',
filename: 'index_bundle.[chunkhash].js'
},
plugins: [new HtmlWebpackPlugin(
title: '按照ejs模板生成出來的頁面',
filename: 'index.html',
template: 'index.ejs',
)],
module: {
loaders: {
// 圖片加載器,雷同file-loader贝攒,更適合圖片盗誊,可以將較小的圖片轉(zhuǎn)成base64,減少http請求
// 如下配置隘弊,將小于8192byte的圖片轉(zhuǎn)成base64碼
test: /\.(png|jpg|gif)$/,
loader: 'url?limit=8192&name=./static/img/[hash].[ext]',
},
},
};
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>這是一個用<b>html-webpack-plugin</b>生成的HTML頁面</h1>
<p>大家仔細(xì)瞧好了</p>
<img src="<%= require('./imgs/login-bg.jpg') %>" />
</body>
</html>
我們改動了什么呢哈踱?
- 參數(shù)
output.filename
里,我們添了個變量[chunkhash]梨熙,這個變量的值會隨chunk內(nèi)容的變化而變化开镣,那么,這個chunk文件最終的路徑就會是一個動態(tài)路徑了咽扇。 - 我們在頁面上添加了一個
<img>
邪财,它的src是require一張圖片,相應(yīng)地质欲,我們配置了針對圖片的loader配置树埠,如果圖片比較小,require()
就會返回DataUrl嘶伟,而如果圖片比較大怎憋,則會拷貝到dist/static/img/
目錄下,并返回新圖片的路徑九昧。
下面來看看绊袋,到底html-webpack-plugin
能不能處理好這些動態(tài)的路徑。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<title>按照ejs模板生成出來的頁面</title>
</head>
<body>
<h1>這是一個用<b>html-webpack-plugin</b>生成的HTML頁面</h1>
<p>大家仔細(xì)瞧好了</p>
![](http://upload-images.jianshu.io/upload_images/2970956-47399d0cc987233e.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
<script src="index_bundle.c3a064486c8318e5e11a.js"></script>
</body>
</html>
顯然铸鹰,html-webpack-plugin
成功地將chunk加載了癌别,又處理好了轉(zhuǎn)化為DataUrl格式的圖片,這一切蹋笼,都是我們手工難以完成的事情展姐。
還未結(jié)束
至此躁垛,我們實現(xiàn)了使用webpack生成HTML頁面并嘗到了它所帶來的甜頭,但我們尚未實現(xiàn)“對多個頁面共有的部分實現(xiàn)復(fù)用”圾笨,下一節(jié)《webpack多頁應(yīng)用架構(gòu)系列(十三):構(gòu)建一個簡單的模板布局系統(tǒng)》我們就來介紹這部分的內(nèi)容缤苫。
示例代碼
諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed
)墅拭。
附系列文章目錄(同步更新)
- webpack多頁應(yīng)用架構(gòu)系列(一):一步一步解決架構(gòu)痛點:
https://segmentfault.com/a/1190000006843916
- webpack多頁應(yīng)用架構(gòu)系列(二):webpack配置常用部分有哪些活玲?:
https://segmentfault.com/a/1190000006863968
- webpack多頁應(yīng)用架構(gòu)系列(三):怎么打包公共代碼才能避免重復(fù)?:
https://segmentfault.com/a/1190000006871991
- webpack多頁應(yīng)用架構(gòu)系列(四):老式j(luò)Query插件還不能丟谍婉,怎么兼容舒憾?:
https://segmentfault.com/a/1190000006887523
- webpack多頁應(yīng)用架構(gòu)系列(五):聽說webpack連less/css也能打包?:
https://segmentfault.com/a/1190000006897458
- webpack多頁應(yīng)用架構(gòu)系列(六):聽說webpack連圖片和字體也能打包穗熬?:
https://segmentfault.com/a/1190000006907701
- webpack多頁應(yīng)用架構(gòu)系列(七):開發(fā)環(huán)境镀迂、生產(chǎn)環(huán)境傻傻分不清楚?:
https://segmentfault.com/a/1190000006952432
- webpack多頁應(yīng)用架構(gòu)系列(八):教練我要寫ES6唤蔗!webpack怎么整合Babel探遵?:
https://segmentfault.com/a/1190000006992218
- webpack多頁應(yīng)用架構(gòu)系列(九):總有刁民想害朕!ESLint為你阻擊垃圾代碼:
https://segmentfault.com/a/1190000007030775
- webpack多頁應(yīng)用架構(gòu)系列(十):如何打造一個自定義的bootstrap:
https://segmentfault.com/a/1190000007043716
- webpack多頁應(yīng)用架構(gòu)系列(十一):預(yù)打包Dll妓柜,實現(xiàn)webpack音速編譯:
https://segmentfault.com/a/1190000007104372
- webpack多頁應(yīng)用架構(gòu)系列(十二):利用webpack生成HTML普通網(wǎng)頁&頁面模板:
https://segmentfault.com/a/1190000007126268
- webpack多頁應(yīng)用架構(gòu)系列(十三):構(gòu)建一個簡單的模板布局系統(tǒng):
https://segmentfault.com/a/1190000007159115
- webpack多頁應(yīng)用架構(gòu)系列(十四):No復(fù)制粘貼箱季!多項目共用基礎(chǔ)設(shè)施
- webpack多頁應(yīng)用架構(gòu)系列(十五):論前端如何在后端渲染開發(fā)模式下夾縫生存
本文首發(fā)于Array_Huang的技術(shù)博客——
實用至上
,非經(jīng)作者同意棍掐,請勿轉(zhuǎn)載藏雏。
原文地址:https://segmentfault.com/a/1190000007126268
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang