引言
前段时间公司需要招聘几个初级前端,面试过程中,问了这么一个问题:"项目打包后的dist文件夹中,比如js、css这些文件的名称为什么是hash值的,就是一串无规则的字符串"。基本上都不知道静态文件为什么需要这种无规则的hash值,今天就稍微说一下哈。
静态文件何时被加载
拿常用的单页面应用举例,当我们访问一个网站的时候,最终会指向 index.html
这个文件,也就是打包后的 dist
文件夹中的 index.html
。
比如说 https://some-domain.com/home,
并点击回车键,我们的服务器中实际上没有这个路由,但我们不是返回 404,而是返回我们的 index.html
。为什么地址中我们没有输入index.html这个路径,但还是指向到 index.html文件并加载它?因为现在大多数都用nginx去部署,一般在url中输入地址的时候末尾都会加个 "/"
,nginx中已经把"/"重定向到 index.html文件了
此时按下回车,这个 index.html
文件就被加载获取到了,然后开始自上而下的去加载里面的引用和代码,比如在html中引入的css、js、图片文件。
浏览器默认缓存
当用户按下回车键就向目标服务器去请求index.html文件,加载解析index.html文件的同时就会连带着加载里面的js、css文件。有没有想过,用户第一次已经从服务器请求下载静态文件到客户端了,第二次去浏览该网站该不会还让我去向服务器请求吧,不会吧不会吧,如果每次都请求下载,那用户的体验多不好,每次请求都需要时间,不说服务器的压力会增加,最重要的是用户的体验感,展现到用户眼前的时间会增加!
所以说浏览器已经想到这个了,当请求静态资源的时候,就会默认缓存请求过来的静态文件,这种浏览器自带的默认缓存就叫做 启发式缓存
。 除非手动设置不需要缓存no-store
,此时请求静态文件的时候文件才不会缓存到本地!
浏览器默认缓存详情可见 MDN 启发式缓存。不管什么缓存都有缓存的时效性吧,如果想知道 启发式缓存
到底把静态文件缓存了多久,可以阅读笔者的这篇文章 浏览器的启发式缓存到底缓存了多久?
vue-cli里的默认配置,css和js的名字都加了哈希值,所以新版本css、js和就旧版本的名字是不同的,不会有缓存问题。
Hash值的作用
那既然知道了浏览器会有默认的缓存,当加载静态资源的时候会命中启发式缓存并缓存到本地。那如果我们重新部署前端包的时候,如何去请求新的静态资源呢,而不是缓存的静态资源?这时候就得用到hash值了。
下面模拟了掘金网站的静态资源获取,当请求静态资源的时候,实际访问的是服务器中静态资源存放的位置
返回即是当前请求静态资源的具体内容
第一次 进来的时候会去请求服务器的资源缓存到本地,比如 0dbcf63.js
这个文件就被缓存到本地了,后面再正常刷新就直接获取的是缓存中的资源了(disk cache 内存缓存
)。
如果前端包重新部署后,试想一下如果 0dbcf63.js
这个js文件不叫这个无规则的名字,而是固定的名字,那浏览器怎么知道去请求缓存中的还是服务器中的,所以浏览器的机制就是请求缓存中的,除非缓存中的过期了,才会去请求服务器中的静态资源。如果没有请求到最新的资源,页面也不会更新到最新的内容,影响用户的体验。
那浏览器这个机制是怎么判断的,那就是根据资源的名称,该资源的资源名称如果没变 并且
没有设置不缓存 并且
资源没过期,那就会请求缓存中的资源,否则就会请求服务器中的资源
当静态资源的名称发生变化的时候,请求路径就会发生变化,所以就会重新命中服务器新部署的静态资源!这就是为什么需要hash值的原因,为了区分之前部署的文件和这次部署文件的区别,好让浏览器去重新获取最新的资源。
第三方库
由于像 lodash 或 react 这样的第三方库很少像本地源代码一样频繁修改,因此通常推荐将第三方库提取到单独的 chunk-vendor 中
js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({
title: 'Caching',
}),
],
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
optimization: {
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
这样依赖的静态文件就会打包到chunk-vendor中,并且多次打包不会改变文件的hash值,以上是webpack原生的配置,如果使用的vue脚手架,那么脚手架已经都配置好了。