webpack多頁應用架構系列(十二):利用webpack生成HTML普通網頁&頁面模板

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-bacjobzd-kg.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang

爲何要用webpack來生成HTML頁面

按照咱們前面的十一篇的內容來看,本身寫一個HTML頁面,而後在上面加載webpack打包的js或其它類型的資源,感受不也用得好好的麼?javascript

是的沒錯,不用webpack用requireJs其實也能夠啊,甚至於,傳統那種人工管理模塊依賴的作法也沒有什麼問題嘛。css

但既然你都已經看到這一篇了,想必早已和我同樣,追求着如下這幾點吧:html

  • 更懶,能自動化的事情毫不作第二遍。
  • 更放心,調通的代碼比人靠譜,更不容易出錯。
  • 代碼潔癖,什麼東西該放哪,一點都不能含糊,混在一塊兒我就要死了。

那麼,廢話很少說,下面就來講說使用webpack生成HTML頁面有哪些好處吧。前端

對多個頁面共有的部分實現複用

在實際項目的開發過程當中,咱們會發現,雖然一個項目裏會有不少個頁面,但這些頁面總有那麼幾個部分是相同或類似的,尤爲是頁頭頁尾,基本上是徹底一致的。那咱們要怎麼處理這些共有的部分呢?java

複製粘貼流

不就是複製粘貼的事嘛?寫好一份完整的HTML頁面,作下個頁面的時候,直接copy一份文件,而後直接在copy的文件上進行修改不就行了嗎?

誰是這麼想這麼作的,放學留下來,我保證不打死你!我曾經接受過這麼一套系統,頂部欄菜單想加點東西,就要每一個頁面都改一遍,可維護性爛到爆啊。webpack

Iframe流

Iframe流常見於管理後臺類項目,可維護性OK,就是缺陷比較多,好比說:git

  • 點擊某個菜單,頁面是加載出來了可是瀏覽器地址欄上的URL沒變,刷新的話又回到首頁了。
  • 搜索引擎收錄完蛋,前臺項目通常不能用Iframe來佈局。
  • 沒有逼格,Low爆了,這是最重要的一點(大誤)。

SPA流

最近這幾年,隨着移動互聯網的興起,SPA也變得很是常見了。不過SPA的侷限性也很是大,好比搜索引擎沒法收錄,但我我的最在乎的,是它太複雜了,尤爲是一些原本業務邏輯就多的系統,很容易懵圈。程序員

後端模板渲染

這倒真是一個辦法,只是,須要後端的配合,利用後端代碼把頁面的各個部分給拼合在一塊兒,因此這方法對前端起家的程序員仍是有點門檻的。github

利用前端模板引擎生成HTML頁面

所謂「用webpack生成HTML頁面」,其實也並非webpack起的核心做用,實際上靠的仍是前端的模板引擎將頁面的各個部分給拼合在一塊兒來達到公共區域的複用。webpack更多的是組織統籌整個生成HTML頁面的過程,並提供更大的控制力。最終,webpack生成的究竟是完整的頁面,仍是供後端渲染的模板,就全看你本身把控了,很是靈活,外人甚至察覺不出來這究竟是你本身寫的仍是代碼統一輩子成的。web

處理資源的動態路徑

若是你想用在文件名上加hash的方法做爲緩存方案的話,那麼用webpack生成HTML頁面就成爲你惟一的選擇了,由於隨着文件的變更,它的hash也會變化,那麼整個文件名都會改變,你總不能在每次編譯後都手動修改加載路徑吧?仍是放心交給webpack吧。

自動加載webpack生成的css、less

若是你使用webpack來生成HTML頁面,那麼,你能夠配置好每一個頁面加載的chunk(webpack打包後生成的js文件),生成出來的頁面會自動用<script>來加載這些chunk,路徑什麼的你都不用管了哈(固然前提是你配置好了output.publicPath)。另外,用extract-text-webpack-plugin打包好的css文件,webpack也會幫你自動添加到<link>裏,至關方便。

完全分離源文件目錄和生成文件目錄

使用webpack生成出來的HTML頁面能夠很安心地跟webpack打包好的其它資源放到一塊兒,相對於另起一個目錄專門存放HTML頁面文件來講,整個文件目錄結構更加合理:

build
  - index
    - index
      - entry.js
      - page.html
    - login
      - entry.js
      - page.html
      - styles.css

如何利用webpack生成HTML頁面

webpack生成HTML頁面主要是經過html-webpack-plugin來實現的,下面來介紹如何實現。

html-webpack-plugin的配置項

每個html-webpack-plugin的對象實例都只針對/生成一個頁面,所以,咱們作多頁應用的話,就要配置多個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, // 爲靜態資源生成hash值
    minify: true,
    xhtml: true,
  });
  configPlugins.push(htmlPlugin);
});

