vue项目搭建
-
- [🚀 第 1 & 2 步:初始化工程与依赖安装 (终端执行)](#🚀 第 1 & 2 步:初始化工程与依赖安装 (终端执行))
- [`第 3 步:核心构建配置 (Vite & TS)`](#
第 3 步:核心构建配置 (Vite & TS)) -
- `vite.config.ts`
- [` tsconfig.json (打通 @ 别名):`](#
tsconfig.json (打通 @ 别名):)
- [第 4 步:定义全局泛型契约 (Types)](#第 4 步:定义全局泛型契约 (Types))
-
- [` src/types/global.d.ts`](#
src/types/global.d.ts)
- [` src/types/global.d.ts`](#
- [📡 第 5 步:C 端专属的 Axios 请求总线 (Utils)](#📡 第 5 步:C 端专属的 Axios 请求总线 (Utils))
-
- [`请创建 src/utils/request.ts:`](#
请创建 src/utils/request.ts:)
- [`请创建 src/utils/request.ts:`](#
🚀 第 1 & 2 步:初始化工程与依赖安装 (终端执行)
bash
# 1. 创建 C端门户工程
npm create vite@latest blog-portal -- --template vue-ts
# 2. 进入工程目录
cd blog-portal
# 3. 安装默认依赖
npm install
# 4. 安装运行时四大件与图标库
npm install vue-router pinia axios element-plus @element-plus/icons-vue
# 5. 安装工程化自动化构建神仙插件
npm install -D unplugin-vue-components unplugin-auto-import @types/node sass
第 3 步:核心构建配置 (Vite & TS)
bash
端口隔离:Admin 前端占用了 5173,为了让你能同时启动前后台两个项目联调,我们必须把 Portal 的端口改成 5174。
代理指向:Admin 代理到 8080,Portal 必须代理到你的 C 端后端(假设是 blog-server 的 8081 或 blog-web 的 8082,这里以 8081 为例)。
vite.config.ts
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports:['vue', 'vue-router', 'pinia'],
resolvers: [ElementPlusResolver()],
dts: 'src/types/auto-imports.d.ts',
}),
Components({
resolvers:[ElementPlusResolver()],
dts: 'src/types/components.d.ts',
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
port: 5174, // ✨ 架构师细节:与 Admin 的 5173 错开,允许双开!
open: true,
proxy: {
'/api': {
// ✨ 架构师细节:指向你的 C 端服务端口 (blog-server 或 blog-web)
target: 'http://localhost:8081',
changeOrigin: true,
// 注意:如果你的后端 Controller 上写了 @RequestMapping("/api/xxx"),这里就不写 rewrite
// 如果后端没有 /api 前缀,请把下面这行注释打开:
// rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
tsconfig.json (打通 @ 别名):
typescript
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib":["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* ✨ 让 TS 认识 @ 符号指向 src */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
第 4 步:定义全局泛型契约 (Types)
src/types/global.d.ts
清空或创建 src/types/global.d.ts,这是前后端唯一的数据灵魂纽带。
typescript
// src/types/global.d.ts
/**
* 对应后端 Java 的 ApiResponse<T> 统一返回实体
*/
declare interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
/**
* 分页数据标准格式 (对应后端的 PageResponse)
*/
declare interface PageData<T> {
total: number;
records: T[];
}
📡 第 5 步:C 端专属的 Axios 请求总线 (Utils)
C 端的 401 柔性处理 在后台管理中,没登录(401)是绝对的死罪,直接 window.location.href = '/login'
踢飞。
但在博客门户中,用户正看着长篇大论的文章,突然 Token 过期了,你如果把他强行踢到另一个页面,他的阅读进度就全毁了!
因此,C
端的 request.ts 拦截器更加"温柔":只清空 Token,弹个警告,然后抛出异常让组件自己决定要不要弹出一个"登录模态框"。
请创建 src/utils/request.ts:
typescript
// src/utils/request.ts
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'
// 1. 创建 Axios 实例
const service: AxiosInstance = axios.create({
baseURL: '/api', // 走 Vite 代理
timeout: 10000,
})
// 2. 请求拦截器 (携带 Token)
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
(error: AxiosError) => Promise.reject(error)
)
// 3. 响应拦截器 (✨ C端专属柔性处理)
service.interceptors.response.use(
(response: AxiosResponse<ApiResponse>) => {
const res = response.data
// 假设后端 200 (或 0) 代表成功,请根据你的后端 ApiResponse 实际情况调整
if (res.code === 200) {
// 核心剥离:把核心数据直接交给前端业务组件
return res.data as any
} else {
// 💥 业务异常处理
ElMessage.error(res.message || '业务异常')
// ✨ C端特有的 401 处理:只清空状态,不强制刷新页面!
if (res.code === 401) {
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
// 架构提示:这里可以触发一个全局事件(EventBus),让外层的 Layout 弹出一个漂亮的登录 Dialog
ElMessage.warning('登录已过期,请重新登录以进行互动')
}
return Promise.reject(new Error(res.message || 'Error'))
}
},
(error: AxiosError) => {
// 拦截 404/500/502 等 HTTP 网络异常
let msg = '网络未知异常'
if (error.response) {
const status = error.response.status
switch (status) {
case 404: msg = '请求的接口不存在 (404)'; break;
case 500: msg = '服务器内部错误 (500)'; break;
case 502: msg = '网关错误,后端服务暂未启动 (502)'; break;
default: msg = `请求失败 (${status})`;
}
} else if (error.message.includes('timeout')) {
msg = '网络请求超时'
}
ElMessage.error(msg)
return Promise.reject(error)
}
)
export default service