Skip to content
Nuxt.js 概述
Nuxt.js 是基于 Vue 生态构建的一层约定与工具链。它封装了路由、数据获取、渲染模式选择和项目结构等工程基础设施,让开发者不必从零搭建这些模块。要理解它的必要性,需要先了解原生 Vue 单页应用(SPA)存在的局限。
为什么需要 Nuxt.js
一个标准的 Vue SPA 入口文件长这样:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SPA App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>浏览器收到的这份 HTML 中没有任何可见内容,实际页面需要等待 JavaScript 下载、解析、执行后才由 Vue 动态生成。在这种模式下,搜索引擎抓取与首屏体验都会出现明显差异。
SEO 问题:大部分搜索引擎爬虫不会等待客户端 JavaScript 执行完毕再索引页面,它们抓取到的内容往往仅是一个空的根节点。对于依赖内容索引的资讯、电商和文档类站点,影响严重。
首屏加载延迟:用户在低速网络或低性能设备上必须先经历完整的 JS 加载和执行流程,才能看到第一个有意义的内容。在 3G 条件下,白屏时间可能达到数秒。
这不是 Vue 本身的问题,而是 SPA 架构在服务端内容缺失与加载延迟上的天然取舍。
服务端渲染(SSR)能够在响应请求时直接返回填充了数据的 HTML,解决上述两个问题。然而手动实现 SSR 需要自行搭建 Node.js 服务、处理数据预取、组件序列化、客户端激活等一系列环节,多数团队并不需要重复实现这一底层设施。Nuxt.js 在此提供了一个集成方案:在 Vue 上层,用约定和配置取代这些手动工作。
Nuxt.js 与 Vue、Node.js 的关系
Nuxt 不是 Vue 的替代品,而是一个“元框架”。它把 Vue 作为核心渲染引擎,同时增加了由 Node.js 驱动的服务层。
架构可以分层理解:
- Vue 层:负责组件渲染、响应式、组合式 API。开发者书写的依然是标准的
.vue单文件组件,使用<template>、<script setup>、<style>这些原生能力。 - Node.js 服务层:通过内部的 Nitro 引擎,Nuxt 提供 SSR 能力、API 路由、静态生成。服务端逻辑可以在 JavaScript/TypeScript 同一上下文中完成。
- 约定与自动化层:文件系统路由、组件自动导入、数据获取的组合式函数(
useFetch、useAsyncData)等,都是在 Vue 原语之上提供的工程效率提升。
因此,从 Vue 的视角看,Nuxt 在组件模型之外补齐了路由、数据获取和部署目标的统一抽象。从 Node.js 的视角看,Nuxt 允许使用同一套代码仓库构建前端界面并生成后端服务或静态站点。
渲染模式
Nuxt 并不强制所有页面使用同一种渲染方式。它允许混合 SSR、SSG 和纯客户端渲染,并且这一决策可以精细到单个路由。
服务端渲染(SSR)
每次请求到达服务端,Nuxt 都会执行一遍渲染过程,生成包含数据的完整 HTML 再返回。页面加载后,客户端的 Vue 会“激活”这些服务端渲染出的 DOM 节点,使其变为可交互的应用。
适合内容频繁变化、且每个请求都需要最新数据的场景,例如用户仪表盘、消息列表。
静态站点生成(SSG)
构建时预渲染所有页面为静态 HTML,部署到 CDN 或静态文件服务器。每个用户收到的都是构建时生成的那份内容,没有服务端运行。
适合文档站、博客、产品介绍页等内容不常变化但访问量可能很大的页面。
客户端渲染(SPA)
部分路由可以关闭 SSR,退回到传统 SPA 模式。构建时只生成一个入口 HTML 壳子,页面内容完全由客户端 JavaScript 绘制。
通常用在后台管理系统中那些无需 SEO、且对首屏速度不敏感的页面。
混合渲染
在 nuxt.config.ts 中通过 routeRules 为不同路由指定不同策略,可以混合使用以上模式:
ts
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/users/**': { ssr: true },
'/admin/**': { ssr: false }
}
})这种能力意味着不必在全站级别做二选一:静态生成的首页可以秒开,需要实时数据的用户路由走 SSR,后台管理界面则留在客户端。
核心约定
Nuxt 将一套约定植入 Vue 开发流程。这些约定在后续章节才会详细展开,但其工程价值在一开始就应当被理解。
文件系统路由
纯 Vue 项目通过 vue-router 的配置数组手动创建路由。Nuxt 用目录结构代替了这些配置:
app/
└── pages/
├── index.vue → /
└── about.vue → /about在 pages 目录下新建一个 .vue 文件,就自动增加了一条同名路由。背后使用的依然是 vue-router,但开发者不再需要编写 createRouter 或维护路由表。
组件与组合式函数的自动导入
每个页面反复编写 import MyComponent from '...' 是一种工程负担。Nuxt 会扫描 app/components 目录,将其中组件注册为全局可用,任何页面或组件可直接使用标签,无需手动导入。
组合式函数(composables)也遵循类似机制。放在 composables/ 下的文件会被自动暴露,useFoo 命名导出无需在调用侧 import。
由此带来的直接变化是代码更简洁,并且 tree-shaking 仍然有效:未使用的组件和组合式函数不会被打包进生产代码。
内置组件与约定目录
Nuxt 提供了与路由和布局相关的内置组件:
<NuxtPage>:渲染当前匹配路由的页面组件,类似 Vue Router 的<router-view>。<NuxtLink>:增强版的导航链接,在视口内预加载目标页面资源,提升路由切换速度。
除了 pages/,Nuxt 还预设了若干具有特定职责的目录,例如:
layouts/:存放页面布局,默认使用default.vue。页面可通过definePageMeta切换布局。middleware/:存放路由中间件,在导航前执行鉴权、重定向等逻辑。components/:自动导入的 Vue 组件。composables/:自动导入的组合式函数。
这些目录的意义在于团队协作时,每个人都能通过文件位置快速判断代码的职能。
命令行工具
Nuxi 是 Nuxt 的 CLI,提供从初始化项目到构建、部署的完整流程:
bash
# 初始化一个新项目
npx nuxi init my-app
# 进入项目并安装依赖
cd my-app && npm install
# 启动开发服务器(默认支持 HMR)
npm run dev
# 构建生产版本
npm run build
# 生成静态站点(SSG)
npm run generate这些命令底层整合了 Vite 打包与 Nitro 服务端引擎,启动 dev 后文件修改会触发热模块替换,该特性同时作用于 Vue 组件和服务端代码。
从 Vue 到 Nuxt 的转变
对于之前使用 Vue CLI 或 Vite 脚手架的开发者,进入 Nuxt 后最直观的变化是项目结构不再从零搭建,而是从一个预制好的骨架开始。原来手动引入路由、注册组件、决定渲染策略这些“底层组装”工作,被替换为遵循约定。学习曲线的一部分从“怎么配置”移到了“这套约定怎样工作”。这不是限制——几乎所有约定都可以通过配置覆盖,但在起步阶段,它让项目能从一套经过验证的默认值开始。
另一个视角变化是“前端”和“后端”的边界变得模糊。SSR 过程中组件可能在服务端执行一次,又在客户端激活一次。开发者仍需按 Vue 组件的方式编写,但必须意识到组件的服务端运行环境没有 window、localStorage,访问浏览器 API 需要使用条件守卫或 Nuxt 提供的 import.meta.client 标记。
注意点
- 不需要彻底掌握 Vue 的全部知识再入手,但 Vue 组件基础和响应式理解仍然是必要的。当出现奇怪的 SSR 行为或 hydration 问题时,追根溯源还是要回到 Vue 的机制上。
- 组件自动导入依赖命名约定:组件文件必须使用 PascalCase 或 kebab-case,组合式函数必须放在
composables/并以use前缀导出。不遵守这些约定时自动导入会失效,且这类问题在开发工具中不易直观察觉。 layouts/、middleware/等目录虽然“存在即生效”,但具体的行为和 API 会在下一篇文章中展开。现阶段只需知道通过目录名可以区分代码职责。
