引言

静态资源缓存是前端性能优化的一个点,所以在前端开发过程中,一般会最大限度的利用缓存(这里主要是强缓存)。回到本文主题,在使用webpack构建的项目中,稍有不慎的话,即使服务器设置了缓存策略,可能构建的项目无法实现静态资源缓存。那么webpack怎样才能达到使用缓存的效果呢,下面就来谈谈这个问题。

区分一下几种不同的hash

我们都知道,webpack有各种hash值,包括每次项目构建hash,不同入口的chunkhash、文件的内容contenthash,这么多hash,它们有什么区别呢?

hash

hash是跟整个webpack构建项目相关的,每次项目构建hash对应的值都是不同的,即使项目文件没有做“任何修改”

其实是有修改的,因为每次webpack打包编译都会注入webpack的运行时代码,导致整个项目有变化,所以每次hash值都会变化的。

以本人项目代码为例,代码两次构建前后没有做任何修改的对比图

可以看出,前后两次对应项目构建hash改变了。由此推断使用该方式是无法达到缓存的,因为每次hash都会变化。

chunkhash

chunkhash,从字面上就能猜出它是跟webpack打包的chunk相关的。具体来说webpack是根据入口entry配置文件来分析其依赖项并由此来构建该entry的chunk,并生成对应的hash值。不同的chunk会有不同的hash值。一般在项目中把公共的依赖库和程序入口文件隔离并进行单独打包构建,用chunkhash来生成hash值,只要依赖公共库不变,那么其对应的chunkhash就不会变,从而达到缓存的目的。

一般在项目中对webpack的entry使用chunkhash,具体表现在output配置项上:

  1. moudule.exports = {
  2. entry: {
  3. app: './src/main.js',
  4. vendor: ['react', 'redux', 'react-dom', 'react-redux', 'react-router-redux']
  5. },
  6. output: {
  7. path:path.join(__dirname, '/dist/js'),
  8. filename: '[name].[chunkhash].js'
  9. }
  10. ...
  11. }

最后appvendor的chunkhash编译结果如下图

contenthash

