详解浏览器缓存和webpack缓存配置


Posted in Javascript onJuly 06, 2018

浏览器缓存

浏览器缓存分为两种类型:

  • 强缓存:也称为本地缓存,不向服务器发送请求,直接使用客户端本地缓存数据
  • 协商缓存:也称304缓存,向服务器发送请求,由服务器判断请求文件是否发生改变。如果未发生改变,则返回304状态码,通知客户端直接使用本地缓存;如果发生改变,则直接返回请求文件。

浏览器缓存机制的过程如下:

详解浏览器缓存和webpack缓存配置

强缓存(本地缓存)

强缓存是最彻底的缓存,无需向服务器发送请求,通常用于css、js、图片等静态资源。浏览器发送请求后会先判断本地是否有缓存。如果无缓存,则直接向服务器发送请求;如果有缓存,则判断缓存是否命中强缓存,如果命中则直接使用本地缓存,如果没命中则向服务器发送请求。判断是否命中本地缓存的方法有两种: Expires Cache-Control

Expires

Expires是http1.0的响应头,代表的含义是资源本地缓存的过期时间,由服务器设定。服务器返回给浏览器的响应头中如果包含Expires字段,浏览器发送请求时拿当前时间和Expires字段值进行比较,判断资源缓存是否失效。如下图所示:

详解浏览器缓存和webpack缓存配置 

Date 代表请求资源的时间, Expires 代表资源缓存的过期时间,可以看到服务器设置资源的缓存时间为5分钟。2017-02-10 07:53:19之前,请求这个资源就是命中本地缓存。超过这个时间再去请求则不命中。

Cache-Control

Cache-Control是http1.0中新增的字段。由于Expires设置的是资源的具体过期时间,如果服务器时间和客户端时间不一样,就会造成缓存错乱,比如认为调节了客户端的时间,所以设置资源有效期的时长更合理。http1.1添加了Cache-Control的max-age字段。max-age代表的含义是资源有效期的时长,是一个相对时长,单位为s。

详解浏览器缓存和webpack缓存配置 

Cache-Control: max-age = 300设置资源的过期时间为5分钟。浏览器再次发送请求时,会把第一次请求的时间和max-age字段值相加和当前时间比较,以此判断是否命中本地缓存。max-age使用的都是客户端时间,比Expires更可靠。如果max-age和Expires同时出现,max-age的优先级更高。Cache-Control提供了更多的字段来控制缓存:

  • no-store,不判断强缓存和协商缓存,服务器直接返回完整资源
  • no-cache,不判断强缓存,每次都需要向浏览器发送请求,进行协商缓存判断
  • public,指示响应可被任何缓存区缓存
  • private,通常只为单个用户缓存,不允许任何共享缓存对其进行缓存,通常用于用户个人信息

协商缓存

协商缓存的判断在服务器端进行,判断是否命中的依据就是这次请求和上次请求之间资源是否发生改变。未发生改变命中,发生改变则未命中。判断文件是否发生改变的方法有两个: Last-ModifiedIf-Modified-SinceEtagIf-None-Match

Last-Modified、If-Modified-Since

Last-Modified是http1.0中的响应头字段,代表请求的资源最后一次的改变时间。If-Modified-Since是http1.0的请求头,If-Modified-Since的值是上次请求服务器返回的Last-Modified的值。浏览器第一次请求资源时,服务器返回Last-Modified,浏览器缓存该值。浏览器第二次请求资源时,用于缓存的Last-Modified赋值给If-Modified-Since,发送给服务器。服务器判断If-Modified-Since和服务器本地的Last-Modified是否相等。如果相等,说明资源未发生改变,命中协商缓存;如果不相等,说明资源发生改变,未命中协商缓存。

详解浏览器缓存和webpack缓存配置 

可以看到该请求返回的是304状态码,说明资源的Last-Modified未改变,所以这次请求的Last-Modified和If-Modified-Since是一致的。

Etag、If-None-Match

Last-Modified、If-Modified-Since使用的都是服务器提供的时间,所以相对来说还是很可靠的。但是由于修改时间的精确级别或者定期生成文件这种情况,会造成一定的错误。所以http1.1添加Etag、If-None-Match字段,完善协商缓存的判断。Etag是根据资源文件内容生成的资源唯一标识符,一旦资源内容发生改变,Etag就会发生改变。基于内容的标识符比基于修改时间的更可靠。If-None-Match的值是上次请求服务器返回的Etag的值。Etag、If-None-Match的判断过程和Last-Modified、If-Modified-Since一致,Etag、If-None-Match的优先级更高。