pageArr其實是各個chunk的name,因爲我在output.filename設置的是'[name]/entry.js',所以也起到構建文件目錄結構的效果(具體請看這裏),附上pageArr的定義:

module.exports = [
  'index/login',
  'index/index',
  'alert/index',
  'user/edit-password', 'user/modify-info',
];

html-webpack-plugin的配置項真很多,這裏僅列出多頁應用經常使用到的配置:

  • filename,生成的網頁HTML文件的文件名,注意能夠利用/來控制文件目錄結構的,其最終生成的路徑,是基於webpack配置中的output.path的。
  • template,指定一個基於某種模板引擎語法的模板文件,html-webpack-plugin默認支持ejs格式的模板文件,若是你想使用其它格式的模板文件,那麼須要在webpack配置裏設置好相應的loader,好比handlebars-loaderhtml-loader啊之類的。若是不指定這個參數,html-webpack-plugin會使用一份默認的ejs模板進行渲染。若是你作的是簡單的SPA應用,那麼這個參數不指定也行,但對於多頁應用來講,咱們就依賴模板引擎給咱們拼裝頁面了,因此這個參數很是重要。
  • inject,指示把加載js文件用的<script>插入到哪裏,默認是插到<body>的末端,若是設置爲'head',則把<script>插入到<head>裏。
  • minify,生成壓縮後的HTML代碼。
  • hash,在html-webpack-plugin負責加載的js/css文件的網址末尾加個URL參數,此URL參數的值是表明本次編譯的一個hash值,每次編譯後該hash值都會變化,屬於緩存解決方案。
  • chunks,以數組的形式指定由html-webpack-plugin負責加載的chunk文件(打包後生成的js文件),不指定的話就會加載全部的chunk。

生成一個簡單的頁面

下面提供一份供生成簡單頁面(之因此說簡單,是由於不指定頁面模板,僅用默認模板)的配置:

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,內容以下所示:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>簡單頁面</title>
  </head>
  <body>
    <script src="index_bundle.js"></script>
  </body>
</html>

因爲沒有指定模板文件,所以生成出來的HTML文件僅有最基本的HTML結構,並不帶實質內容。能夠看出,這更適合React這種把HTML藏js裏的方案。

利用模板引擎獲取更大的控制力

接下來,咱們演示如何經過制定模板文件來生成HTML的內容,因爲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>你們仔細瞧好了</p>
  </body>
</html>

'html-webpack-plugin'的配置裏也要指定template參數:

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>你們仔細瞧好了</p>
    <script src="index_bundle.js"></script>
  </body>
</html>

到這裏,咱們已經能夠控制整個HTML文件的內容了,那麼生成後端渲染所需的模板也就不是什麼難事了,以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>你們仔細瞧好了</p>
    <p>這是用smarty生成的內容:<b>{$articleContent}</b></p>
  </body>
</html>

處理資源的動態路徑

接下來在上面例子的基礎上,咱們演示如何處理資源的動態路徑:

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,更適合圖片,能夠將較小的圖片轉成base64,減小http請求
      // 以下配置,將小於8192byte的圖片轉成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>你們仔細瞧好了</p>
    <img src="<%= require('./imgs/login-bg.jpg')  %>" />
  </body>
</html>

咱們改動了什麼呢?

  1. 參數output.filename裏,咱們添了個變量[chunkhash],這個變量的值會隨chunk內容的變化而變化,那麼,這個chunk文件最終的路徑就會是一個動態路徑了。
  2. 咱們在頁面上添加了一個<img>,它的src是require一張圖片,相應地,咱們配置了針對圖片的loader配置,若是圖片比較小,require()就會返回DataUrl,而若是圖片比較大,則會拷貝到dist/static/img/目錄下,並返回新圖片的路徑。

下面來看看,到底html-webpack-plugin能不能處理好這些動態的路徑。

<!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>你們仔細瞧好了</p>
    <img src="data:image/jpeg;base64,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" />
    <script src="index_bundle.c3a064486c8318e5e11a.js"></script>
  </body>
</html>

顯然,html-webpack-plugin成功地將chunk加載了,又處理好了轉化爲DataUrl格式的圖片,這一切,都是咱們手工難以完成的事情。

還未結束

至此,咱們實現了使用webpack生成HTML頁面並嚐到了它所帶來的甜頭,但咱們還沒有實現「對多個頁面共有的部分實現複用」,下一篇《webpack多頁應用架構系列(十三):構建一個簡單的模板佈局系統》咱們就來介紹這部分的內容。

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seedhttps://github.com/Array-Huang/webpack-seed)。

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

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-bacjobzd-kg.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang
相關文章
相關標籤/搜索