Nuxt 内容站点如何用 noScripts 与预渲染组合实现近零 JavaScript 页面?
【免费下载链接】nuxtThe full-stack Vue framework.项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
内容或营销站点的页面大多由文本和图片构成,而 Nuxt 默认在服务端渲染页面后会在浏览器中完整 hydrate。对一个 95% 是静态内容的页面来说,这份 hydrate 用的 JavaScript 基本是纯开销:它推迟交互、与内容争抢带宽和 CPU,还会拖累移动端 PageSpeed 之类的指标。Nuxt 提供了在不需要的地方省掉这笔成本的手段:在 routeRules 中用prerender在构建期把内容路由渲染成静态 HTML,再用noScripts从渲染出的 HTML 中剥离 Nuxt 脚本,得到"近零 JavaScript"的页面;少数需要交互的路由则保留脚本、用 lazy hydration 控制 hydrate 时机。
主路径是:为路由声明prerender: true, noScripts: true→ 用nuxt build构建 → 检查构建产物确认无脚本。服务端渲染的小部件和保留脚本的路由是两个可选分支,放在后面。
先判断哪些路由能用 noScripts
noScripts生效时,渲染出的 HTML 会省略以下内容(见 noScripts 特性说明):
- Nuxt 入口
<script>标签 - 入口 chunk 的 import map
- 内联 payload
<script>(启用 payload extraction 时还有 payload preload link) - JavaScript 资源提示(JS chunk 的
preload和prefetchlink)
CSS 不受影响:样式表链接和内联样式照常渲染,页面外观一致。但页面从此不会 hydrate,意味着页面上的一切都不是交互式的:
- 没有
@click等事件处理器 <NuxtLink>不做 prefetch,链接以普通<a>标签工作- 页面上没有客户端导航,进入或离开该页都是整页文档加载
所以第一步是选路由:noScripts只应用于"本来就不需要任何客户端交互"的路由。另注意,被noScripts路由规则命中的路由总是走 buffered(非流式)渲染器。
开启方式有全局features.noScripts(取值为'production'或'all')和按路由的noScriptsroute rule 两种。内容站点建议用 route rule:只影响指定路由,且在开发、生产两种模式下都生效,两者差异见文末的 E8007 一节。
配置 routeRules
修改nuxt.config.ts,声明哪些路由要预渲染、哪些要去掉脚本:
export default defineNuxtConfig({ routeRules: { // pure content: prerendered, zero JS '/': { prerender: true, noScripts: true }, '/blog/**': { prerender: true, noScripts: true }, // pages with an interactive widget: prerendered, scripts kept '/contact': { prerender: true }, }, })两个关键点:
- 路由规则属于 Hybrid Rendering 机制,文档明确指出使用
nuxt generate时不可用。因此这套"按路由预渲染 + 去脚本"的组合要用nuxt build构建。 - 预渲染由 Nitro 爬虫基于"根页面 + 页内链接"发现路由,未被链接到的页面不会被自动预渲染。爬虫发现不了的页面(如 CMS 生成的文章列表)需要用
nitro.prerender.routes显式列出,或通过prerender:routeshook 从接口拉取后注册,详见 Prerendering。
如果只把静态产物部署到静态托管,还需要显式声明 SPA 回退页——nuxt generate和nuxt build --prerender会自动生成200.html/404.html,用 routeRules 预渲染时要手动加(见 Deployment):
export default defineNuxtConfig({ routeRules: { '/200.html': { prerender: true }, }, })构建并核对产物
执行nuxt build后,直接检查构建产物来验证:
.output/public中应有各命中路由的静态 HTML;预渲染路由还会伴随生成_payload.json(记录构建时捕获的数据,供客户端导航复用)。- 打开某个
noScripts路由的 HTML 核对省略项:没有 Nuxt 入口<script>、没有 import map、没有内联 payload<script>、没有 JS 的preload/prefetchlink;样式相关标签仍在。 - 用
npx serve .output/public起本地静态服务预览(文档给出的静态产物预览方式)。内容应正常显示,点击链接以整页加载跳转,页面上不执行 JavaScript。
导航方向 Nuxt 会替双方处理:
- 从有脚本的路由经
navigateTo、<NuxtLink>或router.push进入noScripts路由时触发文档加载,目标页由服务端按无脚本形式提供,而不是交给客户端路由渲染。 - 由于这些路由从不在客户端渲染,只要覆盖它们的路由规则能在构建期解析,对应页面组件会整个被排除出客户端 bundle。
- 两类页面都会输出声明式 speculation rules 标签,让支持的浏览器在用户点击前 prefetch/prerender 目标。规则范围限定在页面路由上,指向
/logout这类服务端路由的链接不会被推测性抓取。
可选:noScripts 路由上的服务端小部件
渲染 markdown、语法高亮代码、CMS 内容这类"有真实逻辑但无需交互"的部分,可以写成 server component(文件名带.server.vue后缀),只在服务端渲染,其依赖不会进入客户端 bundle:
app/ └─ components/ └─ HighlightedMarkdown.server.vue<template> <article> <h1>{{ post.title }}</h1> <HighlightedMarkdown :markdown="post.body" /> </article> </template>(对应文件:app/pages/blog/[slug].vue)
这种纯静态 island 在noScripts路由上工作正常,因为它的 HTML 在渲染时就已嵌入。文档同时给出明确警告:island 中完全交互的小部件(标记了nuxt-client的组件,或交互式 island 插槽)在noScripts路由上不可用——island 的 teleports 靠一小段内联脚本把内容移回原位,脚本被禁用后这一步无法执行,什么都不会 hydrate。页面里只要需要一个交互组件,就该给该路由保留脚本、改用 lazy hydration。
可选:保留脚本的路由用 lazy hydration
需要部分交互的路由保留脚本,用 hydration 策略控制组件何时变为交互式:
<template> <div> <HeroSection /> <!-- static content that never needs to become interactive --> <LazyTestimonials hydrate-never /> <!-- hydrate only when scrolled into view --> <LazyNewsletterSignup hydrate-on-visible /> <!-- hydrate when the browser is idle --> <LazyCookieBanner hydrate-on-idle /> </div> </template>hydrate-never:组件 HTML 由服务端渲染但永不 hydrate,JavaScript 从不执行,对内容站点尤其有用。注意任何 prop 变化都会立即触发 hydrate。hydrate-on-visible/hydrate-on-idle:滚动进入视口 / 浏览器空闲时 hydrate。- 内置 lazy hydration 目前只对单文件组件(SFC)生效,策略 prop 必须直接写在模板里(不能用
v-bind展开 prop 对象),也不支持从#components直接导入的组件。 - 所有延迟 hydrate 的组件 hydrate 后都会发出
@hydrated事件,可以监听它来确认组件确实变为可交互。
常见坑:开发正常、上线失效(E8007)
如果改用全局features.noScripts,默认值是'production':只在生产构建中剥离脚本,开发环境下页面仍携带 JavaScript。于是依赖脚本的行为(lazy hydration 策略、nuxt-client组件)在开发时看似正常,部署后悄悄失效。Nuxt 在开发期间检测到渲染路由使用了这些特性时会发出 E8007 警告。
文档给出的两个解决方向:
- 移除受影响路由上的客户端依赖,或者用
noScripts路由规则把脚本剥离限定到真正静态的路由——规则在开发环境同样生效,出问题会立即暴露。本文主路径就是这种写法。 - 设置
features.noScripts: 'all',让开发环境也剥离脚本,从而在开发期就能观察到生产行为。
边界与限制
noScripts页面没有任何交互能力,不要覆盖含表单、按钮、页内跳转的路由。- 通配路由会扩大 speculation rules 的范围:catch-all 页面(如
pages/[...slug].vue)匹配所有路径,规则会退回覆盖所有同源链接。 - 想要整页跳转有动画而不是闪烁,可以启用实验性 view transitions,这些页面会自动加入 cross-document view transitions:
export default defineNuxtConfig({ experimental: { viewTransition: true, }, })完整配方见 Mostly-Static Sites,相关文档:noScripts 特性、预渲染、部署、延迟 hydrate 组件。
【免费下载链接】nuxtThe full-stack Vue framework.项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考