構建多頁面應用——hash

這裏的hash主要從兩個方面來說。一個是webpack生成的hash,另一個是頁面鏈接中的hash,如:http://localhost:8080/ywbk.html#restaurant中的#restaurant。後者在單頁面應用的路由處理中經常用到。

webpack中的hash

webapck每次構建都會生成一個新的hash(主要用於生產環境)。

它的作用就是用來標記構建生成的狀態,通常使用的過程中,會將它注入到構建輸出(生成)的文件名中。

webpack.config.js文件中的位置如下:

...
output: {
  path: path.resolve(__dirname, 'dist/'),
  filename: isDev ? 'assets/js/[name].js' : 'assets/js/[name].[contenthash].js',
  publicPath: isDev ? config.devUrl : config.deployUrl
},
module: {
  rules: [
    ...
    {
      include: path.resolve(__dirname, 'assets/imgs/other/'),
      test: /\.(png|jpe?g|gif)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: isDev ? '[name].[ext]' : '[name].[hash].[ext]',
            outputPath: 'assets/imgs/'
          }
        }
      ]
    }
    ...
  ]
}
...
plugins: plugins.concat([
  new MiniCssExtractPlugin({
    filename: isDev ? 'assets/css/[name].css' : 'assets/css/[name].[contenthash].css',
    chunkFilename: isDev ? 'assets/css/[id].css' : 'assets/css/[id].[contenthash].css'
  })
  ...
])

如果項目是第一次構建,那麼所有的靜態資源都會被添加同一個hash,當第二構建如果有內容改變,構建生成的文件會被賦予新的hash。爲了提高見構建的速度,減少構建生成不必要的文件(如果第一次生成的文件和第二次生成的文件內容相同,那麼沒必要重新在生成一次)。爲此webpack提供了三個hash字段可供選擇,分別是hash、chunkhash、contenthash。

  • hash:不管內容改變與否,所有的文件都會被重新生成一遍。這是一種粗放的模式。
  • chunkhash:它主要針對與webpack配置文件的entry中定義的入口文件。
  • contenthash: 它主要針對的是webpack構建的過程中,提取(或者說分離)出來的內容,如:extract-text-webpack-plugin

針對多頁面應用的構建特點,使用contenthash是一個不錯的選擇,本文中所使用的示例wepbakck4.x-multi-page符合這個特點。css的分離,js文件的分離(webpack4的SplitChunk)。

爲什麼要在多頁面應用中使用單頁面應用的hash來實現路由的控制

很明顯的一點是傳統的多頁面應用的業務模塊往往會出現多個頁面之間會有很多相同內容,這樣在單擊導航實現路由切換的時候,總是會看到相同的內容,這樣會給用戶造成一種錯覺‘爲什麼總是同一個頁面’。這樣的用戶體驗往往不好。最突出的就是包含二級導航的頁面。(可參考聖捷集團的官網

單頁面應用給我們了一個很好的啓示,可以通過將這些頁面結構相似的,而只有一部分內容類同的頁面組合成爲一個頁面。

這樣做的好處顯而易見,減少多頁面構建生成的頁面數量,我們之構建生成一級和二級頁面,以及一些頁面結構很少雷同的頁面,而不構建生成三級頁面。

優化後的示例地址

具體的實現有兩種解決方案。

第一種,在每個頁面中使用一個vue(結合vue-router)的示例(也可以使用,react,angular)。

第二種,自己實現對不同hash的處理。

注:如果使用hash,在開發的時候一定要模擬一個服務器環境,直接用瀏覽器打開是無法實現的,瀏覽器控制檯會提示跨域的錯誤。

本文所使用的示例用的是第二種方案。

具體的實現過程如下:

在生成子導航的模擬數據中添加了一個type值,

tabs: [
  {
    cn_name: '聖捷投資',
    en_name: 'SHENGJIE INVESTMENT',
    type: 'investment'
  }, {
    cn_name: '董事長致辭',
    en_name: 'CHAIRMANS SPEECH',
    type: 'speech'
  }
  ...
]

這樣使用pug-loader處理生成的html對應的元素中會包含一個data-type自定義屬性。參考代碼如下:

<div class="tabs">
  <div class="tab-item active" data-type="finance">
    <div>互聯網金融</div>
    <div>ONLINE FINANCE</div>
  </div>
  <div class="tab-item" data-type="allfinance">
    <div>全品類金融</div>
    <div>WHOLE CATEGORY FINANCE</div>
  </div>
  ...
</div>

然後,使用JavaScript通過控制觸發條件,如url的hash改變,進而控制頁面的展示效果。參考代碼如下:

···
$('.tab-item').on('click', function() {
  var type = $(this).data('type')
  window.location.hash = type
  tab(this, type)
})
···

首先,單點擊二級導航時,改變url的hash。這樣做可以讓用戶通過操作瀏覽器的前進和後退按鈕來控制頁面,此外使用瀏覽器的前進和後退按鈕的好處是,瀏覽可以記錄頁面的狀態。(只用上面的代碼無法實現想要的效果)

其次,使用hashchange這個瀏覽器自帶的監聽hash改變的api(他兼容>=ie8的瀏覽器,所以可以放心使用)。

···
$(window).on('hashchange', function() {
  tabcheck()
})
···

通過它們,就可以輕鬆的實現url的hash

爲了頁面呈現更好的效果,可以給頁面添加一個滾動的動畫,如果不使用hash在傳統的頁面中實現有些棘手。

那麼針對頁面底部的網站導航,如何結合hash來操作頁面並實現一致的路由切換效果呢?

這裏需要監聽頁面的load狀態,在webpack中,使用commonjs來組織js代碼塊,需要注意window.load(...)無效的情況。

具體的實現就不一一介紹了,可參考demo中tabs.js文件的代碼

源代碼

webpack4.x multi-page

構建多頁面應用系列文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章