博客加载优化之旅

追求网站速度

第一站:instant.page

最开始看到的是 instant.page。它的理念非常简单直接:在用户鼠标悬停在链接上超过 65 毫秒后,就开始预加载(prefetch)该链接的 HTML 内容。这样,当用户真正点击链接时,页面内容可能已经下载到本地缓存,从而实现“即时”加载。

集成它也非常简单,只需在 <body> 标签结束前加入一行代码:

<script src="//instant.page/5.2.0" type="module" integrity="sha384-jnZyxPjiipYXnSU0ygqeac2q7CVYMbh84q0uHVRRxEtvFPiQYbXWUorga2aqZJ0z"></script>

这个方案的局限性:在移动端,这种基于鼠标悬停的机制不生效。

在搜索的过程中又发现了 Google Chrome Labs 的 quicklink。它比 instant.page 更智能,采用了不同的策略:

  1. 检测视口内的链接:只预加载出现在用户当前屏幕视口内的链接。
  2. 等待浏览器空闲:使用 requestIdleCallback 来确保预加载操作不会影响当前页面的渲染性能。
  3. 检测慢速网络:在用户的网络连接较慢(navigator.connection.saveData === true)或设置为“慢速2G”时,不会进行预加载。

这使得 quicklink 成为了一个在性能和资源消耗之间取得绝佳平衡的工具。在 Astro 项目中集成它也很方便:

  1. 安装 quicklink:

    npm install quicklink
  2. 在你主Layout文件中引入并初始化:

    import quicklink from 'quicklink';
    
    // 在页面加载完成后初始化
    window.addEventListener('load', () => {
       quicklink.listen();
    });

目前站点:Astro View Transitions

Perplexity 推荐了 Astro 的最新功能:View Transitions API。这本是一个浏览器原生 API,Astro 对其进行了完美的封装,让开发者可以轻松地在 MPA 架构下实现 SPA 般的平滑页面过渡。

与前两者不同,View Transitions 关注的不是“预加载”,而是“过渡动画”。它通过捕捉两个页面状态的“快照”,并智能地计算出两者之间的差异,然后应用流畅的动画效果。

开启 View Transitions

在 Astro 中开启这个功能的方法如下:

  1. 导入 ViewTransitions 组件:在你的公共布局文件(例如 src/layouts/Layout.astro)的 <head> 部分,导入并使用 ViewTransitions 组件。

    ---
    import { ViewTransitions } from 'astro:transitions';
    ---
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>My Astro Site</title>
        <ViewTransitions />
      </head>
      <body>
        <slot />
      </body>
    </html>

自定义过渡效果

Astro 的 View Transitions 还支持自定义。

  • 内置动画:除了默认的 fade,还可以使用 slide 等内置动画。

    <ViewTransitions fallback="slide" />
  • 持久化元素:通过 transition:name 指令标记在页面跳转时保持不变的元素(例如导航栏),让它们平滑地“移动”到新页面的对应位置,而不是重新渲染。

    <!-- src/components/Navbar.astro -->
    <nav transition:name="navbar" class="main-nav">
      ...
    </nav>
  • 自定义 CSS 动画:使用 ::view-transition-old()::view-transition-new() 等 CSS 伪元素,完全自定义元素的进入和离开动画。

    ::view-transition-old(root) {
      animation: fade-out 0.5s ease-out forwards;
    }
    ::view-transition-new(root) {
      animation: fade-in 0.5s ease-in forwards;
    }
    
    @keyframes fade-in {
      from { opacity: 0; }
    }
    @keyframes fade-out {
      to { opacity: 0; }
    }