基础概念
Nuxt3: Vue生态系统的最佳上层框架
框架定位
Nuxt3是基于Vue3的现代全栈通用框架,为开发者提供了结构化的开发方案和丰富的功能,大幅提升开发效率和用户体验。作为Vue生态中的顶级框架,Nuxt3已于2022年底发布稳定版,现已成为Vue项目开发的首选解决方案。
核心优势
1. 现代化开发环境
- Vue3整合:深度集成Vue3 Composition API和最新特性
- Vite驱动:默认使用Vite构建工具,提供极速的开发体验(HMR)
- TypeScript支持:零配置TypeScript支持,完善的类型推断和编辑器集成
- 文件系统路由:基于目录结构自动生成路由配置
- 自动导入:组件、API和函数自动导入,减少重复import语句
2. 多种渲染模式
- 服务端渲染(SSR):改善SEO和首屏加载体验
- 客户端渲染(CSR/SPA):传统单页应用体验
- 静态站点生成(SSG):预渲染页面为静态HTML
- 混合渲染(Hybrid):同一应用中混合使用不同渲染策略
- 边缘渲染(Edge-Side Rendering):在CDN边缘节点渲染内容
3. 全栈开发能力
- Nitro服务引擎:强大的服务器端引擎
- API路由:轻松创建后端API端点
- 服务器中间件:处理请求响应周期
- 多平台部署:支持Node.js、Serverless、静态托管等多种部署方式
4. 卓越开发体验
- Nuxt DevTools:内置开发调试工具,可视化应用结构和性能
- 模块生态系统:丰富的官方和社区模块(图像优化、内容管理、认证等)
- 层(Layers):跨项目复用配置、组件和功能
- 增量静态再生(ISR):结合静态生成和动态更新的优势
架构组成
- Nuxt3由多个紧密集成的包组成:
- nuxt:核心引擎,协调各组件并提供基础功能
- nuxi:命令行工具,用于创建、开发和构建项目
- nitro:服务端引擎,处理SSR和API路由
- @nuxt/vite-builder:Vite集成(默认推荐)
- @nuxt/webpack-builder:Webpack集成(备选方案)
- @nuxt/kit:模块开发工具包
- @nuxt/devtools:开发调试工具集
适用场景
Nuxt3适用于各种Web应用开发场景:
- 内容网站:博客、文档、新闻网站(利用SSG/SSR优化SEO)
- 电商应用:产品展示与交易平台(结合SSR和API路由)
- SaaS产品:企业级Web应用(利用全栈能力)
- 个人作品集:展示类静态网站(利用SSG高效部署)
- Web应用:任何需要结构化开发的中大型Vue项目
Nuxt 整体架构
Nuxt 框架由一些包组成,它们各有不同作用:
- 核心引擎:nuxt,实现核心功能,串联所有模块;
- 打包:@nuxt/vite-builder、@nuxt/webpack-builder;
- 命令行工具:nuxi,创建、调试、打包项目等;
- 服务端引擎:nitro,服务端渲染,API 路由;
- 开发包:@nuxt/kit,用于 Nuxt 模块开发;
- Nuxt 2 桥:@nuxt/bridge,用于 Nuxt2 项目中使用 Nuxt3 特性。
渲染模式
传统服务端渲染
在传统Web开发中,页面渲染完全由服务器执行。服务器获取数据,组装HTML,再发送给客户端显示。典型技术包括JSP、PHP、ASP.NET等。
特点
- 服务器负担重,单机并发能力低
- 前后端代码混合,职责不清晰
- 开发协作效率低,前后端耦合度高
客户端渲染(CSR)
Vue、React等现代前端框架带来的单页应用(SPA)模式,将渲染工作转移到浏览器执行。
工作流程
- 浏览器首次获取几乎为空的HTML框架
- 下载并执行JavaScript代码
- 通过API获取数据
- 在客户端动态构建页面DOM
优势
- 前后端分离,开发效率高
- 减轻服务器渲染压力
- 页面切换流畅,用户体验佳
劣势
- 首屏加载时间长
- SEO表现差,搜索引擎难以爬取内容
- 客户端性能要求高
适用场景:后台管理系统、在线工具、SaaS应用等不依赖SEO的交互密集型应用。
通用渲染
Nuxt的通用渲染模式结合了传统服务端渲染和现代SPA的优势。
工作流程
- 服务器执行Vue组件渲染,生成完整HTML
- 客户端接收并立即显示内容
- 同时下载JavaScript
- 执行"注水(Hydration)"过程,激活静态HTML为可交互应用
注水过程:将静态HTML与JavaScript重新"绑定",恢复事件监听和状态管理,使页面获得交互能力。这是SSR的核心环节。
优势
- 首屏加载快,用户体验好
- SEO友好,搜索引擎可抓取完整内容
- 保留SPA的交互体验
劣势
- 开发约束增加(服务端环境限制)
- 服务器资源消耗较高
- 需要Node.js/Serverless运行环境
适用场景
内容展示类网站、电商平台、企业官网等需要SEO且重视首屏加载速度的应用。
静态站点生成
预先渲染整个网站为静态HTML文件,适合内容变化不频繁的网站。
工作流程
- 构建时执行一次性渲染,生成所有路由的静态HTML
- 将静态文件部署到CDN或静态文件服务器
- 用户访问时直接获取预渲染的HTML
- 客户端执行"注水"过程,恢复交互性
优势
- 极速的首屏加载体验
- 最低的服务器运行成本
- 最佳的安全性(无动态服务器)
- SEO友好
劣势
- 内容更新需要重新构建部署
- 不适合频繁变化的动态内容
- 大型网站构建时间长
最新进展:Nuxt3已支持部分增量生成能力(ISR),通过配置routeRules可实现按需重新生成特定页面,无需重建整站。
适用场景
博客、文档站、营销页面等内容相对固定的网站。
混合渲染
Nuxt3的创新功能,允许在同一应用中基于路由规则使用不同渲染策略。
工作原理
通过routeRules配置,为不同路由路径指定不同的渲染模式
// nuxt.config.js
export default defineNuxtConfig({
routeRules: {
'/': { ssr: true }, // 首页使用SSR
'/products/**': { static: true }, // 产品页使用SSG
'/admin/**': { ssr: false }, // 管理后台使用CSR
'/api/**': { cors: true } // API路由启用CORS
}
})
优势
- 最佳性能与灵活性的平衡
- 按需选择最合适的渲染策略
- 维护成本低,开发体验一致
可靠性
混合渲染在Nuxt3中已经非常稳定可靠,配置正确时不会带来额外的稳定性问题。
适用场景
复杂应用,如电商平台(静态产品页+动态购物车)、内容网站(静态内容+动态评论系统)等。
边缘渲染
在CDN边缘节点执行轻量级渲染,提供最低延迟的用户体验。
工作原理
- 将应用部署到全球分布的边缘计算网络
- 用户请求路由到最近的边缘节点
- 在边缘节点执行轻量级渲染
- 返回渲染结果给用户
技术基础
Nuxt3的Nitro服务器引擎支持多运行时环境,包括Cloudflare Workers、Vercel Edge Functions等无需完整Node.js环境的边缘计算平台。
优势
- 最低的网络延迟(TTFB)
- 全球一致的快速响应
- 扩展性好,无需管理服务器
- 降低主源服务器负载
可靠性
随着Cloudflare、Vercel等平台的成熟,边缘渲染已经达到生产可用的稳定性水平。
适用场景
全球用户分布的应用、对延迟敏感的服务,如实时数据展示、交易系统等。
渲染模式对比总结
渲染模式
首屏性能
SEO友好度
服务器负载
开发复杂度
内容更新
最佳应用场景
CSR
较慢
较差
低
低
实时
管理后台、工具类应用
SSR
快
优秀
中高
中
实时
内容网站、电商、门户
SSG
极快
优秀
极低
低
需重建
博客、文档、营销页
Hybrid
按需优化
按需优化
按需优化
中
灵活
复杂多功能应用
Edge
极快
优秀
分散
中高
实时
全球化服务、高性能应用
Nuxt3的优势在于提供统一的开发体验,让开发者能够根据具体需求灵活选择最合适的渲染策略,实现性能与开发效率的最佳平衡。
开发应用
安装配置部分略过,可以去官网查看:[https://nuxt.com.cn/](https://nuxt.com.cn/)
Pages
Pages是Nuxt应用的基础,通过文件系统自动生成路由。
文件系统路由详解
-
基本路由:
-
pages/index.vue→/ -
pages/about.vue→/about -
pages/products/index.vue→/products -
pages/products/[id].vue→/products/:id -
动态路由详解:
-
单参数:
pages/users/[id].vue→/users/:id -
多参数:
pages/[category]/[product].vue→/:category/:product -
可选参数:
pages/[[slug]].vue→/或/:slug -
捕获所有:
pages/[...slug].vue→ 匹配所有未定义的路由 -
嵌套路由:
-
创建同名的目录和文件:
-
pages/users.vue -
pages/users/index.vue -
pages/users/profile.vue -
父组件需要包含
<NuxtPage />组件作为子路由的出口
声明
对于目录结构建议直接浏览官方文档:[https://nuxt.com.cn/](https://nuxt.com.cn/),此处不做赘述。
重要知识介绍
useFetch()
useFetch是Nuxt 3提供的一个组合式函数(composable),用于在Nuxt应用中进行数据获取。它专为Nuxt的服务端渲染(SSR)和客户端水合(hydration)过程设计,能够自动处理跨请求状态管理、请求重复去除、数据缓存等复杂问题。
核心特性
- 同构请求 - 在服务端和客户端使用相同的API
- 自动状态管理 - 提供加载状态、错误处理等
- 智能缓存 - 避免重复请求相同的数据
- 请求去重 - 多个组件请求相同URL只发一次请求
- 自动序列化/反序列化 - 服务端到客户端数据传递
- 类型安全 - 提供完整的TypeScript支持
基本使用语法
const { data: profile, pending, error, refresh } = useFetch('/api/user/profile', {
// 使用pick选择特定字段
pick: ['id', 'name', 'email']
})
// data将只包含这三个字段,即使API返回了更多字段
data: 请求返回的数据(响应式)pending: 请求是否正在进行中(布尔值)error: 请求错误信息(如果有)refresh: 刷新数据的函数
请求发送逻辑流程
1. 初始化阶段
当组件调用useFetch时,Nuxt会:
- 检查缓存 - 首先检查相同URL和选项的请求是否已存在
- 生成唯一键 - 基于URL和选项创建缓存键
- 初始化状态 - 设置pending = true、data = null、error = null
2. 服务端渲染(SSR)阶段
如果在服务端渲染期间调用useFetch:
- 发起请求 - 服务端直接发起HTTP请求
- 等待结果 - 服务端会等待请求完成(这会阻塞页面渲染)
- 更新状态 - 请求完成后更新data或error
- 序列化数据 - 将结果序列化并注入到生成的HTML中
- 状态传递 - 将数据状态传递给客户端,避免客户端重复请求
3. 客户端水合(Hydration)阶段
当浏览器接收到HTML并执行JavaScript:
- 接收注入数据 - 从HTML中提取序列化的请求结果
- 恢复状态 - 使用服务端传来的数据恢复data状态
- 跳过重复请求 - 不会重新发起相同的请求
4. 客户端导航阶段
当用户在应用内导航到新页面时:
- 检查缓存 - 如果请求已缓存且未过期,直接使用缓存数据
- 发起请求 - 如无缓存或已过期,发起新请求
- 更新UI - 根据pending、data、error状态更新界面
- 缓存结果 - 将新结果存入缓存
详细参数选项
useFetch(url, {
// 请求方法
method: 'POST',
// 请求体
body: { name: '张三' },
// 请求头
headers: {
'Content-Type': 'application/json'
},
// 查询参数
query: { limit: 10 },
// 唯一键(用于缓存标识)
key: 'custom-key',
// 转换响应数据的函数
transform: (data) => data.results,
// 响应类型
responseType: 'json',
// 仅在服务端获取(不在客户端重新获取)
server: true,
// 仅在客户端获取(服务端不获取)
client: false,
// 懒加载模式(手动触发加载)
lazy: false,
// 请求超时时间(毫秒)
timeout: 5000,
// 缓存选项
cache: {
// 缓存时间(毫秒),超时后自动失效
maxAge: 60000
}
})
使用示例
-
基本GET请求
-
将API请求逻辑提取到composables中
// composables/useProducts.ts export function useProducts(query = {}) { return useFetch('/api/products', { query, transform: (response) => response.data, default: () => [] }) }
// 使用 const { data: products } = useProducts({ category: 'electronics' })
-
错误处理
$fetch
// 指定返回类型
interface User {
id: number;
name: string;
email: string;
}
// 使用泛型指定响应类型
const user = await $fetch<User>('/api/user/1')
// TypeScript会知道user有id、name和email属性
console.log(user.name) // 类型安全
// 创建取消控制器
const controller = new AbortController()
// 设置超时自动取消
const timeout = setTimeout(() => controller.abort(), 3000)
try {
const data = await $fetch('/api/slow-operation', {
signal: controller.signal
})
clearTimeout(timeout)
return data
} catch (error) {
if (error.name === 'AbortError') {
console.log('请求被取消')
}
throw error
}
// 创建全局拦截器
const customFetch = $fetch.create({
// 基础URL
baseURL: 'https://api.example.com',
// 请求拦截
onRequest({ options }) {
// 添加认证token
const token = localStorage.getItem('token')
if (token) {
options.headers = options.headers || {}
options.headers.Authorization = `Bearer ${token}`
}
},
// 响应拦截
onResponse({ response }) {
// 统一处理响应
return response._data.result
},
// 错误拦截
onResponseError({ response }) {
// 处理401错误
if (response.status === 401) {
// 重定向到登录页
navigateTo('/login')
}
}
})
// 使用自定义fetch实例
const data = await customFetch('/users')
实际应用场景
// 表单提交
async function submitForm() {
const formData = {
username: username.value,
password: password.value,
rememberMe: rememberMe.value
}
try {
const result = await $fetch('/api/login', {
method: 'POST',
body: formData
})
// 登录成功处理
localStorage.setItem('token', result.token)
navigateTo('/dashboard')
} catch (error) {
// 错误处理
if (error.response?.status === 401) {
loginError.value = '用户名或密码错误'
} else {
loginError.value = '登录失败,请稍后重试'
}
}
}
// 数据删除
async function deleteItem(id) {
if (!confirm('确定要删除吗?')) return
try {
await $fetch(`/api/items/${id}`, {
method: 'DELETE'
})
// 删除成功
showNotification('删除成功')
// 刷新列表
refreshNuxtData('items-list')
} catch (error) {
console.error('删除失败', error)
showNotification('删除失败:' + error.message, 'error')
}
}
// 实时搜索
const searchQuery = ref('')
const searchResults = ref([])
const isSearching = ref(false)
let searchTimeout
// 防抖搜索函数
async function performSearch() {
clearTimeout(searchTimeout)
if (!searchQuery.value) {
searchResults.value = []
return
}
searchTimeout = setTimeout(async () => {
isSearching.value = true
try {
searchResults.value = await $fetch('/api/search', {
params: {
q: searchQuery.value,
limit: 10
},
// 添加短超时,确保响应速度
timeout: 3000
})
} catch (error) {
console.error('搜索失败', error)
} finally {
isSearching.value = false
}
}, 300) // 300ms防抖
}
// 监听搜索词变化
watch(searchQuery, performSearch)
useAsyncDate()
缓存机制
useAsyncData的缓存机制是Nuxt数据获取系统的核心特性之一,它可以:
- 避免重复请求相同数据
- 在页面导航间保持数据状态
- 确保服务端渲染(SSR)和客户端水合(hydration)过程中数据一致
- 提供数据失效和刷新的控制方法
缓存键(Cache Key)的工作原理
每次调用useAsyncData时,必须提供一个唯一的键或生成键的函数:
// 静态键
useAsyncData('users', () => $fetch('/api/users'))
// 动态键(函数形式)
useAsyncData(() => `user-${id.value}`, () => $fetch(`/api/users/${id.value}`))
这个键决定了:
- 数据在缓存中的存储位置
- 如何识别相同的数据请求
- 何时可以重用已缓存的数据
缓存存储位置
useAsyncData的缓存存储在Nuxt的内部状态管理系统中:
- 服务端: 存储在请求生命周期内的内存中
- 客户端: 存储在应用实例的内存中
- SSR传递: 服务端的缓存数据会被序列化并传递到客户端
- 注意: 这不是持久化缓存,页面刷新会清空缓存数据
缓存的生命周期
- 创建: 首次调用特定键的useAsyncData时
- 复用: 后续使用相同键调用时
- 刷新: 手动调用refresh()或watch触发时
- 失效: 调用refreshNuxtData(key)或clearNuxtData(key)时
- 清除: 页面刷新或应用重启时
使用示例
const page = ref(1)
const { data: paginatedUsers } = useAsyncData(
() => `users-page-${page.value}`,
() => $fetch('/api/users', { params: { page: page.value } })
)
// 当page变化时,键也会变化
function nextPage() {
page.value++
// 自动使用新键`users-page-2`获取并缓存新数据
}
initialCache选项
// 默认使用缓存(如果有)
const { data: posts } = useAsyncData('posts',
() => $fetch('/api/posts'),
{ initialCache: true } // 默认值
)
// 强制忽略缓存,总是发起新请求
const { data: freshPosts } = useAsyncData('posts',
() => $fetch('/api/posts'),
{ initialCache: false }
)
手动控制缓存
1. 刷新特定数据
const { data, refresh } = useAsyncData('users', () => $fetch('/api/users'))
// 手动刷新,发起新请求并更新缓存
function updateUsers() {
refresh()
}
2. 全局刷新数据
// 刷新指定键的所有useAsyncData调用
function refreshUserData() {
refreshNuxtData('users')
}
// 刷新多个数据
function refreshAllData() {
refreshNuxtData(['users', 'posts', 'comments'])
}
3. 完全清除缓存
// 清除特定键的缓存
clearNuxtData('users')
// 清除所有缓存
clearNuxtData()
缓存与SSR的关系
服务端渲染时:
- 请求数据并存入服务端缓存
- 缓存数据被序列化并注入到HTML中
客户端水合时:
- 从HTML中提取序列化数据
- 重建客户端缓存,与服务端保持一致
- 跳过重复请求,实现无缝水合
客户端导航时:
使用内存中的缓存数据
- 仅在需要时(缓存未命中或失效)发起新请求