渲染模式总览
Nuxt 4 支持通用渲染、客户端渲染、混合渲染和边缘渲染。渲染指浏览器和服务器解析 JavaScript 代码,把 Vue 组件转换成 HTML 元素。
这四种模式的区别在于 HTML 在哪台机器上生成,以及能不能按路由分别选择。
通用渲染
通用渲染(universal rendering)和 PHP、Ruby 应用的服务器端渲染类似:Nuxt 在服务器环境里执行 Vue.js 代码,把渲染完成的 HTML 返回给浏览器。
页面如果已经预先生成,Nuxt 也可能直接从缓存返回 HTML。用户马上就能拿到应用的完整初始内容,不必等 JavaScript 跑完。
HTML 文档下载完成后,浏览器解析它,Vue.js 接着接管。同一份 JavaScript 代码在浏览器里初始化应用,把事件监听器绑定到 HTML 上,页面恢复可交互,这个过程叫水合(hydration)。
水合完成后,页面还能使用动态界面和页面过渡。
服务器和客户端各跑一部分
通用渲染下,同一个 Vue 文件里的不同部分会分别在服务器和客户端执行。
<script setup lang="ts">const counter = ref(0) // 服务器和客户端都会执行
const handleClick = () => { counter.value++ // 只在客户端执行}</script>
<template> <div> <p>Count: {{ counter }}</p> <button @click="handleClick">Increment</button> </div></template>首次请求这个组件时,counter 要在模板里渲染,而 counter 这个 ref 是在服务器端的 Node.js 环境里初始化的。
handleClick 的函数体在服务端永远不会执行,和客户端才有的 DOM、BOM 一样。
浏览器执行水合时,counter 会被重新初始化,handleClick 最终绑定到按钮上,之后它的函数体始终在浏览器里运行。
不同渲染模式下,Nuxt 目录里各部分代码的执行环境不同。
- 水合过程中,中间件(middlewares)和页面(pages)在服务器和客户端上都运行。
- 插件(plugins)可以只在服务器运行、只在客户端运行,或者两边都运行。
- 组件(components)可以用
<ClientOnly>包裹,强制只在客户端渲染。 - 可组合项(composables)和工具函数(utilities)按使用位置决定运行环境。
优势
浏览器显示静态内容比显示 JavaScript 生成的内容快,用户能立即看到页面内容,水合过程又保留了 Web 应用的交互性。
通用渲染把页面的完整 HTML 内容像传统服务器应用那样发给浏览器,网络爬虫可以直接索引页面内容,适合需要被快速索引的页面。
缺点
服务器和浏览器提供的 API 不同,写一份两边都能直接运行的代码并不容易。
按需渲染页面要求服务器持续运行,每月都要付费,和传统服务器一样。客户端导航能减少服务器请求,改用边缘渲染也能降低成本。
客户端渲染
客户端渲染(client-side rendering,CSR)指在浏览器里用 JavaScript 生成 HTML 内容。原生 Vue SPA 默认就是这样:浏览器必须先下载并执行完所有 JS,才能生成页面 HTML 展示内容。
优势
开发时不区分服务器和客户端环境,可以直接使用 window 这类只有浏览器才有的 API。
纯客户端应用可以托管在任何静态服务器上,只需要 HTML、CSS 和 JavaScript 文件,不需要为常驻服务器付出基础设施成本。
代码完全在浏览器里运行,断网后也能继续工作。
缺点
用户必须等浏览器下载、解析并执行完所有需要的 JavaScript 文件,等待时间取决于网络状况和用户设备。
搜索引擎爬虫不会等界面渲染完成再索引,客户端渲染交付的内容因此要更久才会出现在搜索结果里。
客户端渲染适合不需要索引,或者用户频繁交互的 Web 应用。
混合渲染
混合渲染(hybrid rendering)用路由规则(route rules)给不同路由单独配置缓存策略,同时规定服务器收到某个 URL 的新请求时怎么响应。
旧版 Nuxt 里整个应用只能统一选一种渲染模式:要么全部服务端渲染,要么全部客户端渲染。内容类网站很难两全,内容页面适合静态生成,后台管理模块需要登录权限,更像动态 SPA。
Nuxt 现在内置了路由规则和混合渲染。路由规则能批量匹配路由,单独改匹配到的路由的渲染模式,或者给它们配置专属缓存策略。Nuxt 会自动注册对应的中间件,依托 Nitro 服务缓存层,给匹配到的路由包一层缓存处理。
在 nuxt.config.ts 里配置:
export default defineNuxtConfig({ routeRules: { // 首页:构建时提前预渲染(静态生成) '/': { prerender: true },
// 产品列表页:按需生成,后台自动刷新,API 数据变化前一直缓存 '/products': { swr: true },
// 产品详情页:按需生成,后台自动刷新,缓存 1 小时(3600 秒) '/products/**': { swr: 3600 },
// 博客列表页:按需生成,后台自动刷新,在 CDN 上缓存 1 小时 '/blog': { isr: 3600 },
// 博客文章页:构建或部署时生成一次,下次部署前一直缓存在 CDN '/blog/**': { isr: true },
// 管理后台:只在客户端渲染(关闭 SSR,纯 SPA 模式) '/admin/**': { ssr: false },
// API 路由:自动添加跨域 CORS 响应头 '/api/**': { cors: true },
// 旧页面:自动重定向到新页面 '/old-page': { redirect: '/new-page' }, },})九个键的作用如下。
| 键 | 类型 | 作用 |
|---|---|---|
redirect | string | 定义服务端重定向,把请求路由跳转到指定地址 |
ssr | boolean | 关闭部分页面的服务端 HTML 渲染;设为 false 后,这些页面只在浏览器里渲染 |
cors | boolean | 设为 true 会自动添加跨域资源共享(CORS)响应头,跨域参数可以用 headers 覆盖 |
headers | object | 为指定页面或资源(静态资源、接口)添加自定义 HTTP 响应头 |
swr | number 或 boolean | 给服务端响应加缓存头,在服务端或反向代理里缓存完整响应,TTL 可配。缓存过期后先返回旧缓存,同时在后台重新生成。设为 true 时只加 stale-while-revalidate 头,不设过期时间 |
isr | number 或 boolean | 作用和 swr 一致,区别是把响应缓存到受支持平台(Netlify、Vercel)的 CDN 上。设为 true 时内容一直缓存在 CDN,直到下次项目部署才更新 |
prerender | boolean | 构建阶段提前渲染路由,把生成的页面作为静态资源打包进构建产物 |
noScripts | boolean | 禁用指定页面的 Nuxt 脚本和 JS 资源预加载提示,页面只保留静态 HTML |
appMiddleware | string、string[] 或 Record<string, boolean> | 为 Vue 应用内的页面路径(不是 Nitro 服务端路由)指定需要或不需要执行的应用中间件 |
边缘渲染
边缘渲染(edge-side rendering,ESR)把 Nuxt 应用放到 CDN 的边缘服务器上渲染。渲染地点离用户更近,延迟更低。
用户请求页面时,请求不会直接发到源服务器,而是由最近的边缘服务器拦截处理。边缘服务器直接生成页面 HTML 并返回,数据传输的物理距离短,页面加载更快。
ESR 能实现,靠的是驱动 Nuxt 的服务引擎 Nitro。Nitro 跨平台,可以运行在 Node.js、Deno、Cloudflare Workers 等环境里。
目前支持 ESR 的平台有三个。
- Cloudflare Pages:通过 Git 集成和
nuxt build命令,零配置直接启用 - Vercel:使用
nuxt build命令,并配置环境变量NITRO_PRESET=vercel-edge - Netlify:使用
nuxt build命令,并配置环境变量NITRO_PRESET=netlify-edge
Vue 3 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 226 天
部分内容可能已过时