工程中遇到的问题

强缓存的优势很明显,无需向服务器发送请求,节省了大量的时间和带宽。但是有一个问题,缓存有效期内想更新资源怎么办?我在工程中还遇到另外一个问题,一个项目有四个环境,测试环境、开发环境、在线确认环境、在线环境,四个环境的域名相同,这样就会造成四个环境的缓存共用问题。比如先访问了测试环境,index.js被换成到浏览器中,再切换到在线环境,在线环境会请求index.js,此时浏览器就会使用本地缓存中测试环境的index.js,造成代码错乱。

如何使强缓存失效,是问题的关键。通常的解决方法是更新文件名,文件名不一样的话,浏览器就会重新请求资源。我们要保证新发布版本和不同环境中的文件名是不一样的。其中一种方法在文件名后加版本号:

index.js?version=1
index.css?version=1

webpack提供了很简单的方法可以配置缓存。

// webpack.config.js
module.exports = {
 entry: "main.js",
 output: {
 path: "/build",
 filename: "main.[hash].js"
 }
};

通过hash占位符,在每次生成打包文件时,都会通过文件内容生成唯一的hash,并添加到输出的文件名中。如果有多个入口文件,可以使用name占位符设置输出:

// webpack.config.js
module.exports = {
 entry: {
  main:"main.js",
  sub:"sub.js"
 },
 output: {
 path: "/dist",
 filename: "[name].[hash].js"
 }
};

这时候有一个问题是,此时的hash是根据两个文件的内容来生成的,两个文件名使用的hash是一致的。如果main.js和sub.js只有一个改变,两个文件名都会改变,两个文件都会重新请求,造成资源浪费。webpack提供了chunkhash来代替hash在多入口情况下使用。chunkhash是根据每个入口文件单独生成的哈希值,避免上述情况。

webpack打包动态生成文件名,我们需要动态地把文件引用插入到html启动文件中。 html-webpack-plugin 可以帮我很好地解决这个问题。 html-webpack-plugin 可以动态地生成一个html文件,并在html文件中动态插入webpack打包生成的资源文件。

var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
 entry: 'main.js',
 output: {
 path: '/dist',
 publicPath: '/dist',
 filename: 'main.[hash].js'
 },
 plugins: [new HtmlWebpackPlugin()]
};

默认在 webpackConfig.output.path 路径下生成 index.html ,生成的html文件如下:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8">
 <title>Webpack App</title>
 </head>
 <body>
 <script src="main.2a6c1fee4b5b0d2c9285.js"></script>
 </body>
</html>

通常html启动文件都有自定义的内容,所以 html-webpack-plugin 提供了模板功能,template字段设置模板的路径, html-webpack-plugin 以template为模板,动态添加webpack打包生成的资源路径。

var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
 entry: 'main.js',
 output: {
 path: '/dist',
 publicPath: '/dist',
 filename: 'main.[hash].js'
 },
 plugins: [new HtmlWebpackPlugin(
  {
   template:'index.html'
  }
 )]
};

原index.html内容(\index.html):

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <title>stat-front</title>
 <link rel="stylesheet" href="//at.alicdn.com/t/font_ejl5slgdvtg74x6r.css" rel="external nofollow" rel="external nofollow" >
 </head>
 <body>
 <div id="app" class="app-root">
  <router-view></router-view>
 </div>
 <!-- built files will be auto injected -->
 </body>
</html>

生成的index.html内容(\dist\index.html):

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <title>stat-front</title>
 <link rel="stylesheet" href="//at.alicdn.com/t/font_ejl5slgdvtg74x6r.css" rel="external nofollow" rel="external nofollow" >
 </head>
 <body>
 <div id="app" class="app-root">
  <router-view></router-view>
 </div>
 <!-- built files will be auto injected -->
 <script src="main.2a6c1fee4b5b0d2c9285.js"></script>
 </body>
</html>

最开始的时候静态的index.html在根目录下, webpack-dev-server 设置的启动路径就是根目录下的index.html,如果要启动生成的index.html,还需要设置 webpackConfig.output.publicPath :

var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
 entry: 'main.js',
 output: {
 path: '/dist',
 publicPath: '/',
 filename: 'main.[hash].js'
 },
 plugins: [new HtmlWebpackPlugin(
  {
   template:'index.html'
  }
 )]
};

