webpack多頁應(yīng)用架構(gòu)系列(十二):利用webpack生成HTML普通網(wǎng)頁&頁面模板

本文首發(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-loaderhtml-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>

我們改動了什么呢哈踱?

  1. 參數(shù)output.filename里,我們添了個變量[chunkhash]梨熙,這個變量的值會隨chunk內(nèi)容的變化而變化开镣,那么,這個chunk文件最終的路徑就會是一個動態(tài)路徑了咽扇。
  2. 我們在頁面上添加了一個<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-seedhttps://github.com/Array-Huang/webpack-seed)墅拭。

附系列文章目錄(同步更新)

本文首發(fā)于Array_Huang的技術(shù)博客——實用至上,非經(jīng)作者同意棍掐,請勿轉(zhuǎn)載藏雏。
原文地址:https://segmentfault.com/a/1190000007126268
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末作煌,一起剝皮案震驚了整個濱河市掘殴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粟誓,老刑警劉巖奏寨,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹰服,居然都是意外死亡病瞳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門获诈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仍源,“玉大人心褐,你說我怎么就攤上這事舔涎。” “怎么了逗爹?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵亡嫌,是天一觀的道長嚎于。 經(jīng)常有香客問我,道長挟冠,這世上最難降的妖魔是什么于购? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮知染,結(jié)果婚禮上肋僧,老公的妹妹穿的比我還像新娘。我一直安慰自己控淡,他們只是感情好嫌吠,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掺炭,像睡著了一般辫诅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涧狮,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天炕矮,我揣著相機(jī)與錄音,去河邊找鬼者冤。 笑死肤视,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涉枫。 我是一名探鬼主播钢颂,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拜银!你這毒婦竟也來了殊鞭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤尼桶,失蹤者是張志新(化名)和其女友劉穎操灿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泵督,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡趾盐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了小腊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片救鲤。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖秩冈,靈堂內(nèi)的尸體忽然破棺而出本缠,到底是詐尸還是另有隱情,我是刑警寧澤入问,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布丹锹,位于F島的核電站稀颁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏楣黍。R本人自食惡果不足惜匾灶,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望租漂。 院中可真熱鬧阶女,春花似錦、人聲如沸哩治。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锚扎。三九已至吞瞪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驾孔,已是汗流浹背芍秆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留翠勉,地道東北人妖啥。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像对碌,于是被迫代替她去往敵國和親荆虱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容