前段时间公司需要招聘几个初级前端,面试过程中,问了这么一个问题:“项目打包后的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 中
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脚手架,那么脚手架已经都配置好了。
作者:Lakeiedward链接:https://juejin.cn/post/7311219067199881254