这样webpack-dev-server在内存中生成的资源都存放在根目录下,生成的index.html会代替原index.html启动。

总结

以上所述是小编给大家介绍的浏览器缓存和webpack缓存配置,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
5款Javascript颜色选择器
Oct 25 Javascript
JQuery中的ready函数冲突的解决方法
May 17 Javascript
JQuery EasyUI 对话框的使用方法
Oct 24 Javascript
jQuery EasyUI API 中文文档 - Panel面板
Sep 30 Javascript
基于jquery的跟随屏幕滚动代码
Jul 24 Javascript
js简单实现让文本框内容逐个字的显示出来
Oct 22 Javascript
jQuery制作简洁的多级联动Select下拉框
Dec 23 Javascript
javascript实现checkbox全选的代码
Apr 30 Javascript
JS Input里添加小图标的两种方法
Nov 11 Javascript
vue2.0 资源文件assets和static的区别详解
Apr 08 Javascript
vue日历/日程提醒/html5本地缓存功能
Sep 02 Javascript
JS 数组和对象的深拷贝操作示例
Jun 06 Javascript
JS获取子节点、父节点和兄弟节点的方法实例总结
Jul 06 #Javascript
深入浅析AngularJs模版与v-bind
Jul 06 #Javascript
vue中子组件调用兄弟组件方法
Jul 06 #Javascript
vue中element 上传功能的实现思路
Jul 06 #Javascript
vue 组件中添加样式不生效的解决方法
Jul 06 #Javascript
vue实现动态添加数据滚动条自动滚动到底部的示例代码
Jul 06 #Javascript
微信小程序利用canvas 绘制幸运大转盘功能
Jul 06 #Javascript
You might like
PHP读取汉字的点阵数据
2015/06/22 PHP
Javascript 调试利器 Firebug使用详解六
2009/07/05 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
jquery高效反选具体实现
2013/05/05 Javascript
js获取IP和PcName(IE)在vs中可用
2013/08/02 Javascript
javascript调试过程中找不到哪里出错的可能原因
2013/12/16 Javascript
javascript 获取元素样式必杀技
2014/05/04 Javascript
js中匿名函数的创建与调用方法分析
2014/12/19 Javascript
jQuery自定义添加&quot;$&quot;与解决&quot;$&quot;冲突的方法
2015/01/19 Javascript
JavaScript控制两个列表框listbox左右交换数据的方法
2015/03/18 Javascript
jquery分割字符串的方法
2015/06/24 Javascript
JS实现鼠标框选效果完整实例
2016/06/20 Javascript
jQuery密码强度验证控件使用详解
2017/01/05 Javascript
vue2.0的虚拟DOM渲染思路分析
2018/08/09 Javascript
小程序封装路由文件和路由方法(5种全解析)
2019/05/26 Javascript
vue 实现超长文本截取,悬浮框提示
2020/07/29 Javascript
vue 解决无法对未定义的值,空值或基元值设置反应属性报错问题
2020/07/31 Javascript
[01:14]2019完美世界城市挑战赛(秋季赛)全国总决赛精彩花絮
2020/01/08 DOTA
python 文件和路径操作函数小结
2009/11/23 Python
python中实现定制类的特殊方法总结
2014/09/28 Python
wxPython绘图模块wxPyPlot实现数据可视化
2019/11/19 Python
使用css3实现超炫的loading加载动画效果
2014/05/07 HTML / CSS
css3的transform中scale缩放详解
2014/12/08 HTML / CSS
Mytheresa中国官网:德国时尚奢侈品商城
2017/08/04 全球购物
专门经营化妆刷的美国彩妆品牌:Sigma Beauty
2017/09/11 全球购物
新农村建设典型材料
2014/05/31 职场文书
运动会拉拉队口号
2014/06/09 职场文书
大学生毕业求职信
2014/06/12 职场文书
喝酒驾驶检讨书
2014/10/01 职场文书
2014年采购部工作总结
2014/11/20 职场文书
怀孕辞职信怎么写
2015/02/28 职场文书
婚庆司仪开场白
2015/05/29 职场文书
python异常中else的实例用法
2021/06/15 Python
MySQL的安装与配置详细教程
2021/06/26 MySQL
Spring Boot 排除某个类加载注入IOC的操作
2021/08/02 Java/Android
python微信智能AI机器人实现多种支付方式
2022/04/12 Python