<font id="zqva1"></font>
<rt id="zqva1"></rt>
  • <tt id="zqva1"></tt>
    <cite id="zqva1"></cite>

    <cite id="zqva1"><noscript id="zqva1"></noscript></cite>
      <rp id="zqva1"><meter id="zqva1"></meter></rp>

        <cite id="zqva1"></cite>
          <b id="zqva1"></b>
          <rp id="zqva1"></rp>
          <cite id="zqva1"></cite>

          <rt id="zqva1"></rt>

        1. <rp id="zqva1"></rp>

          關于Webpack中hash的用法

          時間:?2017-12-01閱讀:?1995標簽:?Hash

          在webpack的配置項中,可能會見到hash這樣的字符。

          當存在hash配置的時候,webpack的輸出將可以得到形如這樣的文件:

          page1_bundle_54e8c56e.js
          

          這種帶哈希值的文件名,可以幫助實現靜態資源的長期緩存,在生產環境中非常有用。關于這一點的詳細內容,可以參考這篇久遠的大公司里怎樣開發和部署前端代碼


          在webpack中配置hash

          下面是一個帶hash輸出的webpack配置的例子(webpack v3.0.0):

          var env = {
              src: path.resolve(__dirname, './src'),
              output: path.resolve(__dirname, './dist'),
              publicPath: '/'
          };
          
          module.exports = {
              entry: {
                  'page1': './page1',
                  'page2': './page2'
              },
              context: env.src,
              output: {
                  path: env.output,
                  filename: './[name]/bundle_[chunkhash:8].js',
                  publicPath: env.publicPath
              },
              devtool: false,
              module: {
                  rules: [{
                      test: /\.(png|jpg)$/,
                      use: 'url-loader?limit=8192&name=[path][name]_[hash:8].[ext]'
                  }, {
                      test: /\.css$/,
                      use: ExtractTextPlugin.extract({
                          fallback: 'style-loader',
                          use: 'css-loader'
                      })
                  }]
              },
              plugins: [
                  new ExtractTextPlugin({
                      filename: './[name]/style_[contenthash:8].css'
                  })
              ]
          };
          

          可以看到,有多個地方都出現了hash這個詞,但形式不太一樣。

          output的情況

          output的filename可以指定hash。有兩個值可以選擇:

          • [hash]。hash值是特定于整個構建過程的。
          • [chunkhash]。hash值是特定于每一個文件的內容的。

          我們理想的緩存設計是,在一次版本更新(重新構建)后,只有當一個文件的內容確實發生了變化,它才需要被重新下載,否則應使用緩存。

          因此,以上兩個值中更推薦的是[chunkhash]。你也可以閱讀這篇官方的緩存指南了解更多細節。

          file-loader的情況

          url-loader和file-loader是同一家,參照file-loader文檔可知,文件名name可以使用標識符[hash]來啟用hash。此外,你還可以按照[<hashType>:hash:<digestType>:<length>]的格式更詳細地定制hash結果。

          [hash:8]中的:8則和前面output的一樣,指定了hash結果的截取長度。

          extract-text-webpack-plugin的情況

          被引用的css通過extract-text-webpack-plugin來得到帶hash的文件。參照extract-text-webpack-plugin文檔,在指定生成文件的文件名filename時可以使用標識符[contenthash](可以看到,和之前的并不相同)。

          引用帶hash的文件

          當靜態資源的文件名變成這樣的帶哈希值的版本后,引用這些靜態資源就需要稍多花一點工夫。


          純前端的情況

          如果沒有任何服務端,只是純html、css、js的前端應用的話,一般使用html-webpack-plugin。

          例如,新建一個index.ejs模板文件如下:

          <!DOCTYPE html>
          <html>
          
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <meta http-equiv="X-UA-Compatible" content="ie=edge">
              <title>App Example</title>
          </head>
          
          <body>
              <main id="root"></main>
          </body>
          
          </html>
          

          然后增加html-webpack-plugin到webpack:

          {
            plugins: [
              new HtmlWebpackPlugin({
                template: 'index.ejs'
              })
            ]
          }
          

          執行一次webpack構建,得到生成的index.html:

          <!DOCTYPE html>
          <html>
          
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <meta http-equiv="X-UA-Compatible" content="ie=edge">
              <title>App Example</title>
              <link href="/page1/style_626f7c3f.css" rel="stylesheet">
          </head>
          
          <body>
              <main id="root"></main>
              <script type="text/javascript" src="/page1/bundle_0f33bdc8.js"></script>
          </body>
          
          </html>
          

          可以看到,html-webpack-plugin在模板文件內容的基礎上,就添加好了需要引用的bundle js。如果還有生成的css文件(通過extract-text-webpack-plugin),也會被添加到適當的位置。


          純前端、多頁的情況

          如果webpack有多個entry文件,例如本文最前面給出的例子:

          {
              entry: {
                  'page1': './page1',
                  'page2': './page2'
              }
          }
          

          在這種情況下,html-webpack-plugin會把全部entry的輸出都集中到一個.html里。所以,這可能并不是我們想要的。

          我們更希望的是為每一個entry生成一個.html。這時候,可以使用的是multipage-webpack-plugin。這個插件實際也依賴了html-webpack-plugin。

          例如,有這樣的目錄結構:

          .
          ├─ package.json
          ├─ src
          │   ├─ page1
          │   │   ├─ index.css
          │   │   ├─ index.ejs
          │   │   ├─ index.js
          │   │   └─ potofu.jpg
          │   └─ page2
          │       ├─ index.css
          │       ├─ index.ejs
          │       └─ index.js
          └─ webpack.config.js
          

          然后在webpack配置文件中加入multipage-webpack-plugin:

          {
            plugins: [
              new MultipageWebpackPlugin({
                  htmlTemplatePath: '[name]/index.ejs',   // 源模板文件的位置
                  bootstrapFilename: 'manifest.js',
                  templatePath: '[name]'  // 輸出html文件的路徑
              }),
            ]
          }
          

          [name]標識符對應的是每一個entry的名稱(注意,在本文的時間點,需要使用multipage-webpack-plugin的master分支,也就是最新版,才支持此標識符)。在這個例子中,只有兩個取值:page1,page2。

          bootstrapFilename如字面意義,是指保存webpack的bootstrap代碼的文件命名。而webpack的bootstrap代碼被這樣單獨放到一個文件里,是因為multipage-webpack-plugin在內部(強行)為你啟用了CommonsChunkPlugin。

          執行一次webpack構建,得到的輸出結果:

          dist
          ├─ manifest.js
          ├─ page1
          │   ├─ bundle_29862ad6.js
          │   ├─ index.html
          │   ├─ potofu_26766d43.jpg
          │   └─ style_0b5ab6ef.css
          ├─ page2
          │   ├─ bundle_6a9c6f12.js
          │   ├─ index.html
          │   └─ style_914dffd0.css
          └─ shared
              └─ bundle_9fa1a762.js
          

          取其中一個page1/index.html,內容是:

          <!DOCTYPE html>
          <html>
          
          <head>
              <meta charset="UTF-8">
              <title>page1</title>
              <link href="/page1/style_0b5ab6ef.css" rel="stylesheet">
          </head>
          
          <body>
              <div class="page-box">page1</div>
              <script type="text/javascript" src="/manifest.js"></script>
              <script type="text/javascript" src="/shared/bundle_9fa1a762.js"></script>
              <script type="text/javascript" src="/page1/bundle_29862ad6.js"></script>
          </body>
          
          </html>
          

          可以看到,關聯的css、js靜態資源,都已被正確添加。


          帶服務端的情況

          如果是帶服務端的應用,引用帶hash的資源文件將是另一個思路。

          常見的做法是,為所有的靜態資源生成一個.json清單文件,然后在服務端讀取這個.json,然后把清單信息提供給模板文件,由此來正確地引用所需的靜態資源

          插件webpack-manifest-pluginassets-webpack-plugin都可以幫助完成這一點。

          服務端例子 - Spring Boot & Thymeleaf

          請看一個Spring Boot(1.5.3.RELEASE) & Thymeleaf(2.1)的例子。這里選擇webpack-manifest-plugin。

          首先,在webpack的配置中加入這個插件:

          {
            plugins: [
               new ManifestPlugin()
            ]
          }
          

          執行webpack構建,即生成一個資源清單文件manifest.json(位置取決于webpack的output配置,這里是src/main/resources/static),它的內容是這樣:

          {
            "account/login.css": "account/login_style_f549ea0a.css",
            "account/login.js": "account/login_bundle_279af402.js"
          }
          

          接下來,創建一個幫助類ResourceFormatter(名稱自擬):

          public class ResourceFormatter{
          
              private JsonNode resourceMap;
          
              public ResourceFormatter(){
                  ObjectMapper mapper = new ObjectMapper();
                  Resource resource = new ClassPathResource("static/manifest.json");
          
                  try {
                      resourceMap = mapper.readValue(resource.getFile(), JsonNode.class);
                  } catch (IOException e) {
                      resourceMap = null;
                  }
              }
          
              public String format(String originPath){
          
                  if(resourceMap != null && resourceMap.has(originPath)){
                      return "/" + resourceMap.get(originPath).asText();
                  }
          
                  return "/" + originPath;
              }
          }
          

          這個幫助類在初始化的時候就會讀取manifest.json,而在format()方法里則會利用清單信息對路徑進行轉換。

          然后,把這個幫助類添加到模板引擎Thymeleaf內,包含兩步。

          第一步,創建一個Dialect類:

          public class ResourceDialect extends AbstractDialect implements IExpressionEnhancingDialect {
          
              public ResourceDialect() {
                  super();
              }
          
              @Override
              public String getPrefix() {
                  return "resource";
              }
          
              @Override
              public Map<String, Object> getAdditionalExpressionObjects(IProcessingContext processingContext) {
                  Map<String, Object> expressions = new HashMap<>();
                  expressions.put("resourceFormatter", new ResourceFormatter());
                  return expressions;
              }
          }
          

          可以看到ResourceFormatter在這里被實例化并添加。

          第二步,在Spring應用中注冊這個Dialect類:

          @Configuration
          public class ThymeleafConfig {
              @Bean
              public ResourceDialect resourceDialect() {
                  return new ResourceDialect();
              }
          }
          

          到此,就可以在Thymeleaf視圖模板文件中使用了。修改視圖文件如下(只包含修改的部分):

          <link rel="stylesheet" th:href="@{${#resourceFormatter.format('account/login.css')}}" th:unless="${@environment.acceptsProfiles('dev')}" />
          <!-- ... -->
          <script th:src="@{${#resourceFormatter.format('account/login.js')}}"></script>
          

          最后,啟動服務,訪問該頁,可以看到最終的輸出信息:

          <link rel="stylesheet" href="/account/login_style_f549ea0a.css">
          <!-- ... -->
          <script src="/account/login_bundle_279af402.js"></script>
          

          這就是我們要的帶hash的文件了。

          此外,關于如何在Spring Boot中引入webpack,可以參考這個spring-boot-angular2-seed


          服務端例子 - Koa

          看完了一個傳統Java應用的例子,再來看看現代的Node應用。Koa(v2)是簡潔的Node服務端框架,在它的基礎上引用帶hash的資源文件,也是同樣的思路。

          首先,同樣是在webpack配置中加入webpack-manifest-plugin。

          運行webpack構建生成manifest.json,內容大概會像這樣:

          {
            "page1.css": "page1/style_0b5ab6ef.css",
            "page1.js": "page1/bundle_0f33bdc8.js",
            "page1\\potofu.jpg": "page1/potofu_26766d43.jpg"
          }
          

          然后,讀取這個json,為Koa(通過ctx.state)添加一個資源路徑轉換的幫助方法:

          import manifest from './public/manifest.json';
          
          app.use(async(ctx, next) => {
              ctx.state.resourceFormat = (originPath) => {
          
                  if (originPath in manifest) {
                      return "/" + manifest[originPath];
                  }
          
                  return "/" + originPath;
              };
              await next();
          });
          

          最后,在視圖模板(這里的模板引擎是ejs)內,引用所需的靜態資源:

          <link rel="stylesheet" href="<%= resourceFormat('page1.css') %>">
          <!-- ... -->
          <script src="<%= resourceFormat('page1.js') %>"></script>
          

          到此,Koa的例子就完成了。


          結語

          帶hash的文件是現在web啟用緩存來提升性能比較建議的形式,如果你也有類似的生產環境優化的需要,很推薦你也試試。

          原文地址:http://acgtofe.com 

          站長推薦

          1.阿里云: 本站目前使用的是阿里云主機,安全/可靠/穩定。點擊領取2000元代金券、了解最新阿里云產品的各種優惠活動點擊進入

          2.騰訊云: 提供云服務器、云數據庫、云存儲、視頻與CDN、域名等服務。騰訊云各類產品的最新活動,優惠券領取點擊進入

          3.廣告聯盟: 整理了目前主流的廣告聯盟平臺,如果你有流量,可以作為參考選擇適合你的平臺點擊進入

          鏈接: http://www.modern-decoration.com.cn/article/detial/204

          fly63.com版權所有,內容以共享、參考、研究為目的,不存在任何商業目的。其版權屬原作者所有,如有侵權,請與小編聯系!情況屬實本人將予以刪除!

          文章投稿關于web前端網站點搜索站長推薦網站地圖站長QQ:522607023

          小程序專欄: 土味情話心理測試腦筋急轉彎幽默笑話段子句子語錄成語大全

          国产精品高清视频免费 - 视频 - 在线观看 - 影视资讯 - 唯爱网