大多为静态的网站
为内容和营销网站提供接近零的 JavaScript,同时保留交互岛。
默认情况下,Nuxt 会先对页面进行服务端渲染,然后再对整个页面进行 hydration。对于一个 95% 都是静态文本和图片的页面来说,这些 hydration JavaScript 基本上是额外开销:它会延迟交互,并与内容争夺带宽和 CPU,从而可能损害诸如移动端 PageSpeed 分数之类的指标。
Nuxt 提供了避免在不需要的地方付出这部分成本的组件。这个方案将它们结合起来:
- 预渲染 你的内容路由,在构建时完成。
- 使用
noScripts路由规则,移除这些路由上的脚本。 - 对于需要服务端渲染但不需要交互性的组件,使用 服务器组件。
- 在保留脚本的路由上使用 延迟 hydration,这样只有在需要时才为交互付费。
预渲染并移除脚本
对于完全没有客户端交互的路由,将 routeRules 中的 prerender 和 noScripts 结合使用:
nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/blog/**': { prerender: true, noScripts: true },
'/about': { prerender: true, noScripts: true },
},
})
这些路由会在构建时渲染为纯 HTML,渲染后的页面会省略 Nuxt 入口脚本、导入映射、payload 脚本以及 JS 资源提示。CSS 仍然会包含在内。其结果是页面几乎没有 JavaScript 成本。
noScripts 页面不会进行 hydration。页面上的任何内容都不可交互:没有 @click 处理器,没有 <NuxtLink> 预取(链接仍然会像普通 <a> 标签一样正常工作),也不能从该页面进行客户端导航。仅在你确实需要这种行为的路由上使用它。用于服务端渲染小部件的岛屿
静态页面的某些部分仍然适合做成带有真实逻辑的组件:渲染后的 markdown、语法高亮代码、CMS 内容。为此请使用 服务端组件。它们在服务端渲染,其依赖项不会进入客户端 bundle:
Directory Structure
-| app/
---| components/
-----| HighlightedMarkdown.server.vue
app/pages/blog/[slug].vue
<template>
<article>
<h1>{{ post.title }}</h1>
<HighlightedMarkdown :markdown="post.body" />
</article>
</template>
像这样的静态岛屿在 noScripts 路由上也能正常工作,因为它们的 HTML 会在渲染时嵌入。
岛屿内部的完全交互式小部件(标记为
nuxt-client 的组件,或交互式岛屿插槽)在 noScripts 路由上无法工作。岛屿传送内容会在水合前通过一小段内联脚本重新定位到正确位置;在禁用脚本时,这种重新定位无法执行,因而不会发生水合。如果某个页面哪怕只需要一个交互式小部件,也应在该路由上保持脚本开启,并改用延迟水合。其余部分的延迟 Hydration
对于需要一些交互性的路由,保留脚本启用,但使用延迟 hydration来控制组件何时进行 hydration:
app/pages/index.vue
<template>
<div>
<HeroSection />
<!-- 永远不需要变为可交互的静态内容 -->
<LazyTestimonials hydrate-never />
<!-- 仅在滚动到视图中时进行 hydration -->
<LazyNewsletterSignup hydrate-on-visible />
<!-- 在浏览器空闲时进行 hydration -->
<LazyCookieBanner hydrate-on-idle />
</div>
</template>
hydrate-never 在内容型网站上特别有用:组件的 HTML 会在服务端渲染,但它永远不会被 hydration,因此它的 JavaScript 也永远不会执行(不过属性变化仍然会触发 hydration)。
整合起来
一个典型的以静态内容为主的网站最终会是这样的:
nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// 纯内容:预渲染,零 JS
'/': { prerender: true, noScripts: true },
'/blog/**': { prerender: true, noScripts: true },
// 带有交互式组件的页面:预渲染,但保留脚本
'/contact': { prerender: true },
},
})
- 内容路由只会输出纯 HTML 和 CSS
- islands(
.server.vue组件)会在各处处理服务端渲染的组件,同时将沉重依赖排除在客户端包之外 - 少数需要交互的路由会保留脚本,并通过
hydrate-on-visible/hydrate-on-idle/hydrate-never来限制浏览器前期要做的工作量