我的新宠技术栈 Nuxt 4 Nuxt UI MongoDB 和 Quarkus
我最近把主力技术栈换成了 Nuxt 4 + Nuxt UI + MongoDB + Quarkus,用了一段时间之后觉得值得写一篇完整的分享。
选这个组合的原因不复杂:原型快,生产性能扛得住,每个组件的文档都过关,社区活跃,而且从简单 API 到复杂系统都能覆盖。下面逐个说。
Nuxt 4:Vue 生态的又一次进化
2025 年 7 月 15 日发布的 Nuxt 4,重心放在稳定性和 DX 上。不是那种大张旗鼓的破坏性升级,而是把过去一年在小版本里验证过的东西变成默认值。
项目组织
新的目录结构把应用代码统一放在 app/ 下,和 node_modules/、.git/ 分离。效果是文件监视器更快了(Windows 和 Linux 上尤其明显),IDE 的上下文判断也更准。
my-app/
├─ app/
│ ├─ assets/
│ ├─ components/
│ ├─ pages/
│ └─ app.vue
├─ shared/
├─ server/
└─ nuxt.config.ts能感知到的性能提升
Turbo 模式构建速度快达 10 倍。服务器冷启动从 Nuxt 3 的 ~2.3s 降到 ~1.1s,砍掉了 52%。CLI 和 Vite 开发服务器之间走 socket 通信,网络端口开销没了。原生 edge server 让你在离用户最近的地方跑应用,延迟压到最低。
数据获取
useAsyncData 和 useFetch 现在自动在使用相同 key 的组件之间共享数据,卸载时自动清理,缓存控制也更灵活了。
// 优化的数据获取示例
const { data: products } = await useAsyncData(
'products',
() => $fetch('/api/products'),
{
server: true,
default: () => [],
transform: (data) => data.map(p => ({...p, formattedPrice: formatCurrency(p.price)}))
}
)Nuxt UI v4:免费的专业设计
2025 年 9 月发布的 Nuxt UI v4 把之前的 Nuxt UI 和 Nuxt UI Pro 合成了一个 MIT 许可的开源库。以前要花钱才能用的 Pro 组件,现在全免费。
110 多个生产就绪组件,覆盖了表单(输入框、选择框、复选框、单选框)、导航(菜单、面包屑、分页)、布局(卡片、模态框、侧边栏、手风琴)、数据展示(表格、列表、网格)和反馈(提示框、警报、进度条、骨架屏)。日常项目需要的组件基本不缺了。
底层选得也好。Tailwind CSS v4 构建快 5 倍。Reka UI 保证 WAI-ARIA 合规,无障碍性不用自己操心。Tailwind Variants 提供完整的 TypeScript 类型和主题系统。
实际示例
<template>
<UContainer class="py-8">
<UCard>
<template #header>
<h1 class="text-2xl font-bold">用户管理</h1>
</template>
<UTable :rows="users" :columns="columns">
<template #actions-data="{ row }">
<UButton
icon="i-heroicons-pencil"
size="sm"
color="primary"
variant="ghost"
@click="edit(row)"
/>
</template>
</UTable>
<template #footer>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">
{{ users.length }} users found
</span>
<UPagination
v-model="page"
:total="total"
:page-count="10"
/>
</div>
</template>
</UCard>
</UContainer>
</template>
<script setup lang="ts">
const users = ref([])
const page = ref(1)
const total = ref(100)
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'actions', label: 'Actions' }
]
async function loadUsers() {
const { data } = await useFetch('/api/users', {
query: { page: page.value }
})
users.value = data.value
}
</script>MongoDB:灵活的数据层
MongoDB 的 JSON 文档模型和 JavaScript/TypeScript 天然契合,这是它对我最大的吸引力。不需要在语言之间做思维转换。
schema 可以随需求演进,不用写复杂的 migration。对需求频繁变化的快速原型、半结构化数据、迭代频繁的敏捷开发来说,这一点省了很多痛苦。
和技术栈的集成
在 Nuxt 里用 Mongoose 连 MongoDB 很直接:
// Nuxt 中的 Mongoose 连接
import mongoose from 'mongoose'
export default defineNitroPlugin(async (nitroApp) => {
if (!mongoose.connection.readyState) {
await mongoose.connect(process.env.MONGODB_URI)
}
})
// 示例架构
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, unique: true },
role: { type: String, enum: ['admin', 'user', 'editor'] },
preferences: {
theme: String,
notifications: Boolean
},
createdAt: { type: Date, default: Date.now }
})
// 优化索引
userSchema.index({ email: 1 })
userSchema.index({ createdAt: -1 })复合索引、聚合管道、全文搜索、change stream 实时更新、sharding 水平扩展,这些功能组合起来,从中小规模到高并发的场景都能覆盖。
Quarkus:Java 给云原生的回答
Quarkus 的卖点是启动快、内存省。对微服务和 serverless 来说,这两点直接决定成本。
2025 年的基准数据:原生模式冷启动 ~50ms,RSS 内存 ~12MB,P99 延迟 ~95ms,已用堆 ~3.2MB。这个量级的资源消耗,放 Kubernetes 里跑容器非常合适。
JVM 模式下热重载快,适合日常开发。原生模式编译成原生可执行文件,适合生产部署。两个模式切换很自然。
// Quarkus 中的 REST API 示例
@Path("/api/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProductResource {
@Inject
ProductService service;
@GET
@Operation(summary = "列出产品", description = "返回所有产品")
public Response list(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size) {
List<Product> products = service.list(page, size);
return Response.ok(products).build();
}
@POST
@Operation(summary = "创建产品")
public Response create(ProductDTO dto) {
Product product = service.create(dto);
return Response.status(Status.CREATED)
.entity(product)
.build();
}
}MongoDB 集成
Quarkus 提供 MongoDB Panache 来简化数据访问,写法和 JPA 类似:
// 使用 Panache 的实体
@Entity
public class Product extends PanacheMongoEntity {
public String name;
public String description;
public BigDecimal price;
public Integer stock;
public String category;
public LocalDateTime createdAt;
// 静态查询
public static List<Product> findByCategory(String category) {
return list("category", category);
}
public static List<Product> findActive() {
return list("stock > 0");
}
public static long countByCategory(String category) {
return count("category", category);
}
}
// 仓库
@ApplicationScoped
public class ProductRepository implements PanacheMongoRepository<Product> {
public List<Product> searchWithStock(int page, int size) {
return find("stock > 0")
.page(page, size)
.list();
}
public Optional<Product> searchByCode(String code) {
return find("code", code).firstResultOptional();
}
}整体架构
四个组件一起跑起来的时候,这个栈的优势才真正体现出来:
┌─────────────────────────────────────────────────────────┐
│ Nuxt 4 (前端) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Nuxt UI v4 │ │ Pages/Views│ │ API Client │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼ HTTP/REST/gRPC
┌─────────────────────────────────────────────────────────┐
│ Quarkus (后端) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ REST API │ │ Services │ │ MongoDB Panache │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ MongoDB │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Products │ │ Users │ │ Orders │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘Nuxt 配置:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxt/ui',
'@nuxtjs/mdc',
'@pinia/nuxt'
],
runtimeConfig: {
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
}
},
ui: {
global: true,
icons: ['heroicons', 'simple-icons']
},
devtools: { enabled: true }
})集中式 API 客户端:
// composables/useApi.ts
export const useApi = () => {
const config = useRuntimeConfig()
const client = $fetch.create({
baseURL: config.public.apiBase,
headers: {
'Content-Type': 'application/json'
},
timeout: 30000
})
return {
get: <T>(url: string, options?: object) =>
client<T>(url, { method: 'GET', ...options }),
post: <T>(url: string, body: object, options?: object) =>
client<T>(url, { method: 'POST', body, ...options }),
put: <T>(url: string, body: object, options?: object) =>
client<T>(url, { method: 'PUT', body, ...options }),
delete: <T>(url: string, options?: object) =>
client<T>(url, { method: 'DELETE', ...options })
}
}性能数据
前端(Nuxt 4)
| 指标 | 值 | 基准 |
|---|---|---|
| 首次内容绘制 | ~1.2s | 比 Nuxt 3 快 33% |
| 最大内容绘制 | ~1.9s | 比 Nuxt 3 快 34% |
| 累积布局偏移 | ~0.08 | 比 Nuxt 3 好 47% |
| 构建时间 | ~18s | 比 Nuxt 3 快 60% |
后端(Quarkus 原生)
| 指标 | 值 | 基准 |
|---|---|---|
| 冷启动 | ~50ms | 领先的 Java 框架 |
| 内存 RSS | ~12MB | 容器的理想选择 |
| P99 延迟 | ~95ms | 一致的性能 |
| 吞吐量 | ~50K req/s | 高并发 |
适合什么场景
需要无障碍、设计完整的 UI 时,Nuxt UI 110 多个组件省了大量造轮子的时间。Quarkus 的低资源消耗和毫秒级响应适合微服务和 serverless。MongoDB 对需求变化频繁的初创项目来说迁移成本低。面向全球用户的应用,Nuxt Edge 加 Quarkus 可以把延迟压到很低。
结论
这套栈用下来,我的感受是每个组件都在自己的位置上做对了事。Nuxt 4 负责前端的速度和类型安全,Nuxt UI 负责界面的一致性和可访问性,MongoDB 负责数据的灵活性,Quarkus 负责后端的性能和资源效率。文档质量过关,社区活跃,学习曲线可控,长期维护可预期。
不是每个项目都需要这套组合,但如果你的需求落在上面这些场景里,值得试试。
额外资源: