随着前端技术的不断发展和演进,越来越多的 Web 应用选择使用 SPA(Single Page Application,单页应用)架构进行开发。SPA 应用通常由静态文件打包而成,包括 HTML、CSS、JavaScript 等资源文件。为了提高用户体验,我们需要尽可能快速地加载这些资源,其中缓存技术是一个非常重要的方案。本文将介绍 SPA 应用中如何实现打包后的文件快速缓存。
一、缓存策略
在讲解具体实现之前,我们需要先了解一下缓存策略。
1.强缓存
浏览器在第一次请求资源时,服务器返回一个 Response,其中包括 Cache-Control 和 Expires 两个头部。浏览器会根据这两个头部判断是否可以直接从缓存读取。
- Cache-Control:指定此资源的缓存策略,常见取值有 max-age(最大缓存时间)、no-cache(需要协商缓存)等。
- Expires:指定此资源过期的时间。该值为一个日期时间戳,表示该时间之前缓存的 Response 可以直接使用。该头部需要服务端提供正确的日期时间。
如果这两个头部都没有被设置,浏览器认为该资源可以被强缓存。
2.协商缓存
当强缓存失效时,浏览器需要向服务器发送请求,询问资源是否更新。此时浏览器会发送 If-Modified-Since 和 If-None-Match 两个头部,询问服务器该资源是否有更新。
- If-Modified-Since:上次的修改时间,服务器返回的新 Response 中的 Last-Modified 值会与此头部进行比较,如果没有变化,则返回 304 Not Modified,表示资源未做任何修改,可以使用上一次的缓存。
- If-None-Match:服务器返回的新 Response 中的 ETag 值与此头部进行比较,值相同则返回 304 Not Modified。
在实际开发中,我们一般会结合强缓存和协商缓存来进行缓存策略的设置。这里我们大致介绍一下缓存策略,后面会有更详细的示例。
二、打包后的文件快速缓存
SPA 应用中的静态资源通常是由 webpack 等构建工具进行打包的,这些静态资源文件通常都被放到了一起,如何对这些文件进行缓存呢?考虑到这些文件都是在 webpack 构建完成后生成的,我们可以通过 webpack 插件来处理缓存问题。
1. 使用 contenthash 进行文件名的生成
webpack 在生成打包文件时,会将所有的代码和资源打包成一个或多个文件,并为每个文件生成一个唯一的 hash 值,当文件内容发生变化时,hash 值也会随之改变。
我们可以使用这个 hash 值,将文件名设置为包含 hash 值的字符串,如下所示:
<script src="main.5c8e7d61.js"></script>
对于静态文件,如图片、样式文件等也可以使用同样的方式进行处理。
这种方式可以保证文件内容发生变化时,文件名也发生变化,因此浏览器不会读取到旧的缓存文件,而是重新获取新文件。
2. 基于文件名的缓存策略
我们可以通过设置 HTTP 头部,来控制浏览器的缓存行为。webpack 提供了一个插件 webpack-plugin-manifest
,可以将生成的文件信息保存到一个文件中,例如 manifest.json
,并指定该文件在 HTML 中的路径,当浏览器请求该文件时,可以根据文件名和版本号等参数,判断是否需要重新请求该文件。
下面是一个示例:
-- -------------------- ---- ------- ----- -------------- - ---------------------------------- -------------- - - -- ---------- ------- - --------- ---------------------------- -- ------- ----------- ----- ----------------------- -------- -- -------- - --- ---------------- --------- ---------------- -- -- ------------- ---- --------- --------- -- ---------------- ---------------- ----- -- - ------------- --- ---- --- ----- - -- ------ ------------- -------------------- ----- --- ----- -------- ------- - --- -- -展开代码
在 HTML 中使用 manifest.json,例如:
-- -------------------- ---- ------- --------- ----- ------ ------ ----- -------------- --------------------------- ---- -- ------------- -- --- ------- ------ ---- ---- --- ------- -------------------------------------- ---- ------- -- -- --- ------- -------展开代码
在服务器端,我们需要对请求 /manifest.json
进行特殊处理,将 HTTP 响应头部中的 Cache-Control 和 Expires 都设置为 Cache-Control: max-age=31536000(即一年的时长),这样浏览器就可以在一年内缓存该文件。
-- -------------------- ---- ------- ---------------- ----------------------------------- -------- - ----------- ----- ----- ----- -- - -- --- ------------- --------- -- ------------------------ - ------------------------------ ------------------- ------------------------ --- --------------- - --------------------------- - -- ---展开代码
三、 基于版本号的缓存策略
有时候即使文件内容没有变化,我们也需要更新文件的版本号来强制浏览器重新加载文件,例如更新一些资源的过期时间。一种常见的做法是通过增加版本号的方式,在文件名中添加版本号信息。我们可以将构建时间作为版本号,通过插件实现。
下面是一个示例:
-- -------------------- ---- ------- ----- - ------------------ - - ------------------------------- -------------- - - -- ---------- ------- - --------- ---------------------------- ----------------------- -- ------------ ----- ----------------------- -------- -- -------- - --- --------------------- -- ---------- -- -展开代码
在服务器端,我们同样需要设置强缓存,但需要根据请求的 URL 进行判断。例如,对于一般请求,我们可以设置缓存时间为 1 年,而对于包含版本号的请求,则需要设置为立即过期。
-- -------------------- ---- ------- ----- -------- - ----------- ---------------- ----------------------------------- -------- - ----------- ----- ----- ----- -- - -- ---------------------------------------- - -- -------------------- ------------------------------ ----------- ------------------------ --- --------------------- - ---- - -- ----------- ------------------------------ ------------------- - ------- ------------------------ --- --------------- - ------------------------ - -- ---展开代码
四、 总结
实现 SPA 应用中的打包文件快速缓存需要注意以下几个点:
- 使用 contenthash 对文件名进行设置,保证文件内容更新时文件名也会更新,避免浏览器读取旧的缓存文件。
- 对于打包生成的 manifest.json 文件,设置 Cache-Control 和 Expires 都为一年,确保浏览器能够长期地缓存该文件。
- 如果需要更新资源的过期时间等信息,可以通过在文件名中添加时间戳来实现。
- 在服务器端,需要根据请求的 URL 来设置不同的缓存策略。
通过合理地设置缓存策略,可以显著地提高 Web 应用的性能,减少用户的等待时间,提升用户体验。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/647bf05e968c7c53b073050d