vue3 vite + js项目搭建全流程(axios,router,pinia,echarts,sass,element-plus部署)
Gitee仓库:示例代码
一、项目初始化
npm create vite@latest my-vue-project -- --template vue
cd my-vue-project
npm install
二、依赖安装
npm install axios
npm install vue-router@4
npm install pinia
npm install pinia pinia-plugin-persistedstate
npm install -D sass-embedded
npm install echarts --save
npm install element-plus --save
npm install @element-plus/icons-vue
- 在
main.js
中挂载
import { createApp } from 'vue'
import App from './App.vue'
/* 全局样式 */
import './style.css'
/* 字体 */
import '@/assets/fonts/font.css';
/* 路由 */
import router from './router'
/* 状态管理 */
import { createPinia } from 'pinia'
/* pinia持久化存储 */
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
/* ElementPlus */
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
/* ElementPlus-图标 */
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(pinia)
app.use(router)
app.use(ElementPlus)
app.mount('#app')
三、项目结构建议
# 项目根目录
public/ # 静态资源目录(不会被 webpack 处理,直接复制到打包目录)
├── favicon.ico # 网站图标文件
src/ # 主开发目录
├── api/ # API 请求模块目录(存放所有接口请求文件)
├── assets/ # 静态资源目录(会被 webpack 处理)
│ ├── fonts/ # 字体文件目录
│ │ ├── font.css # 字体样式定义文件
│ │ └── typeface.ttf # 字体文件
│ ├── icons/ # SVG 图标资源目录
│ ├── images/ # 图片资源目录
│ ├── logo/ # 网站 LOGO 文件目录
│ ├── music/ # 音频文件目录
│ └── styles/ # 全局样式文件目录(可存放 scss/less/css 文件)
│
├── components/ # 公共组件目录(可复用的 Vue 组件)
├── router/ # 路由配置目录(Vue Router 配置文件)
├── stores/ # 状态管理目录(Pinia/Vuex 状态管理配置)
├── utils/ # 工具函数目录(公共方法、工具类文件)
├── views/ # 页面视图目录(路由页面组件)
│ ├── 404/ # 404 页面目录
│ │ └── index.vue
│ ├── home/ # 首页目录
│ │ └── index.vue
│ ├── index.vue # 默认入口页面
│ └── login.vue # 登录页面组件
│
├── App.vue # Vue 应用根组件
└── main.js # 应用入口文件(初始化 Vue 实例)
# 环境变量文件
.env.development # 开发环境变量配置(本地开发使用)
.env.production # 生产环境变量配置(正式部署使用)
四、环境变量
开发环境配置
.env.development
# 页面标题 VITE_APP_TITLE = 个人网页 # 开发环境配置 VITE_APP_ENV = 'development' # 开发环境地址 VITE_APP_BASE_API = '/dev-api' # websocket 开关 默认使用sse推送 VITE_APP_WEBSOCKET = false
生产环境配置
.env.production
# 页面标题 VITE_APP_TITLE = 个人网页 # 生产环境配置 VITE_APP_ENV = 'production' # 生产环境地址 VITE_APP_BASE_API = '/prod-api' # websocket 开关 默认使用sse推送 VITE_APP_WEBSOCKET = false
五、重构核心配置
- 配置路径别名&跨域处理
vite.config.js
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue'
import path from 'path' // 引入 path 模块
export default defineConfig(({ mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd(), '');
return {
plugins: [vue()],
resolve: {
alias: {
'~': path.resolve(__dirname, './'),
'@': path.resolve(__dirname, './src') // 将 @ 指向 src 目录
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
server: {
host: '0.0.0.0', // 监听所有可用网络接口
open: true, // 启动开发服务器时自动打开默认浏览器
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://localhost:8080', // 后端地址
changeOrigin: true,
ws: true, // 启用 WebSocket 代理
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), ''),
secure: false // 如果使用 HTTPS 需要设为 true
}
}
}
}
})
六、基础文件配置
清除全局边距
index.html
* { padding: 0; margin: 0; }
设置路由出口
App.vue
<template> <div id="app"> <!-- 路由出口 --> <router-view /> </div> </template> <style scoped> * { margin: 0; padding: 0; } </style>
首页配置
views/index.vue
<template> <div class="common-layout"> <el-container> <!-- 导航栏 --> <el-header> <el-menu :default-active="$route.path" class="el-menu-demo" mode="horizontal" :ellipsis="false" router> <el-menu-item index="/">HOME</el-menu-item> <el-menu-item index="/404">404</el-menu-item> </el-menu> </el-header> <!-- 主体 --> <el-main> <RouterView /> </el-main> </el-container> </div> </template> <script setup></script> <style lang="scss" scoped> .el-header, .el-main { margin: 0; padding: 0; } .el-main { height: calc(100vh - 60px); overflow: hidden; position: relative; } .common-layout { margin: 0; padding: 0; width: 100%; height: 100vh; box-sizing: border-box; /* 固定页面 */ position: fixed; } </style>
七、Vue Router 配置
创建路由管理文件
src/router/index.js
import { createRouter, createWebHistory } from "vue-router"; import { constantRoute } from "./routes"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: constantRoute, // 滚动行为 scrollBehavior() { return { left: 0, top: 0 } } }); export default router;
创建路由文件
src/router/routes.js
// 对外暴露配置路由(常量路由) export const constantRoute = [ { // 主页 path: "/", name: "home", component: () => import("@/views/index.vue"), children: [ { path: '/home', redirect: '/' // 重定向到默认子路由 }, { path: '', // 首页(默认) name: 'HOME', component: () => import("@/views/home/index.vue") } ] }, { // 404 path: "/404", name: "404", component: () => import("@/views/404/index.vue"), }, { // 任意路由(若进入未知路由则跳转该路由) path: "/:pathMatch(.*)*", name: "Any", redirect: "/404", }, ];
八、Pinia 状态管理
创建 store 实例
src/stores/user.js
import { defineStore } from 'pinia' /* 示例 用户数据存储 */ export const useUserStore = defineStore('user', { state: () => ({ token: '', username: 'Guest', isLoggedIn: false }), actions: { login(token, username) { this.token = token this.username = username this.isLoggedIn = true }, logout() { this.token = '' this.username = 'Guest' this.isLoggedIn = false } }, persist: { enabled: true, // 开启持久化 strategies: [ { key: 'userData', // 自定义存储 key(默认使用 store id) storage: localStorage, // 指定存储方式(默认 sessionStorage) paths: ['token', 'isLoggedIn'] // 仅持久化 token 和 isLoggedIn } ] } })
在 Vue 组件中操作 Store
<script setup> import { useUserStore } from '@/stores/user' import { storeToRefs } from 'pinia' const userStore = useUserStore() const { token, username, isLoggedIn } = storeToRefs(userStore) // 使用 storeToRefs 保持响应性 const handleLogin = () => { userStore.login('abc123', 'JohnDoe') // 调用 Action } const handleLogout = () => { userStore.logout() } </script> <template> <div v-if="isLoggedIn"> <p>Welcome, {{ username }} (Token: {{ token }})</p> <button @click="handleLogout">Logout</button> </div> <div v-else> <button @click="handleLogin">Login</button> </div> </template>
九、Axios 封装
创建拦截器
src/utils/request.js
import axios from "axios"; // 创建 axios 实例 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, // 通过环境变量配置 timeout: 50000, headers: { "Content-Type": "application/json" } }); // 请求拦截器 service.interceptors.request.use( config => { // 携带 Token 的逻辑(示例) const token = localStorage.getItem("access_token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => Promise.reject(error) ); // 响应拦截器 service.interceptors.response.use( response => { // 处理 2xx 状态码 const { code, message } = response.data; if (code === 200) { return response.data; // 直接返回核心数据 } else { return Promise.reject(new Error(message || "请求异常")); } }, error => { // 处理非 2xx 状态码 const status = error.response?.status; switch (status) { case 401: console.error("登录过期,请重新登录"); router.replace("/login"); break; case 403: console.error("没有权限访问"); break; case 500: console.error("服务器内部错误"); break; default: console.error("网络异常,请稍后重试"); } return Promise.reject(error); } ); export default service;
创建请求
src/api/user/index.js
import request from "@/utils/request"; // 示例:获取用户信息 export const getUserInfo = () => { return request({ url: '/user/getUserInfo', method: 'get', }); };
十、外部字体引入
/*
* src/assets/fonts/font.css
* ttf字体文件放在同级目录下 './'
*
* font-family - 自定义字体名称
* src - 字体路径
* font-weight - 字体粗细
* font-style - 字体样式
*/
/* 字体引入 */
@font-face {
font-family: 'Typeface';
src: url('./typeface.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
十一、项目指令
# 运行项目
npm run dev
# 打包项目
npm run build
评论 (0)