Vue 3 项目结构详细讲解
·
一、项目整体结构概览
二、完整项目结构树
my-vue3-project/
│
├── 📁 node_modules/ # 项目依赖包(自动生成)
│
├── 📁 public/ # 静态资源目录
│ ├── 📄 favicon.ico # 网站图标
│ └── 📄 robots.txt # 搜索引擎配置
│
├── 📁 src/ # 源代码目录
│ ├── 📁 assets/ # 资源文件
│ │ ├── 📁 images/ # 图片资源
│ │ ├── 📁 styles/ # 样式文件
│ │ └── 📁 fonts/ # 字体文件
│ │
│ ├── 📁 components/ # 公共组件
│ │ ├── 📁 common/ # 通用组件
│ │ └── 📁 layout/ # 布局组件
│ │
│ ├── 📁 views/ # 页面组件
│ │ ├── 📄 Home.vue # 首页
│ │ ├── 📄 About.vue # 关于页
│ │ └── 📄 Login.vue # 登录页
│ │
│ ├── 📁 router/ # 路由配置
│ │ └── 📄 index.js # 路由主文件
│ │
│ ├── 📁 store/ # 状态管理(Pinia)
│ │ ├── 📄 index.js # store 主文件
│ │ └── 📁 modules/ # 模块化 store
│ │
│ ├── 📁 utils/ # 工具函数
│ │ ├── 📄 request.js # axios 封装
│ │ └── 📄 common.js # 通用工具
│ │
│ ├── 📁 api/ # API 接口
│ │ ├── 📄 user.js # 用户相关接口
│ │ └── 📄 product.js # 产品相关接口
│ │
│ ├── 📄 App.vue # 根组件
│ └── 📄 main.js # 应用入口
│
├── 📁 tests/ # 测试文件
│ ├── 📁 unit/ # 单元测试
│ └── 📁 e2e/ # 端到端测试
│
├── 📄 .env # 环境变量
├── 📄 .env.development # 开发环境变量
├── 📄 .env.production # 生产环境变量
├── 📄 .gitignore # Git 忽略文件
├── 📄 index.html # HTML 模板
├── 📄 package.json # 项目配置
├── 📄 README.md # 项目说明
└── 📄 vite.config.js # Vite 配置
三、核心文件详解
3.1 入口文件 (index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 应用</title>
</head>
<body>
<!-- Vue 应用挂载点 -->
<div id="app"></div>
<!-- Vite 会自动注入 main.js -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
3.2 应用入口 (main.js)
// main.js - 应用程序的入口文件
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import router from './router'
import App from './App.vue'
// 引入全局样式
import './assets/styles/main.css'
// 创建 Vue 应用实例
const app = createApp(App)
// 使用 Pinia 状态管理
app.use(createPinia())
// 使用路由
app.use(router)
// 全局组件注册示例
// app.component('GlobalComponent', GlobalComponent)
// 全局属性注册示例
// app.config.globalProperties.$http = axios
// 挂载应用到 DOM
app.mount('#app')
3.3 主要目录功能流程
四、src 目录详细说明
4.1 组件目录结构
组件示例结构:
<!-- components/common/Button.vue -->
<template>
<button
:class="['btn', `btn-${type}`, { 'btn-disabled': disabled }]"
:disabled="disabled"
@click="handleClick"
>
<slot></slot>
</button>
</template>
<script setup>
// 使用 Composition API
import { defineProps, defineEmits } from 'vue'
// 定义属性
const props = defineProps({
type: {
type: String,
default: 'primary',
validator: (value) => ['primary', 'success', 'warning', 'danger'].includes(value)
},
disabled: {
type: Boolean,
default: false
}
})
// 定义事件
const emit = defineEmits(['click'])
// 处理点击
const handleClick = (e) => {
if (!props.disabled) {
emit('click', e)
}
}
</script>
<style scoped>
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background-color: #1890ff;
color: white;
}
.btn-disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>
4.2 路由配置结构
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '@/components/layout/Layout.vue'
const routes = [
{
path: '/',
component: Layout,
redirect: '/home',
children: [
{
path: 'home',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: { title: '首页', icon: 'home' }
},
{
path: 'about',
name: 'About',
component: () => import('@/views/About.vue'),
meta: { title: '关于', icon: 'info' }
}
]
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
meta: { title: '登录', hideHeader: true }
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/404.vue')
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
// 路由守卫
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = to.meta.title || 'Vue 3 App'
// 权限验证逻辑
const token = localStorage.getItem('token')
if (to.path !== '/login' && !token) {
next('/login')
} else {
next()
}
})
export default router
4.3 状态管理 (Pinia)
// store/modules/user.js
import { defineStore } from 'pinia'
import { login, getUserInfo } from '@/api/user'
export const useUserStore = defineStore('user', {
// 状态
state: () => ({
token: localStorage.getItem('token') || '',
userInfo: null,
permissions: []
}),
// 计算属性
getters: {
isLoggedIn: (state) => !!state.token,
username: (state) => state.userInfo?.username || '',
hasPermission: (state) => (permission) => {
return state.permissions.includes(permission)
}
},
// 方法
actions: {
// 登录
async login(loginForm) {
try {
const { data } = await login(loginForm)
this.token = data.token
localStorage.setItem('token', data.token)
await this.getUserInfo()
return { success: true }
} catch (error) {
return { success: false, message: error.message }
}
},
// 获取用户信息
async getUserInfo() {
try {
const { data } = await getUserInfo()
this.userInfo = data.userInfo
this.permissions = data.permissions
} catch (error) {
console.error('获取用户信息失败', error)
}
},
// 登出
logout() {
this.token = ''
this.userInfo = null
this.permissions = []
localStorage.removeItem('token')
}
}
})
4.4 API 接口管理
// utils/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 创建 axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000
})
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
error => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
ElMessage.error(res.message || '请求失败')
return Promise.reject(new Error(res.message || 'Error'))
}
return res
},
error => {
console.error('响应错误:', error)
ElMessage.error(error.message || '网络错误')
return Promise.reject(error)
}
)
export default service
// api/user.js
import request from '@/utils/request'
// 用户登录
export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}
// 获取用户信息
export function getUserInfo() {
return request({
url: '/user/info',
method: 'get'
})
}
// 更新用户信息
export function updateUserInfo(data) {
return request({
url: '/user/update',
method: 'put',
data
})
}
五、配置文件详解
5.1 Vite 配置
// vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: 3000,
host: true,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
build: {
target: 'es2015',
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'ui-vendor': ['element-plus']
}
}
}
}
})
5.2 环境变量配置
# .env
VITE_APP_TITLE=My Vue 3 App
# .env.development
VITE_API_BASE_URL=http://localhost:8080/api
VITE_APP_ENV=development
# .env.production
VITE_API_BASE_URL=https://api.example.com
VITE_APP_ENV=production
更多推荐
所有评论(0)