contenthash表示由文件内容产生的hash值,内容不同产生的contenthash值也不一样。在项目中,通常做法是把项目中css都抽离出对应的css文件来加以引用。比方在webpack配置这样来用:

  1. module.exports = {
  2. ...
  3. plugins: [
  4. new ExtractTextPlugin({
  5. filename: 'static/[name]_[chunkhash:7].css',
  6. disable: false,
  7. allChunks: true
  8. })
  9. ...
  10. ]

上面配置有一个问题,因为使用了chunkhash,它与依赖它的chunk共用chunkhash。

比方在上面app chunk例子中依赖一个index.css文件,index.css的hash是跟着app的chunkhash走的,只要app文件变更的话,那么即使index.css文件没有变化,它的hash值也是会跟着变化的,导致缓存失效。

那么这时我们可以使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,它的hash值就不会变。

实现js缓存

webpack插件CommonsChunkPlugin的主要作用是抽取webpack项目入口chunk的公共部分,具体的用法就不做过多介绍,不太了解可以参考webpack官网介绍;

该插件是webpack项目常用的一个优化功能,几乎在每个webpack项目中都会用到。使用该插件带来的好处:

  • 提升webpack打包速度和项目体积:将webpack入口的chunk文件中所有公共的代码提取出来,减少代码体积;同时提升webpack打包速度。

  • 利用缓存机制:依赖的公共模块文件一般很少更改或者不会更改,这样独立模块文件提取出可以长期缓存。

但是在项目中,若插件打开方式不正确的话,上面的第二点其实是无法实现,因为这种情况下:

没有被修改过的公有代码或库代码打包出的Entry Chunk,会随着其他业务代码的变化而变化,导致页面上的长缓存机制失效。

那么,下面就来开启CommonsChunkPlugin正确的打开方式。

CommonsChunkPlugin不正确用法

假如将我们项目的公共库如react、react-dom、react-router与业务代码隔离,将其提取为vendor chunk,webpack配置如下:

  1. const webpack = require("webpack");
  2. const path = require('path');
  3. module.exports = {
  4. entry: {
  5. app: "./src/main.js",
  6. vendor: ["react","react-dom", "redux", "react-redux", "react-router-redux"]
  7. },
  8. output: {
  9. path: path.resolve(__dirname, 'output'),
  10. filename: "[name].[chunkhash].js"
  11. },
  12. plugins: [
  13. new webpack.optimize.CommonsChunkPlugin({names: ["vendor"]})
  14. ]
  15. };

上面将项目一些基础库打包成一个名为vendor的chunk中,并将业务相关的代码打包到一个名为app的chunk中;

webpack打包编译后的结果如下:

我们对其中的业务代码app.js进行修改后,重新编译结果如下:

可以发现,在CommonsChunkPlugin这种配置下,当业务代码app发生变化,而库代码也跟着变化,vender的chunkhash也跟着变化,这样vendor的引用的名称跟着变化,导致浏览器端的长缓存机制失效。

引起问题的原因

引起webpack每次打包编译时vendor跟着变化的原因:

webpack每次build的时候都会生成一些运行时代码。当只有一个文件时,运行时代码直接塞到这个文件中。当有多个文件时,运行时代码会被提取到公共文件中,也就是上面CommonsChunkPlugin配置的vendor chunk中。

webpack每次编译时产生的运行时代码,包括全局webpackJsonp方法的定义和维护模块依赖关系,具体可以参考这里>>

所以,上面webpack的CommonsChunkPlugin配置中,每次编译时这些代码都会打包到vendor中,导致每次vendor的chunkhash每次都会变化。

那么,我们可以在对vendor chunk进行配置,抽取其中的公共代码,即webpack运行时代码,这样就可以将项目依赖的基础库模块与业务模块隔离开来,因为不会对这些文件进行修改,所以这些文件可达到长缓存的作用。具体配置如下:

  1. module.exports = {
  2. entry: {
  3. app: "./app.js",
  4. vendor: ["react","react-dom", "redux", "react-redux", "react-router-redux"]
  5. },
  6. ....
  7. plugins: [
  8. new webpack.optimize.CommonsChunkPlugin({names: ["vendor"]}),
  9. new webpack.optimize.CommonsChunkPlugin({
  10. name: 'manifest',
  11. chunks: ['vendor']
  12. })
  13. ]
  14. };

这样,即使修改业务app代码,项目依赖的基础库vendor chunk也不会发生变化;只是抽取的manifest chunk每次还会变化,但是这个文件体积非常小,相比vendor来说这种方式的收益更大。如下图:

修改app代码后的打包编译结果如下,可以看到vendor的chunkhash没有变化

在webpack中配置CommonsChunkPlugin时需要注意几点:

1、 配置webpack的output项时,其filenamechunkFilename必须使用chunkhash。不要使用hash,否则即使按照上面的配置也不能达到预期的效果。至于hash与chunkhash的区别,可参考github的回答

2、对于图片、字体等静态资源抽离使用的file-loader,其配置的hash表示的是静态文件的内容hash值,不是webpack每次打包编译生成的hash值, 切记!!!

3、对于抽取的css样式文件,需要使用contenthash, 与file-loader中的hash意义相同。此处不能为chunkhash,否则其与抽取该样式文件的entry chunk的chunkhash保持一致,打不到缓存的目的。

实现css的缓存

webpack实现css的缓存,就是使用上面介绍过的contenthash,该hash属性值其实是extra-text-webpack-plugin计算的。具体实现css的缓存,其实就像下面一样使用contenthash即可

  1. module.exports = {
  2. ...
  3. plugins: [
  4. new ExtractTextPlugin({
  5. filename: 'static/[name]_[contenthash:7].css',
  6. disable: false,
  7. allChunks: true
  8. })
  9. ...
  10. ]

实现图片/字体的缓存

对于图片、字体等静态资源,在使用webpack构建提取时,其实是使用了file-loader来完成的,生成对应的文件hash值也就是由对应的file-loader来计算的。那么这些静态文件的hash值使用的是什么hash值呢,其实就是hash属性值。如下面代码所示:

  1. module.exports = {
  2. ...
  3. rules: [
  4. ...
  5. {
  6. test: /\.(gif|png|jpe?g)(\?\S*)?$/,
  7. loader: require.resolve('url-loader'),
  8. options: {
  9. limit: 10000,
  10. name: path.posix.join('static', '[name]_[hash:7].[ext]')
  11. }
  12. },
  13. font: {
  14. test: /\.otf|ttf|woff2?|eot(\?\S*)?$/,
  15. loader: require.resolve('url-loader'),
  16. options: {
  17. limit: 10000,
  18. name: path.posix.join('static', '[name]_[hash:7].[ext]')
  19. }
  20. }
  21. ]
  22. }

可以看到上面使用的是hash属性值,此hash非webpack每次项目构建的hash,它是由file-loader根据文件内容计算出来的,不要误认为是webpack构建的hash。

参考

1、webpack之CommonsChunkPlugin正确打开方式

2、webpack 填坑之路--提取独立文件(模块)

3、webpack代码分割技巧

4、听说你用webpack处理文件名的hash?那么建议你看看你生成的hash对不对

5、chunkhash

6、multiple-commons-chunks

7、用 webpack 实现持久化缓存

8、Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案

谈谈CommonsChunkPlugin抽取公共模块的更多相关文章

  1. Atitit 图像处理 公共模块 矩阵扫描器

    Atitit 图像处理 公共模块 矩阵扫描器 1.1. 调用说明对矩阵像素遍历处理调用1 2. 矩阵扫描器主题结构1 2.1. 主要说明 从像素点开始填充矩阵1 2.2. 得到模板中心点所对应的图像坐 ...

  2. Anatomy of a Database System学习笔记 - 公共模块、结语

    公共模块 1. 使用基于上下文的内存分配器进行内存分配 除了教材里常提到的buffer pool,数据库还会为其他任务分配大量内存,例如,Selinger-style查询优化需要动态的规划查询:has ...

  3. Thinkphp5笔记六:公共模块common的使用

    common模块属于公共模块,Thinkphp框架,默认就能调用. 实际用处:任何模块都可能用到的模型.控制.事件提取出来放到公共模块下. 一.公共事件  apps\common\common.php ...

  4. Vue设置导航栏为公共模块并在登录页不显示

    1.公共模块的内容可以放在App.vue中但是通常登录页面是不需要导航的,那么就需要规避登录页这时,就可以采用keep-alive结合$route.meta来实现这个功能.keep-alive 是 V ...

  5. html引入公共模块

    如果没有母版页,那么大量相同布局的页面会有很多相同的代码,那么这就提到了一个概念,叫重用性:可以将相同布局的代码放在一个单独的文件,里面写一些公共模块,那么在其他页面只需要在指定位置引入他们就可以了写 ...

  6. Ionic4.x 中自定义公共模块

    1.创建公共模块以及组件 ionic g module module/slide ionic g component module/slide 2.公共模块 slide.module.ts 中暴露对应 ...

  7. Spring Cloud微服务(一):公共模块的搭建

    本demo后台采用spring cloud微服务,前端选用vue,进行前后端分离搭建.具体项目见git:光头才能强 创建文件夹,并分别创建以下jar工程 创建公共模块(后续有需要,还会增加).无论是d ...

  8. Python_selenium PO模式下 Tesecase 的相同执行代码做成selenium_base_case公共模块及调用

    作用: PO模式下 Tesecase 的相同执行代码做成selenium_base_case公共模块及调用,提高代码简洁度,实现同样效果. 框架结构: 代码简单实践: common模块下 seleni ...

  9. webpack4 公共模块打包,怎么抽取出来一个需要经常修改的包

    项目中有一个需求: 所有页面引用了一个公共ad.js 因为广告需要不断投放新渠道,所以ad.js需要经常上线更新,这样会导致打包出来的 commons.js经常更新,缓存一下无效了.所以急需将ad.j ...

随机推荐

  1. [最短路][部分转]P1027 Car的旅行路线

    题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第I个城市中高速铁路了的单位 ...

  2. TFboy养成记 CNN

    1/先解释下CNN的过程: 首先对一张图片进行卷积,可以有多个卷积核,卷积过后,对每一卷积核对应一个chanel,也就是一张新的图片,图片尺寸可能会变小也可能会不变,然后对这个chanel进行一些po ...

  3. openstack pike 创建vxlan网络

    #openstack pike 创建vxlan网络 openstack pike 集群高可用  安装部署 汇总 http://www.cnblogs.com/elvi/p/7613861.html # ...

  4. 自动化运维工具——ansible详解(二)

    Ansible playbook 简介 playbook 是 ansible 用于配置,部署,和管理被控节点的剧本. 通过 playbook 的详细描述,执行其中的一系列 tasks ,可以让远端主机 ...

  5. 深入讲解HashMap原理

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  6. Jersy、Jetty和Servlet

    1.Jersy框架 Jersey RESTful WebService框架是一个开源的.产品级别的JAVA框架,是JAX-RS的参考实现.Jersey提供自己的API,其API继承自JAX-RS,提供 ...

  7. NYOJ 323 Drainage Ditches 网络流 FF 练手

    Drainage Ditches 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 Every time it rains on Farmer John's fields, ...

  8. python3 中encode 和decode的使用方法。

    编码: 将文本转换成字节流的过程.即Unicode----------->特定格式的编码方式,产生特定的字节流保存在硬盘中(一般为utf-8格式). 解码: 将硬盘中的字节流转换成文本的过程.即 ...

  9. dubbo源码—dubbo自定义spring xml标签

    dubbo为了和spring更好的集成,提供了一些xml配置标签,也就是自定义标签 spring自定义标签 spring自定义标签的方式如下: 设计配置属性和JavaBean 编写xsd文件,校验xm ...

  10. ML—高斯判别分析

    华电北风吹 天津大学认知计算与应用重点实验室 日期:2015/12/11 高斯判别分析属于生成模型,模型终于学习一个特征-类别的联合概率. 0 多维正态分布 确定一个多维正态分布仅仅须要知道分布的均值 ...