为什么 用vite进行分包后,可以通过 浏览器强制缓存 提高性能?路由懒加载进行的分包与 vite进行的分包有什么不同?

文章目录

  • [一、为什么 用vite进行分包后,可以通过浏览器强制缓存提高性能?](#一、为什么 用vite进行分包后,可以通过浏览器强制缓存提高性能?)
    • [1、 什么是浏览器强制缓存?](#1、 什么是浏览器强制缓存?)
      • [1.1 协商缓存(需要举手提问 🙋‍♂️)](#1.1 协商缓存(需要举手提问 🙋‍♂️))
      • [1.2 强制缓存(霸道总裁,直接拿来用 ⚡)](#1.2 强制缓存(霸道总裁,直接拿来用 ⚡))
    • [2、 强缓存这么好,那它有什么致命缺点?](#2、 强缓存这么好,那它有什么致命缺点?)
    • [3、 为什么用 Vite 分包后,能通过强缓存大幅提高性能?](#3、 为什么用 Vite 分包后,能通过强缓存大幅提高性能?)
      • [场景 A:不分包(所有的东西揉成一个 `index-[hash].js`)](#场景 A:不分包(所有的东西揉成一个 index-[hash].js))
      • [场景 B:用 Vite 分包(利用 `manualChunks` 拆分)](#场景 B:用 Vite 分包(利用 manualChunks 拆分))
  • [二、路由懒加载进行的分包与 vite进行的分包有什么不同?](#二、路由懒加载进行的分包与 vite进行的分包有什么不同?)

一、为什么 用vite进行分包后,可以通过浏览器强制缓存提高性能?

要想完全理解为什么 Vite 分包能利用强制缓存大幅提升性能,我们得先拆开来看:先明白浏览器缓存的"游戏规则",再看 Vite 是怎么利用这个规则在生产环境"作弊"的。


1、 什么是浏览器强制缓存?

浏览器缓存分为两种:协商缓存强制缓存 。它们的区别就在于要不要去问服务器

1.1 协商缓存(需要举手提问 🙋‍♂️)

浏览器每次用这个文件前,都要发个请求问服务器:"服务器大哥,我本地有这个文件,它过期了吗?" 服务器对比一下,如果没变,返回一个 304 Not Modified,浏览器再读取本地缓存。

  • 缺点 :虽然传输体积变小了,但依然发出了一次 HTTP 网络请求。只要有网络请求,就会受到网络延迟(RTT)的限制。

1.2 强制缓存(霸道总裁,直接拿来用 ⚡)

服务器在第一次返回文件时,在响应头(Response Headers)里加上一个标记,比如:
Cache-Control: max-age=31536000(告诉浏览器:这文件一年内都不会变)。

当用户第二次访问或者刷新页面时,浏览器一看这个标记:

"哦,还在保质期内呢,我连网络请求都不发了,直接从本地内存(Memory Cache)或硬盘(Disk Cache)里把文件秒秒钟拿出来。"

  • 状态码 :在开发者工具的 Network 面板里,你会看到状态码是 200 OK (from disk cache)
  • 收益 :网络请求数直接变成 0 ,耗时接近 0ms。这是最极致的网络性能优化。

2、 强缓存这么好,那它有什么致命缺点?

强缓存唯一的缺点就是:太死板

如果服务器上的代码更新了,但是浏览器以为文件还在"保质期"内,它就绝对不会向服务器发请求,而是继续使用本地的老代码。这就导致了用户打不开新功能、或者页面直接报错(俗称"发版后缓存不更新")。

现代前端(Vite/Webpack)解决这个问题的终极武器就是:内容哈希(Content Hash)

当 Vite 打包时,会根据文件的内容计算出一个唯一的哈希值作为文件名,比如 index-b87a2c.js

  • 只要你改了代码,打包出来的文件名就会变成 index-f49e1a.js
  • 因为 HTML 页面是绝对不走强缓存 的(HTML 通常配置 Cache-Control: no-cache),浏览器每次都会拉取最新的 HTML。最新的 HTML 里引用了新的 JS 文件名,浏览器就会去下载新文件;而没变的文件名,浏览器继续走强缓存。

3、 为什么用 Vite 分包后,能通过强缓存大幅提高性能?

这就聊到你问的核心了。如果不分包,强缓存的威力会被严重削弱。

场景 A:不分包(所有的东西揉成一个 index-[hash].js

你把 Vue 核心库、Pinia、Axios、Echarts 还有你写的几百个业务组件,全部打进了一个巨大的 index-v1.js 里。这个包很大(比如 2MB),你给它设置了强缓存。

  • 问题来了 :第二天,你发现首页有个错别字,你把 "欢荧光临" 改成了 "欢迎光临"
  • 打包结果 :由于内容变了,整个 2MB 的大包文件名瞬间变成了 index-v2.js
  • 用户代价 :用户重新访问时,浏览器发现文件名变了,强缓存全部失效 。用户不得不为了你改动的一个错别字,把原本完全没变的 Vue、Echarts 等第三方大库重新下载了一遍! 用户的首屏加载又变慢了。

场景 B:用 Vite 分包(利用 manualChunks 拆分)

你通过 Vite 配置,把包拆开了:

bash 复制代码
dist/assets/
├── vendor-d7c3a1.js       # 包含 vue, vue-router, pinia, axios (150kb)
├── echarts-8f4e2a.js      # 包含完整的 echarts 图表库 (500kb)
└── index-b87a2c.js        # 纯粹是你自己写的业务代码 (50kb)

这时候,你同样给 assets 目录下的所有 JS 文件在服务器(如 Nginx)上配置了强制缓存

同样是改了一个首页错别字,你再次打包:

  1. vendor 里的代码没变,打包出来依然叫 vendor-d7c3a1.js
  2. echarts 里的代码没变,打包出来依然叫 echarts-8f4e2a.js
  3. 你的业务代码变了,打包出来变成了 index-f49e1a.js

此时用户重新进站,神奇的事情发生了:

浏览器请求最新的 HTML,发现里面引用了这三个文件。

  • vendor-d7c3a1.js 匹配本地缓存 ➡️ 命中强缓存,0ms 搞定!
  • echarts-8f4e2a.js 匹配本地缓存 ➡️ 命中强缓存,0ms 搞定!
  • index-f49e1a.js 找不到,发请求下载 ➡️ 只下载了 50kb 的核心业务包。

最终结论 :Vite 分包的本质,是把"轻易不会变动的重型资产(第三方库)"与"天天都在变动的轻量资产(业务代码)"在物理上隔离开 。这样就能保证那些体积最大、下载最慢的第三方库,可以在用户本地的浏览器里永久命中强缓存,只有真正更新了的业务小包才走网络下载。首屏速度自然迎来了质的飞跃。

二、路由懒加载进行的分包与 vite进行的分包有什么不同?

路由懒加载rollupOptions.manualChunks(配置分包) 虽然最终结果都是"把代码拆成了多个 JS 小包",但它们的触发时机、控制维度和核心目的截然不同。

我们可以把它们比作"业务层面的按需索取""物理层面的资产归类"。


1. 什么是路由懒加载?

简单来说,路由懒加载是业务逻辑驱动的代码拆分(Code Splitting)。

如果不用懒加载(全量打包):

当你访问项目的登录页 http://localhost/login 时,浏览器会把主页、个人中心、后台数据大屏等所有页面的 JS 代码一次性全部下载下来。

结果: 首屏巨慢,用户明明只看了一个登录页,却要为整个系统的代码买单。

用了懒加载:

javascript 复制代码
const Dashboard = () => import('./views/Dashboard.vue')

当打包工具(Vite/Rollup)看到 import() 这种动态导入 语法时,它会敏锐地意识到:"哦!这个组件不需要一上来就加载。那我单独把它切出来,打包成一个独立的 dashboard-[hash].js 文件吧。"

  • 什么时候下载? 只有当用户真正点击了"控制台"路由,引发路由跳转时,浏览器才会通过网络去请求下载这个 dashboard-[hash].js

2. 什么是 Vite/Rollup 的 manualChunks 分包?

配置分包(如 manualChunks)是物理/依赖层面驱动的包体积合并与归类。

它是纯粹的工程化手段,不管你业务怎么跳转,它只关心的文件依赖关系缓存命中率

为什么需要它?

如果你只配置了路由懒加载,那么每个路由页面里引用的第三方库(比如 lodashecharts 等)可能就会被散落打包到各个路由小包里;或者有些公用组件被重复打包。

通过 manualChunks,你可以硬性规定:

javascript 复制代码
manualChunks(id) {
  if (id.includes('node_modules')) {
    // 把所有来自 node_modules 的第三方依赖,单独打包成一个叫 vendor 的大包
    return 'vendor'; 
  }
}

结果: 把业务代码和第三方生态彻底剥离。因为 node_modules 里的依赖基本不会变,这样打包出来的 vendor.js 就可以完美触发浏览器的强缓存(304 或 Cache-Control) 。即使你天天改业务代码发布新版本,用户也只需要重新下载小巧的业务包,vendor.js 直接走本地缓存,速度飞快。


3. 两者有什么不同?(核心对比)

为了更直观,我们通过一张表来看看它们在编译和运行时的本质区别:

维度 路由懒加载(动态导入 import() Vite/Rollup 配置分包 (manualChunks)
决定权在谁 开发者(业务层):由你的路由代码怎么写决定。 打包工具(构建层):由你的打包配置文件决定。
主要目的 减少首屏加载体积,不下载当前不需要的页面。 优化浏览器缓存,把变动频繁的业务码和稳定的第三方库分开。
工作阶段 **运行时(Runtime)**按需异步加载。 **构建时(Build time)**做物理文件分类。
典型代表 () => import('./Detail.vue') axiosvue 统一打包进 vendor.js

4. 它们是如何协同工作的?(1 + 1 绝对大于 2)

在实际的 Vite 项目中,我们不是二选一,而是两者结合使用

设想一个标准的 Vue + Vite 项目打包后的文件结构:

bash 复制代码
dist/assets/
├── index-AX432j.js       # 主包(包含 Vue 核心、main.js、全局状态,首屏必须)
├── vendor-BC892k.js      # 由 manualChunks 拆出来的第三方库大包(走强缓存,首屏必须)
├── Home-CD123f.js        # 路由懒加载拆出的首页(首屏必须)
├── About-EF456g.js       # 路由懒加载拆出的关于页(用户点到 About 时才下载)
└── Dashboard-GH789h.js   # 路由懒加载拆出的后台页(用户点到 Dashboard 时才下载)

完美的加载流程:

  1. 用户首次访问首页。
  2. 浏览器只下载了 index.jsvendor.js、和首屏对应的 Home.js
  3. 页面瞬间加载完成(首屏速度优化成功 ⚡)。
  4. 用户点击进入"关于我们"页面,浏览器默默发了一个网络请求,拉取了 About.js(路由懒加载生效 ⚡)。
  5. 第二天,你修改了首页的一个文案,重新打包发布。用户再次访问,浏览器发现 vendor.js 没变,直接读取本地缓存;只下载了变动的一点点业务包(manualChunks 缓存优化成功 ⚡)。
相关推荐
三*一10 小时前
Mapbox GL JS 前端多边形分割实战:从踩坑到优雅实现
开发语言·前端·javascript·vue.js
秋収冬藏10 小时前
第一章:Dify 整体架构总览
前端
时光不负努力10 小时前
阶段 6:前端工程体系 - 企业级落地
前端
KaMeidebaby10 小时前
卡梅德生物技术快报|多肽库筛选技术构建药物递送功能肽库:流程、算法与质控体
前端·数据库·其他·百度·新浪微博
李剑一10 小时前
字节一面,考察的够全面的啊!面试官:请分阶段解释一下从输入URL到页面渲染整个链路中的关键环节和可观测点
前端
xChive10 小时前
前端请求取消:用 AbortController 从 fetch 到 axios
前端·vue.js·axios·fetch·abortcontroller
Python大数据分析@10 小时前
HTML会代替Markdown吗?为什么?
前端·html
一棵树735110 小时前
js总结介绍
前端·javascript·html
jiayong2310 小时前
前端面试题库 - 工程化与性能优化篇
前端·面试·性能优化