前端快速开始
快速了解前端项目结构和开发流程。
项目结构
vue-vben-admin/
├── apps/ # 应用目录
│ ├── web-antd/ # Ant Design 版本
│ ├── web-ele/ # Element Plus 版本
│ ├── web-naive/ # Naive UI 版本
│ └── backend-mock/ # Mock 后端
├── packages/ # 公共包
│ ├── @core/ # 核心包
│ ├── effects/ # 副作用包
│ ├── utils/ # 工具包
│ └── ...
└── pnpm-workspace.yaml # Pnpm 工作区配置技术栈
- Vue 3 - 渐进式 JavaScript 框架
- TypeScript 5 - JavaScript 的超集
- Vite 5 - 下一代前端构建工具
- Pinia 2 - Vue 状态管理
- Vue Router 4 - Vue 路由
- Ant Design Vue 4 - UI 组件库
- Axios - HTTP 客户端
- ECharts 5 - 数据可视化
- Tailwind CSS - 原子化 CSS 框架
快速开始
1. 环境准备
确保已安装:
- Node.js >= 18.0
- pnpm >= 8.0
bash
# 安装 pnpm
npm i -g corepack
corepack enable2. 安装依赖
bash
cd vue-vben-admin
pnpm install3. 启动开发服务器
bash
# 启动 Ant Design 版本
pnpm dev:antd
# 或使用通用命令
pnpm dev4. 构建生产版本
bash
# 构建 Ant Design 版本
pnpm build:antd
# 构建所有版本
pnpm build开发流程
1. 创建页面
在 apps/web-antd/src/views 目录下创建页面:
vue
<!-- src/views/user/list.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getUserList } from '@/api/user'
import type { User } from '@/types/user'
const users = ref<User[]>([])
const loading = ref(false)
const fetchUsers = async () => {
loading.value = true
try {
const { data } = await getUserList()
users.value = data
} finally {
loading.value = false
}
}
onMounted(() => {
fetchUsers()
})
</script>
<template>
<div class="user-list">
<a-table
:columns="columns"
:data-source="users"
:loading="loading"
/>
</div>
</template>
<style scoped>
.user-list {
padding: 20px;
}
</style>2. 配置路由
在 apps/web-antd/src/router/modules 目录下创建路由:
typescript
// src/router/modules/user.ts
import type { RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/user',
name: 'User',
component: () => import('@/layouts/default/index.vue'),
meta: {
title: '用户管理',
icon: 'user',
},
children: [
{
path: 'list',
name: 'UserList',
component: () => import('@/views/user/list.vue'),
meta: {
title: '用户列表',
},
},
],
},
]
export default routes3. 创建 API
在 apps/web-antd/src/api 目录下创建 API:
typescript
// src/api/user.ts
import { request } from '@/utils/request'
import type { User } from '@/types/user'
export interface UserListParams {
page?: number
size?: number
keyword?: string
}
export interface UserListResult {
list: User[]
total: number
}
// 获取用户列表
export function getUserList(params?: UserListParams) {
return request.get<UserListResult>('/api/system/user/list', { params })
}
// 获取用户详情
export function getUserById(id: number) {
return request.get<User>(`/api/system/user/${id}`)
}
// 新增用户
export function createUser(data: Partial<User>) {
return request.post('/api/system/user', data)
}
// 更新用户
export function updateUser(data: User) {
return request.put('/api/system/user', data)
}
// 删除用户
export function deleteUser(id: number) {
return request.delete(`/api/system/user/${id}`)
}4. 定义类型
在 apps/web-antd/src/types 目录下定义类型:
typescript
// src/types/user.ts
export interface User {
id: number
username: string
nickname: string
phone: string
email: string
status: number
createTime: string
updateTime: string
}
export interface UserForm {
username: string
password?: string
nickname: string
phone: string
email: string
status: number
}5. 状态管理
在 apps/web-antd/src/store/modules 目录下创建 Store:
typescript
// src/store/modules/user.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { User } from '@/types/user'
import { getUserInfo } from '@/api/user'
export const useUserStore = defineStore('user', () => {
const userInfo = ref<User | null>(null)
const token = ref<string>('')
// 获取用户信息
const fetchUserInfo = async () => {
const { data } = await getUserInfo()
userInfo.value = data
}
// 设置 Token
const setToken = (newToken: string) => {
token.value = newToken
}
// 清除用户信息
const clearUserInfo = () => {
userInfo.value = null
token.value = ''
}
return {
userInfo,
token,
fetchUserInfo,
setToken,
clearUserInfo,
}
})常用功能
表格组件
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { TableColumn } from 'ant-design-vue'
const columns: TableColumn[] = [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '用户名', dataIndex: 'username' },
{ title: '昵称', dataIndex: 'nickname' },
{ title: '手机号', dataIndex: 'phone' },
{ title: '邮箱', dataIndex: 'email' },
{ title: '状态', dataIndex: 'status', width: 100 },
{ title: '操作', key: 'action', width: 200 },
]
const dataSource = ref([])
</script>
<template>
<a-table
:columns="columns"
:data-source="dataSource"
:pagination="{ pageSize: 10 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
<a-button type="link" danger @click="handleDelete(record)">删除</a-button>
</a-space>
</template>
</template>
</a-table>
</template>表单组件
vue
<script setup lang="ts">
import { ref, reactive } from 'vue'
import type { FormInstance } from 'ant-design-vue'
const formRef = ref<FormInstance>()
const formData = reactive({
username: '',
password: '',
nickname: '',
phone: '',
email: '',
})
const rules = {
username: [{ required: true, message: '请输入用户名' }],
password: [{ required: true, message: '请输入密码' }],
phone: [{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' }],
email: [{ type: 'email', message: '邮箱格式不正确' }],
}
const handleSubmit = async () => {
await formRef.value?.validate()
// 提交表单
}
</script>
<template>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
<a-form-item label="用户名" name="username">
<a-input v-model:value="formData.username" />
</a-form-item>
<a-form-item label="密码" name="password">
<a-input-password v-model:value="formData.password" />
</a-form-item>
<a-form-item label="昵称" name="nickname">
<a-input v-model:value="formData.nickname" />
</a-form-item>
<a-form-item label="手机号" name="phone">
<a-input v-model:value="formData.phone" />
</a-form-item>
<a-form-item label="邮箱" name="email">
<a-input v-model:value="formData.email" />
</a-form-item>
<a-form-item :wrapper-col="{ offset: 6 }">
<a-space>
<a-button type="primary" @click="handleSubmit">提交</a-button>
<a-button @click="formRef?.resetFields()">重置</a-button>
</a-space>
</a-form-item>
</a-form>
</template>弹窗组件
vue
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
const formData = ref({})
const showModal = (record?: any) => {
if (record) {
formData.value = { ...record }
}
visible.value = true
}
const handleOk = async () => {
// 提交表单
visible.value = false
}
defineExpose({ showModal })
</script>
<template>
<a-modal
v-model:open="visible"
title="用户信息"
@ok="handleOk"
>
<!-- 表单内容 -->
</a-modal>
</template>请求封装
typescript
// src/utils/request.ts
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { message } from 'ant-design-vue'
import { useUserStore } from '@/store/modules/user'
const instance: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
})
// 请求拦截器
instance.interceptors.request.use(
(config) => {
const userStore = useUserStore()
if (userStore.token) {
config.headers.Authorization = userStore.token
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
instance.interceptors.response.use(
(response: AxiosResponse) => {
const { code, data, message: msg } = response.data
if (code === 200) {
return data
} else {
message.error(msg || '请求失败')
return Promise.reject(new Error(msg))
}
},
(error) => {
if (error.response?.status === 401) {
// 未登录,跳转到登录页
const userStore = useUserStore()
userStore.clearUserInfo()
window.location.href = '/login'
} else {
message.error(error.message || '网络错误')
}
return Promise.reject(error)
}
)
export const request = instance环境变量
.env
env
# 应用名称
VITE_APP_TITLE=Vben Admin
# 应用描述
VITE_APP_DESC=企业级管理系统框架.env.development
env
# API 地址
VITE_API_URL=http://localhost:8080
# 是否开启 Mock
VITE_USE_MOCK=false.env.production
env
# API 地址
VITE_API_URL=https://api.example.com
# 是否开启 Mock
VITE_USE_MOCK=false常用命令
bash
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 构建生产版本
pnpm build
# 预览构建结果
pnpm preview
# 代码检查
pnpm lint
# 代码格式化
pnpm format
# 类型检查
pnpm type-check开发建议
最佳实践
- 使用 TypeScript 编写代码
- 使用 Composition API
- 合理拆分组件
- 使用 Pinia 管理状态
- 使用 Vue Router 管理路由
- 使用 Axios 封装请求
- 使用 ESLint 和 Prettier
注意事项
- 避免在 setup 中使用 this
- 合理使用 ref 和 reactive
- 注意内存泄漏问题
- 避免过度优化
- 注意 SEO 优化
调试技巧
Vue DevTools
安装 Vue DevTools 浏览器插件,可以:
- 查看组件树
- 查看组件状态
- 查看路由信息
- 查看 Pinia 状态
- 性能分析
控制台调试
typescript
// 开发环境下打印日志
if (import.meta.env.DEV) {
console.log('用户信息:', userInfo)
}网络调试
使用浏览器开发者工具的 Network 标签:
- 查看请求和响应
- 检查请求头和响应头
- 查看请求耗时
- 模拟网络状况