利用 link rel="prefetch":如何让用户的页面秒开?

大家好,我又来了😁。

我们做性能优化,通常盯着的是 首屏加载速度(FCP, LCP)。我们压缩图片、拆分代码(Code Splitting)、上CDN... 拼了老命把首屏时间从 2s 压到了 1s。

但是,用户用起来还是觉得不够快🤔。

为什么?因为当用户点击 下一页 或者 查看详情 时,那个讨厌的 Loading转圈圈 又出现了。哪怕只有 300ms,那也是一种打断。

今天,我想聊一个被很多人低估的小技巧------prefetch(预获取) 。

我不改一行业务代码,不重构架构,只用几行代码,就让用户的下一个页面实现了秒开。


preload vs prefetch

很多同学知道preload,但经常把它和prefetch搞混。这俩虽然长得像,但作用完全不同:

  • preload (预加载) : 我现在的页面马上就要用!

    • 优先级:High。
    • 场景:当前页面的关键字体、首屏的核心CSS/JS。
    • 如果你不用,当前页面会渲染阻塞或闪动。
  • prefetch (预获取) : 我现在闲着也是闲着,帮我把下一个页面的东西先下载下来呗?

    • 优先级:Low (浏览器会在网络空闲(Idle)时才去下载)。
    • 场景 :下一个页面可能需要的 JS Chunk、CSS 或图片。
    • 即使用户没去下一个页面,也不影响当前页面的体验,顶多浪费一点点流量。

我们现在聊的,核心就是利用prefetch,利用用户思考、滑动鼠标的那些垃圾时间,偷偷把下一个页面的资源加载好。


让详情页瞬间出现

假设我们有一个商品列表页 ,用户很有可能会点击第一个商品进入详情页。

在 React/Vue 的单页应用(SPA)中,进入详情页通常意味着要加载一个Detail.chunk.js。如果等用户点击了再加载,那必然有几百毫秒的白屏。

我的做法是这样的:

当检测到用户的鼠标移动到 某个商品卡片上(或者滚动到可视区域时),我就猜:嘿,这哥们可能要点进去了🤣。

这时候,我立刻在<head>里动态插入一个prefetch标签:

JavaScript 复制代码
// 预获取下一个页面的 JS 资源
const prefetcher = (url) => {
  const link = document.createElement('link');
  link.rel = 'prefetch';
  link.href = url;
  document.head.appendChild(link);
};

// 模拟:鼠标移入商品卡片时触发
// 在 Webpack/Vite 项目中,你通常能通过 manifest 找到对应路由的 chunk 地址
card.addEventListener('mouseenter', () => {
  prefetcher('/assets/ProductDetail.23a9c.js');
  prefetcher('/assets/ProductDetail.css');
});

发生了什么?

  1. 用户鼠标悬停(hover)需要 200ms~500ms 才会点击。
  2. 浏览器利用这几百毫秒的空闲,已经在后台把ProductDetail.js下载好了 ,并放进了HTTP 缓存(Disk Cache) 。
  3. 当用户真的点击时,路由跳转,请求资源 -> 浏览器发现缓存里有 -> 直接从磁盘读取 -> 耗时 0ms!

用户感觉就是:手指刚落下,页面就砰地一下出来了。 这种爽快感,是任何代码压缩都给不了的。


手动写mouseenter太累了,而且我怎么知道打包后的文件名叫啥?

Google Chrome 团队早就想到了,他们开源了一个神器叫 quicklink。

它的原理极其简单且暴力:

检测视口(Viewport)内的链接,自动给它们加上prefetch。

JavaScript 复制代码
// 1. 安装
// npm install quicklink

// 2. 在你的页面加载完成后调用
import { listen } from 'quicklink';

listen({
  // 选项:只在网络环境好的时候预取 (默认就是这样)
  // 选项:限制预取的数量,防止浪费流量
  limit: 5 
});

只要这一行代码!

当用户滚动列表时,进入视口的那些商品链接对应的 JS/CSS 资源,就会被自动静默下载。用户点哪个,哪个就是秒开。

Next.js 的 <Link> 组件,默认就内置了类似的功能。 这也是为什么 Next.js 应用通常感觉比普通 React 应用快的原因之一。


但 prefetch 也不是万能的

虽然它很爽,但作为一个负责任的工程师,我必须告诉你几个坑😖:

  1. 必须配合正确的缓存策略 (Cache-Control)

prefetch 下载的资源是放在 HTTP 缓存里的。如果你的服务器设置了Cache-Control: no-store,那用户点击时,浏览器还是会重新发请求,你的预获取就白做了。确保你的静态资源有长缓存(Max-Age)策略。

  1. 别预取 API 接口 (慎用😭)

有些人想连 API 数据也预取。这很危险。因为数据的时效性很难保证。你预取了库存为1,用户点进去时可能已经卖光了。除非是静态博客类的内容,否则只建议预取 JS/CSS/图片 等静态资源。

  1. 移动端省流模式

在移动端,流量就是钱。浏览器通常比较智能,在省流模式或弱网下,会忽略prefetch指令。我们在做手动实现时,也可以通过 navigator.connection.saveData 来判断,如果是省流模式,就别预取了,做个好人😂。


性能优化,不仅仅是把资源做小 ,更是对用户行为的一种预判。

用 link rel="prefetch" ,当你还在苦哈哈地抠那 10kb 的打包体积时,不妨试试这个技巧,说不定你的网站会飞起来。

有时候,让用户感觉快,比真的快,更重要。

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄4 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95274 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大4 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端