引言
如果把前端开发比作做菜,那么:
- 框架是主食(米饭、面条)
- 组件库是配菜(蔬菜、肉类)
- 状态管理是调料(盐、酱油)
- 构建工具是厨具(锅、灶)
- CSS 方案是烹饪手法(煎、炒、炸)
不同的食材搭配,做出不同的菜系。前端技术栈也一样------不同的组合,适合不同的场景。
本文将为你介绍 2025 年最流行的前端技术栈组合,从"家常小炒"到"满汉全席",总有一款适合你。
第一部分:技术栈全景图
前端技术栈的组成
┌─────────────────────────────────────────────────────────┐
│ 前端技术栈构成 │
├─────────────────────────────────────────────────────────┤
│ │
│ 🏗️ 框架层(Foundation) │
│ React / Vue / Angular / Svelte / Solid │
│ │
│ 🧩 UI 组件层(Components) │
│ Ant Design / Element Plus / shadcn/ui / ... │
│ │
│ 🎨 样式方案层(Styling) │
│ Tailwind CSS / CSS Modules / Sass / CSS-in-JS │
│ │
│ 📦 状态管理层(State) │
│ Zustand / Pinia / Redux / Jotai / ... │
│ │
│ 🛣️ 路由层(Routing) │
│ React Router / Vue Router / TanStack Router │
│ │
│ 🔌 数据请求层(Data Fetching) │
│ Axios / TanStack Query / SWR / fetch │
│ │
│ 📝 表单处理层(Forms) │
│ React Hook Form / VeeValidate / Formik │
│ │
│ 🔨 构建工具层(Build) │
│ Vite / Webpack / Turbopack / esbuild │
│ │
│ 📏 代码规范层(Linting) │
│ ESLint / Prettier / Biome │
│ │
│ 🧪 测试层(Testing) │
│ Vitest / Jest / Playwright / Cypress │
│ │
└─────────────────────────────────────────────────────────┘
技术选型的维度
┌─────────────────────────────────────────────────────────┐
│ 选型考虑因素 │
├─────────────────────────────────────────────────────────┤
│ │
│ 👥 团队因素 │
│ • 团队熟悉什么技术? │
│ • 招聘市场哪个更容易找人? │
│ • 学习成本如何? │
│ │
│ 📋 项目因素 │
│ • 项目规模多大? │
│ • 需要 SEO 吗? │
│ • 是 ToB 还是 ToC? │
│ • PC 端还是移动端? │
│ │
│ 🏢 企业因素 │
│ • 公司有技术规范吗? │
│ • 需要长期维护吗? │
│ • 有现成的脚手架吗? │
│ │
│ ⚡ 性能因素 │
│ • 首屏加载速度要求? │
│ • 需要服务端渲染吗? │
│ • 包体积限制? │
│ │
└─────────────────────────────────────────────────────────┘
第二部分:Vue 技术栈组合
🥇 组合一:Vue 企业级标准栈
定位:中后台管理系统的"黄金配方"
┌─────────────────────────────────────────────────────────┐
│ Vue 企业级标准栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Vue 3 │
│ 语言: TypeScript │
│ 构建工具: Vite │
│ 组件库: Element Plus │
│ 状态管理: Pinia │
│ 路由: Vue Router │
│ HTTP 请求: Axios │
│ CSS 方案: Sass/SCSS │
│ 代码规范: ESLint + Prettier │
│ Git 规范: Husky + Commitlint │
│ │
│ 适用场景:企业后台、管理系统、内部工具 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建项目
npm create vue@latest my-admin
# 选择配置
✔ Add TypeScript? Yes
✔ Add JSX Support? No
✔ Add Vue Router? Yes
✔ Add Pinia? Yes
✔ Add Vitest? Yes
✔ Add ESLint? Yes
✔ Add Prettier? Yes
# 进入项目
cd my-admin
# 安装组件库和工具
npm install element-plus
npm install axios
npm install -D sass
npm install -D @element-plus/icons-vue
# 安装 Git 规范工具
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
项目结构:
my-admin/
├── public/
├── src/
│ ├── api/ # API 请求封装
│ │ ├── index.ts # Axios 实例配置
│ │ ├── user.ts # 用户相关 API
│ │ └── product.ts # 产品相关 API
│ ├── assets/ # 静态资源
│ │ └── styles/
│ │ ├── variables.scss # SCSS 变量
│ │ └── global.scss # 全局样式
│ ├── components/ # 公共组件
│ │ ├── Layout/
│ │ └── Common/
│ ├── composables/ # 组合式函数(Hooks)
│ │ ├── useLoading.ts
│ │ └── usePermission.ts
│ ├── router/ # 路由配置
│ │ └── index.ts
│ ├── stores/ # Pinia 状态管理
│ │ ├── user.ts
│ │ └── app.ts
│ ├── types/ # TypeScript 类型定义
│ │ └── index. ts
│ ├── utils/ # 工具函数
│ │ ├── request.ts # 请求封装
│ │ └── auth.ts # 认证相关
│ ├── views/ # 页面组件
│ │ ├── Dashboard/
│ │ ├── User/
│ │ └── Login/
│ ├── App.vue
│ └── main.ts
├── . eslintrc.cjs
├── . prettierrc
├── tsconfig.json
├── vite.config.ts
└── package.json
核心代码示例:
// src/api/index.ts - Axios 封装
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user'
const request = axios.create({
baseURL: import.meta.env. VITE_API_URL,
timeout: 10000,
})
// 请求拦截器
request.interceptors.request.use(
(config) => {
const userStore = useUserStore()
if (userStore.token) {
config.headers.Authorization = `Bearer ${userStore.token}`
}
return config
},
(error) => Promise.reject(error)
)
// 响应拦截器
request.interceptors.response.use(
(response) => response. data,
(error) => {
ElMessage.error(error.response?.data?.message || '请求失败')
return Promise.reject(error)
}
)
export default request
// src/stores/user.ts - Pinia 状态管理
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { login, getUserInfo } from '@/api/user'
export const useUserStore = defineStore('user', () => {
const token = ref(localStorage.getItem('token') || '')
const userInfo = ref<UserInfo | null>(null)
const isLoggedIn = computed(() => !!token. value)
async function loginAction(username: string, password: string) {
const res = await login({ username, password })
token.value = res.token
localStorage.setItem('token', res.token)
}
async function fetchUserInfo() {
const res = await getUserInfo()
userInfo.value = res
}
function logout() {
token.value = ''
userInfo.value = null
localStorage.removeItem('token')
}
return { token, userInfo, isLoggedIn, loginAction, fetchUserInfo, logout }
})
<!-- src/views/User/UserList.vue - 典型列表页 -->
<template>
<div class="user-list">
<!-- 搜索栏 -->
<el-card class="search-card">
<el-form :inline="true" :model="searchForm">
<el-form-item label="用户名">
<el-input v-model="searchForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm. status" placeholder="请选择状态">
<el-option label="启用" value="active" />
<el-option label="禁用" value="inactive" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card class="table-card">
<template #header>
<div class="card-header">
<span>用户列表</span>
<el-button type="primary" @click="handleAdd">新增用户</el-button>
</div>
</template>
<el-table :data="userList" v-loading="loading" stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'danger'">
{{ row.status === 'active' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@change="fetchUserList"
/>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getUserList, deleteUser } from '@/api/user'
const loading = ref(false)
const userList = ref([])
const searchForm = reactive({
username: '',
status: '',
})
const pagination = reactive({
page: 1,
pageSize: 10,
total: 0,
})
async function fetchUserList() {
loading. value = true
try {
const res = await getUserList({
... searchForm,
page: pagination.page,
pageSize: pagination.pageSize,
})
userList.value = res.list
pagination.total = res.total
} finally {
loading. value = false
}
}
function handleSearch() {
pagination.page = 1
fetchUserList()
}
function handleReset() {
searchForm. username = ''
searchForm.status = ''
handleSearch()
}
async function handleDelete(row: any) {
await ElMessageBox.confirm('确定删除该用户吗?', '提示', { type: 'warning' })
await deleteUser(row. id)
ElMessage.success('删除成功')
fetchUserList()
}
onMounted(() => {
fetchUserList()
})
</script>
<style lang="scss" scoped>
.user-list {
padding: 20px;
. search-card {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.el-pagination {
margin-top: 20px;
justify-content: flex-end;
}
}
</style>
🥈 组合二:Vue 现代极简栈
定位:追求现代设计和开发体验的项目
┌─────────────────────────────────────────────────────────┐
│ Vue 现代极简栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Vue 3 │
│ 语言: TypeScript │
│ 构建工具: Vite │
│ 组件库: Naive UI │
│ 状态管理: Pinia │
│ 路由: Vue Router │
│ HTTP 请求: Axios + VueUse │
│ CSS 方案: UnoCSS │
│ 图标: Iconify │
│ 代码规范: ESLint + Prettier │
│ │
│ 适用场景:个人项目、开源项目、追求独特设计的项目 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建项目
npm create vue@latest my-app
# 安装依赖
cd my-app
npm install naive-ui
npm install @vueuse/core
npm install axios
# 安装 UnoCSS
npm install -D unocss @unocss/preset-uno @unocss/preset-icons
# 安装图标
npm install -D @iconify/json
Vite 配置:
// vite. config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'
export default defineConfig({
plugins: [
vue(),
UnoCSS(),
],
})
// uno.config.ts
import { defineConfig, presetUno, presetIcons } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetIcons({
scale: 1.2,
cdn: 'https://esm.sh/',
}),
],
})
使用示例:
<template>
<div class="p-6">
<!-- UnoCSS 原子类 -->
<h1 class="text-2xl font-bold text-gray-800 mb-4">
用户管理
</h1>
<!-- Iconify 图标 -->
<div class="i-carbon-user text-xl mr-2" />
<!-- Naive UI 组件 -->
<n-space vertical>
<n-input v-model:value="keyword" placeholder="搜索用户" />
<n-button type="primary" @click="search">
<template #icon>
<div class="i-carbon-search" />
</template>
搜索
</n-button>
</n-space>
<n-data-table
:columns="columns"
:data="users"
:loading="loading"
class="mt-4"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useAxios } from '@vueuse/integrations/useAxios'
const keyword = ref('')
const { data: users, isLoading: loading, execute } = useAxios('/api/users')
function search() {
execute({ params: { keyword: keyword.value } })
}
</script>
🥉 组合三:Vue 字节风格栈
定位:字节跳动技术风格,现代化中后台
┌─────────────────────────────────────────────────────────┐
│ Vue 字节风格栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Vue 3 │
│ 语言: TypeScript │
│ 构建工具: Vite │
│ 组件库: Arco Design Vue │
│ 状态管理: Pinia │
│ 路由: Vue Router │
│ HTTP 请求: Axios │
│ CSS 方案: Less │
│ 图标: Arco Design Icons │
│ 代码规范: ESLint + Prettier │
│ │
│ 适用场景:追求现代设计、需要暗黑模式、字节系项目 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 使用 Arco 官方脚手架(推荐)
npm create arco
# 或手动安装
npm create vue@latest my-arco-app
cd my-arco-app
npm install @arco-design/web-vue
npm install -D less
npm install -D unplugin-vue-components unplugin-auto-import
按需引入配置:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ArcoResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ArcoResolver()],
}),
Components({
resolvers: [
ArcoResolver({
sideEffect: true,
}),
],
}),
],
})
🏅 组合四:Nuxt 全栈栈
定位:需要 SSR/SSG 的全栈 Vue 应用
┌─────────────────────────────────────────────────────────┐
│ Nuxt 全栈栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Nuxt 3 │
│ 语言: TypeScript │
│ 渲染模式: SSR / SSG / ISR │
│ 组件库: Nuxt UI / Element Plus │
│ 状态管理: Pinia(内置支持) │
│ HTTP 请求: $fetch(内置)/ useFetch │
│ CSS 方案: Tailwind CSS │
│ ORM: Prisma / Drizzle(可选) │
│ 数据库: PostgreSQL / MySQL(可选) │
│ │
│ 适用场景:需要 SEO 的网站、博客、电商、全栈应用 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建 Nuxt 项目
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
# 安装 Nuxt UI(可选,Nuxt 官方组件库)
npm install @nuxt/ui
# 或者安装 Element Plus
npm install element-plus
npm install -D @element-plus/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxt/ui', // 使用 Nuxt UI
// 或
'@element-plus/nuxt', // 使用 Element Plus
],
// 开启 TypeScript 严格模式
typescript: {
strict: true,
},
})
Vue 技术栈对比表
|----------|----------|-----|--------------|
| 组合 | 适用场景 | 难度 | 特点 |
| 企业级标准栈 | 中后台管理 | ⭐⭐ | 最稳定,社区最大 |
| 现代极简栈 | 个人/开源项目 | ⭐⭐⭐ | 设计独特,轻量 |
| 字节风格栈 | 现代化中后台 | ⭐⭐ | 设计现代,暗黑模式 |
| Nuxt 全栈栈 | SEO/全栈应用 | ⭐⭐⭐ | SSR/SSG,功能全面 |
第三部分:React 技术栈组合
🥇 组合一:React 企业级标准栈
定位:大厂标配,稳定可靠
┌─────────────────────────────────────────────────────────┐
│ React 企业级标准栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: React 18 │
│ 语言: TypeScript │
│ 构建工具: Vite │
│ 组件库: Ant Design │
│ 状态管理: Zustand / Redux Toolkit │
│ 路由: React Router │
│ 数据请求: TanStack Query + Axios │
│ 表单处理: React Hook Form + Zod │
│ CSS 方案: CSS Modules / Sass │
│ 代码规范: ESLint + Prettier │
│ │
│ 适用场景:企业后台、管理系统、大型应用 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建项目
npm create vite@latest my-react-admin -- --template react-ts
cd my-react-admin
# 安装核心依赖
npm install antd @ant-design/icons
npm install zustand
npm install react-router-dom
npm install @tanstack/react-query axios
npm install react-hook-form zod @hookform/resolvers
npm install -D sass
项目结构:
my-react-admin/
├── public/
├── src/
│ ├── api/ # API 请求
│ │ ├── client.ts # Axios 实例
│ │ └── user.ts
│ ├── components/ # 公共组件
│ │ ├── Layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── index.tsx
│ │ └── Common/
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useAuth.ts
│ │ └── usePermission.ts
│ ├── pages/ # 页面组件
│ │ ├── Dashboard/
│ │ ├── User/
│ │ │ ├── List.tsx
│ │ │ └── Detail.tsx
│ │ └── Login/
│ ├── router/ # 路由配置
│ │ └── index.tsx
│ ├── stores/ # Zustand 状态
│ │ ├── userStore.ts
│ │ └── appStore.ts
│ ├── types/ # 类型定义
│ │ └── index.ts
│ ├── utils/ # 工具函数
│ │ └── request.ts
│ ├── App. tsx
│ ├── main.tsx
│ └── vite-env. d.ts
├── . eslintrc.cjs
├── tsconfig.json
├── vite.config.ts
└── package.json
核心代码示例:
// src/stores/userStore.ts - Zustand 状态管理
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface UserState {
token: string | null
userInfo: UserInfo | null
setToken: (token: string) => void
setUserInfo: (info: UserInfo) => void
logout: () => void
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
token: null,
userInfo: null,
setToken: (token) => set({ token }),
setUserInfo: (userInfo) => set({ userInfo }),
logout: () => set({ token: null, userInfo: null }),
}),
{
name: 'user-storage',
}
)
)
// src/api/client.ts - Axios + TanStack Query 配置
import axios from 'axios'
import { QueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { useUserStore } from '@/stores/userStore'
export const apiClient = axios.create({
baseURL: import.meta.env. VITE_API_URL,
timeout: 10000,
})
apiClient.interceptors. request.use((config) => {
const token = useUserStore. getState().token
if (token) {
config. headers.Authorization = `Bearer ${token}`
}
return config
})
apiClient.interceptors. response.use(
(response) => response. data,
(error) => {
message.error(error. response?.data?.message || '请求失败')
if (error.response?. status === 401) {
useUserStore.getState().logout()
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
staleTime: 5 * 60 * 1000, // 5 分钟
},
},
})
// src/api/user.ts - API 请求封装
import { apiClient } from './client'
export interface User {
id: number
username: string
email: string
status: 'active' | 'inactive'
}
export interface UserListParams {
page: number
pageSize: number
username?: string
status?: string
}
export const userApi = {
getList: (params: UserListParams) =>
apiClient. get<{ list: User[]; total: number }>('/users', { params }),
getById: (id: number) =>
apiClient.get<User>(`/users/${id}`),
create: (data: Omit<User, 'id'>) =>
apiClient.post<User>('/users', data),
update: (id: number, data: Partial<User>) =>
apiClient.put<User>(`/users/${id}`, data),
delete: (id: number) =>
apiClient.delete(`/users/${id}`),
}
// src/pages/User/List.tsx - 使用 TanStack Query 的列表页
import { useState } from 'react'
import { Table, Card, Form, Input, Select, Button, Space, Tag, message, Popconfirm } from 'antd'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { userApi, User, UserListParams } from '@/api/user'
export default function UserList() {
const queryClient = useQueryClient()
const [params, setParams] = useState<UserListParams>({ page: 1, pageSize: 10 })
// 查询用户列表
const { data, isLoading } = useQuery({
queryKey: ['users', params],
queryFn: () => userApi. getList(params),
})
// 删除用户
const deleteMutation = useMutation({
mutationFn: userApi.delete,
onSuccess: () => {
message.success('删除成功')
queryClient.invalidateQueries({ queryKey: ['users'] })
},
})
const columns = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '用户名', dataIndex: 'username' },
{ title: '邮箱', dataIndex: 'email' },
{
title: '状态',
dataIndex: 'status',
render: (status: string) => (
<Tag color={status === 'active' ? 'green' : 'red'}>
{status === 'active' ? '启用' : '禁用'}
</Tag>
),
},
{
title: '操作',
render: (_: any, record: User) => (
<Space>
<Button type="link">编辑</Button>
<Popconfirm
title="确定删除吗?"
onConfirm={() => deleteMutation. mutate(record. id)}
>
<Button type="link" danger>删除</Button>
</Popconfirm>
</Space>
),
},
]
const handleSearch = (values: any) => {
setParams({ ... params, ... values, page: 1 })
}
return (
<div style={{ padding: 24 }}>
<Card style={{ marginBottom: 16 }}>
<Form layout="inline" onFinish={handleSearch}>
<Form.Item name="username" label="用户名">
<Input placeholder="请输入用户名" />
</Form.Item>
<Form.Item name="status" label="状态">
<Select placeholder="请选择状态" style={{ width: 120 }} allowClear>
<Select.Option value="active">启用</Select.Option>
<Select. Option value="inactive">禁用</Select.Option>
</Select>
</Form.Item>
<Form. Item>
<Button type="primary" htmlType="submit">查询</Button>
</Form.Item>
</Form>
</Card>
<Card title="用户列表" extra={<Button type="primary">新增用户</Button>}>
<Table
rowKey="id"
columns={columns}
dataSource={data?.list}
loading={isLoading}
pagination={{
current: params.page,
pageSize: params.pageSize,
total: data?.total,
onChange: (page, pageSize) => setParams({ ...params, page, pageSize }),
}}
/>
</Card>
</div>
)
}
// src/App.tsx - 应用入口
import { QueryClientProvider } from '@tanstack/react-query'
import { ConfigProvider } from 'antd'
import zhCN from 'antd/locale/zh_CN'
import { RouterProvider } from 'react-router-dom'
import { queryClient } from '@/api/client'
import { router } from '@/router'
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<ConfigProvider locale={zhCN}>
<RouterProvider router={router} />
</ConfigProvider>
</QueryClientProvider>
)
}
🥈 组合二:React 现代潮流栈
定位:2024-2025 最火的技术组合,开发者体验极佳
┌─────────────────────────────────────────────────────────┐
│ React 现代潮流栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: React 18 │
│ 语言: TypeScript │
│ 构建工具: Vite │
│ 组件库: shadcn/ui │
│ 状态管理: Zustand / Jotai │
│ 路由: TanStack Router │
│ 数据请求: TanStack Query │
│ 表单处理: React Hook Form + Zod │
│ CSS 方案: Tailwind CSS │
│ 动画: Framer Motion │
│ 代码规范: Biome(新一代 ESLint 替代) │
│ │
│ 适用场景:追求 DX、现代设计、高度定制化的项目 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建项目
npm create vite@latest my-modern-app -- --template react-ts
cd my-modern-app
# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# 初始化 shadcn/ui
npx shadcn@latest init
# 选择配置
✔ Which style would you like to use? › New York
✔ Which color would you like to use as base color? › Neutral
✔ Do you want to use CSS variables for colors? › yes
# 添加组件
npx shadcn@latest add button input table card dialog
# 安装其他依赖
npm install zustand
npm install @tanstack/react-query
npm install @tanstack/react-router
npm install react-hook-form zod @hookform/resolvers
npm install framer-motion
使用示例:
// 使用 shadcn/ui 组件
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
export default function DashboardPage() {
return (
<div className="p-6 space-y-6">
{/* Tailwind 原子类 + shadcn 组件 */}
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold tracking-tight">仪表盘</h1>
<Dialog>
<DialogTrigger asChild>
<Button>新建项目</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>创建新项目</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<Input placeholder="项目名称" />
<Input placeholder="项目描述" />
<Button className="w-full">创建</Button>
</div>
</DialogContent>
</Dialog>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">总收入</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">¥45,231. 89</div>
<p className="text-xs text-muted-foreground">
较上月 +20.1%
</p>
</CardContent>
</Card>
{/* 更多卡片... */}
</div>
</div>
)
}
🥉 组合三:Next.js 全栈栈
定位:React 全栈开发的最佳选择
┌─────────────────────────────────────────────────────────┐
│ Next.js 全栈栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Next.js 15 (App Router) │
│ 语言: TypeScript │
│ 渲染模式: RSC / SSR / SSG / ISR │
│ 组件库: shadcn/ui │
│ 状态管理: Zustand(客户端) │
│ 数据请求: Server Actions / TanStack Query │
│ 表单处理: React Hook Form + Zod │
│ CSS 方案: Tailwind CSS │
│ ORM: Prisma / Drizzle │
│ 数据库: PostgreSQL / PlanetScale │
│ 认证: NextAuth. js / Clerk │
│ 部署: Vercel │
│ │
│ 适用场景:全栈应用、需要 SEO、SaaS 产品 │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建项目
npx create-next-app@latest my-saas-app
✔ Would you like to use TypeScript? Yes
✔ Would you like to use ESLint? Yes
✔ Would you like to use Tailwind CSS? Yes
✔ Would you like your code inside a `src/` directory? Yes
✔ Would you like to use App Router? Yes
✔ Would you like to use Turbopack? Yes
✔ Would you like to customize the import alias? No
cd my-saas-app
# 初始化 shadcn/ui
npx shadcn@latest init
# 安装数据库相关
npm install prisma @prisma/client
npx prisma init
# 安装认证
npm install next-auth
# 安装其他依赖
npm install zustand
npm install @tanstack/react-query
npm install react-hook-form zod @hookform/resolvers
项目结构:
my-saas-app/
├── prisma/
│ └── schema.prisma # 数据库模型
├── src/
│ ├── app/
│ │ ├── (auth)/ # 认证相关路由组
│ │ │ ├── login/
│ │ │ └── register/
│ │ ├── (dashboard)/ # 仪表盘路由组
│ │ │ ├── dashboard/
│ │ │ ├── settings/
│ │ │ └── layout. tsx
│ │ ├── api/ # API 路由
│ │ │ ├── auth/[...nextauth]/
│ │ │ └── users/
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── globals.css
│ ├── components/
│ │ ├── ui/ # shadcn/ui 组件
│ │ └── shared/ # 共享组件
│ ├── lib/
│ │ ├── prisma. ts # Prisma 客户端
│ │ ├── auth.ts # NextAuth 配置
│ │ └── utils.ts
│ ├── hooks/
│ └── types/
├── tailwind.config.ts
├── next.config.ts
└── package.json
Server Actions 示例:
// src/app/actions/user.ts
'use server'
import { prisma } from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
import { z } from 'zod'
const createUserSchema = z. object({
name: z.string(). min(1),
email: z.string().email(),
})
export async function createUser(formData: FormData) {
const data = createUserSchema.parse({
name: formData.get('name'),
email: formData.get('email'),
})
await prisma.user. create({ data })
revalidatePath('/dashboard/users')
}
export async function deleteUser(id: string) {
await prisma.user. delete({ where: { id } })
revalidatePath('/dashboard/users')
}
// src/app/(dashboard)/dashboard/users/page. tsx
import { prisma } from '@/lib/prisma'
import { Button } from '@/components/ui/button'
import { deleteUser } from '@/app/actions/user'
export default async function UsersPage() {
// 服务端直接查询数据库
const users = await prisma.user.findMany({
orderBy: { createdAt: 'desc' },
})
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6">用户管理</h1>
<div className="space-y-4">
{users. map((user) => (
<div key={user.id} className="flex items-center justify-between p-4 border rounded">
<div>
<p className="font-medium">{user.name}</p>
<p className="text-sm text-gray-500">{user.email}</p>
</div>
<form action={deleteUser. bind(null, user. id)}>
<Button variant="destructive" size="sm">删除</Button>
</form>
</div>
))}
</div>
</div>
)
}
🏅 组合四:React Native 移动端栈
定位:用 React 开发原生移动应用
┌─────────────────────────────────────────────────────────┐
│ React Native 移动端栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 框架: Expo (React Native) │
│ 语言: TypeScript │
│ 导航: Expo Router │
│ 组件库: Tamagui / NativeWind │
│ 状态管理: Zustand │
│ 数据请求: TanStack Query │
│ 本地存储: AsyncStorage / MMKV │
│ 动画: React Native Reanimated │
│ │
│ 适用场景:跨平台移动应用(iOS + Android) │
│ │
└─────────────────────────────────────────────────────────┘
安装命令:
# 创建 Expo 项目
npx create-expo-app my-mobile-app --template tabs
cd my-mobile-app
# 安装依赖
npm install zustand
npm install @tanstack/react-query
npm install nativewind
npm install -D tailwindcss
React 技术栈对比表
|----------------|--------|------|-----------|
| 组合 | 适用场景 | 难度 | 特点 |
| 企业级标准栈 | 中后台管理 | ⭐⭐ | 最稳定,企业首选 |
| 现代潮流栈 | 追求 DX | ⭐⭐⭐ | 最时髦,开发体验佳 |
| Next.js 全栈栈 | 全栈/SEO | ⭐⭐⭐ | 功能最全,一站式 |
| React Native 栈 | 移动端 | ⭐⭐⭐⭐ | 跨平台移动开发 |
第四部分:技术选型速查表
按场景选择
┌─────────────────────────────────────────────────────────┐
│ 按场景选择技术栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 🏢 企业中后台管理系统 │
│ Vue:Vue 3 + Element Plus + Pinia + Vite │
│ React:React + Ant Design + Zustand + Vite │
│ │
│ 🌐 需要 SEO 的网站 │
│ Vue:Nuxt 3 + Tailwind CSS │
│ React:Next.js + shadcn/ui │
│ │
│ 📱 移动端 H5 │
│ Vue:Vue 3 + Vant + Pinia │
│ React:React + Ant Design Mobile │
│ │
│ 📲 原生移动应用 │
│ React Native + Expo + Zustand │
│ Flutter(非 JS 技术栈,但值得考虑) │
│ │
│ 🖥️ 桌面应用 │
│ Electron + React/Vue + 对应组件库 │
│ Tauri + React/Vue(更轻量) │
│ │
│ 📝 博客/文档站 │
│ Astro + Tailwind CSS │
│ VitePress(Vue 技术栈) │
│ Nextra(Next.js 技术栈) │
│ │
│ 🚀 快速原型/MVP │
│ Next.js + shadcn/ui + Prisma + Vercel │
│ Nuxt 3 + Nuxt UI + Supabase │
│ │
└─────────────────────────────────────────────────────────┘
技术栈搭配原则
┌─────────────────────────────────────────────────────────┐
│ 搭配原则 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 框架与组件库要匹配 │
│ React → Ant Design / MUI / shadcn │
│ Vue → Element Plus / Arco / Naive │
│ 不要混搭! │
│ │
│ 2️⃣ 构建工具优先选 Vite │
│ 2025 年了,别再用 Webpack 起新项目了 │
│ 除非你有特殊需求 │
│ │
│ 3️⃣ 状态管理从简 │
│ 小项目:组件状态 + Context 足够 │
│ 中项目:Zustand / Pinia │
│ 大项目:再考虑 Redux Toolkit │
│ │
│ 4️⃣ TypeScript 是标配 │
│ 2025 年了,新项目必须用 TypeScript │
│ 类型安全能避免大量 bug │
│ │
│ 5️⃣ CSS 方案看团队 │
│ 有设计师:CSS Modules / Sass │
│ 无设计师:Tailwind CSS + 组件库 │
│ │
│ 6️⃣ 数据请求用 TanStack Query │
│ 自动缓存、重试、加载状态 │
│ 比裸用 Axios 好太多 │
│ │
└─────────────────────────────────────────────────────────┘
第五部分:2025 年技术趋势
趋势一:全栈框架统一天下
┌─────────────────────────────────────────────────────────┐
│ 全栈框架的崛起 │
├─────────────────────────────────────────────────────────┤
│ │
│ 以前:前端是前端,后端是后端 │
│ │
│ 现在:Next.js / Nuxt / SvelteKit 一把梭 │
│ │
│ • Server Components:服务端组件 │
│ • Server Actions:服务端函数调用 │
│ • Edge Runtime:边缘计算 │
│ • Streaming SSR:流式服务端渲染 │
│ │
│ 趋势:前后端界限越来越模糊 │
│ │
└─────────────────────────────────────────────────────────┘
趋势二:AI 融入开发流程
┌─────────────────────────────────────────────────────────┐
│ AI 辅助开发 │
├─────────────────────────────────────────────────────────┤
│ │
│ 代码生成: │
│ • GitHub Copilot:AI 结对编程 │
│ • Cursor:AI-first 编辑器 │
│ • v0.dev:AI 生成 UI 组件 │
│ │
│ 设计转代码: │
│ • Figma → Code 插件 │
│ • Screenshot to Code │
│ │
│ 趋势:AI 成为开发标配工具 │
│ │
└─────────────────────────────────────────────────────────┘
趋势三:性能优化工具链升级
┌─────────────────────────────────────────────────────────┐
│ 性能优化新工具 │
├─────────────────────────────────────────────────────────┤
│ │
│ 构建工具: │
│ • Turbopack:Next.js 新一代打包器 │
│ • Rspack:Rust 版 Webpack(字节开源) │
│ • Oxc:Rust 工具链(解析、转译、压缩) │
│ │
│ 运行时: │
│ • Bun:更快的 JS 运行时 │
│ • Deno 2. 0:兼容 npm 的安全运行时 │
│ │
│ 趋势:Rust 重写 JS 工具链 │
│ │
└─────────────────────────────────────────────────────────┘
趋势四:类型安全全链路
┌─────────────────────────────────────────────────────────┐
│ 全链路类型安全 │
├─────────────────────────────────────────────────────────┤
│ │
│ 前端 ←→ 后端类型共享: │
│ • tRPC:端到端类型安全 │
│ • Zodios:基于 Zod 的类型安全 HTTP 客户端 │
│ • ts-rest:类型安全的 REST API │
│ │
│ 数据库类型安全: │
│ • Prisma:类型安全的 ORM │
│ • Drizzle:轻量级类型安全 ORM │
│ • Kysely:类型安全的 SQL 查询构建器 │
│ │
│ 趋势:从前端到数据库,全链路 TypeScript │
│ │
└─────────────────────────────────────────────────────────┘
总结
新手推荐路线
┌─────────────────────────────────────────────────────────┐
│ 新手推荐 │
├─────────────────────────────────────────────────────────┤
│ │
│ 如果你选 Vue: │
│ Vue 3 + TypeScript + Vite + Element Plus + Pinia │
│ │
│ 如果你选 React: │
│ React + TypeScript + Vite + Ant Design + Zustand │
│ │
│ 如果你要全栈: │
│ Next.js + TypeScript + Tailwind + shadcn/ui + Prisma │
│ │
│ 核心原则: │
│ • 选主流的,遇到问题容易找答案 │
│ • 选简单的,先跑起来再说 │
│ • 选有大厂背书的,长期维护有保障 │
│ │
└─────────────────────────────────────────────────────────┘
技术栈一句话总结
|--------------------|----------------|
| 技术栈 | 一句话描述 |
| Vue + Element Plus | 国内中后台管理的事实标准 |
| Vue + Naive UI | 追求独特设计的个人/开源项目 |
| Vue + Arco Design | 现代化企业级应用 |
| Nuxt 3 | Vue 全栈开发首选 |
| React + Ant Design | 大厂中后台首选 |
| React + shadcn/ui | 追求极致定制和 DX |
| Next.js + Prisma | React 全栈开发王者 |
| Astro | 内容驱动网站的性能之王 |
最后的话
┌─────────────────────────────────────────────────────────┐
│ │
│ 技术栈没有最好的,只有最适合的。 │
│ │
│ • 团队会什么,就用什么 │
│ • 项目需要什么,就选什么 │
│ • 不要追新,要追稳 │
│ │
│ 记住:技术是为业务服务的,不是为了炫技。 │
│ │
│ 选定一个技术栈,深入学习,比什么都会一点强。 │
│ │
└─────────────────────────────────────────────────────────┘
现在,选择你的技术栈,开始构建吧!🚀
最后更新:2025 年 12 月