Vue3 + TS 企业级工程化项目全套实战(Vue3 + Vite + Pinia + VueRouter + Element Plus)

0 阅读2分钟

本文作者:LIO

一、项目背景与技术栈选型

这是一个面向中大型后台管理系统的 Vue3 工程化全套方案。你可以直接用于项目脚手架、面试准备或技术博客。

核心技术栈

  • Vue3(Composition API)
  • Vite
  • TypeScript
  • Pinia(替代 Vuex)
  • Vue Router 4
  • Element Plus
  • Axios 封装
  • ESLint + Prettier + Husky
  • Scss / CSS Variables

推荐目录结构(可直接复制使用)

src
├── api          # 接口请求统一封装
├── assets       # 图片、样式资源
├── components   # 公共组件
├── config       # 全局常量、环境变量
├── hooks        # 通用 hooks
├── layouts      # 布局组件
├── router       # 路由 + 权限路由
├── store        # Pinia 模块化仓库
├── types        // TS 类型声明
├── utils        // 工具函数
├── views        // 业务页面
├── App.vue
└── main.ts

二、Axios 全局请求封装(生产级)

1. 创建 src/utils/request.ts

import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'

let loadingInstance: any = null

const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000,
})

// 请求拦截器
request.interceptors.request.use(
  (config) => {
    loadingInstance = ElLoading.service({ fullscreen: true })
    const token = localStorage.getItem('token')
    if (token) config.headers.Authorization = `Bearer ${token}`
    return config
  },
  (err) => Promise.reject(err)
)

// 响应拦截器
request.interceptors.response.use(
  (res) => {
    loadingInstance.close()
    return res.data
  },
  (err) => {
    loadingInstance.close()
    ElMessage.error(err.response?.data?.message || '请求失败')
    return Promise.reject(err)
  }
)

export default request

2. API 分层示例(src/api/user.ts)

import request from '@/utils/request'

export function getUserInfo() {
  return request.get('/user/info')
}

export function login(data: any) {
  return request.post('/login', data)
}

三、Pinia 仓库模块化设计(src/store/index.ts)

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: localStorage.getItem('token') || '',
    userInfo: null as any,
  }),

  actions: {
    setToken(token: string) {
      this.token = token
      localStorage.setItem('token', token)
    },

    async fetchUserInfo() {
      const res = await getUserInfo()
      this.userInfo = res.data
    },
  },
})

四、Vue Router + 权限路由

src/router/index.ts

import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/store/user'

const routes = [
  {
    path: '/',
    component: () => import('@/layouts/MainLayout.vue'),
    meta: { requireAuth: true },
    children: [
      { path: '', component: () => import('@/views/Home.vue') },
      { path: 'user', component: () => import('@/views/User.vue') },
    ],
  },
  { path: '/login', component: () => import('@/views/Login.vue') },
]

const router = createRouter({
  history: createWebHistory(),
  routes,
})

// 全局路由守卫
router.beforeEach((to, from, next) => {
  const userStore = useUserStore()
  if (to.meta.requireAuth && !userStore.token) {
    next('/login')
  } else {
    next()
  }
})

export default router

五、工程化与打包优化(重点)

  1. 路由懒加载
component: () => import('@/views/Home.vue')
  1. 分包策略(vite.config.ts)
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: '[ext]/[name]-[hash].[ext]',
      },
    },
  },
})
  1. ESLint + Prettier
npx eslint --fix .
  1. Husky 提交校验
npx husky install
npx husky add .husky/pre-commit "npm lint"

六、常见业务场景落地(可直接复制)

1. 分页列表

<template>
  <el-table
    :data="list"
    :loading="loading"
  >
    <!-- 列 -->
  </el-table>
  <el-pagination
    v-model:page-size="pageSize"
    v-model:current-page="pageNum"
    :total="total"
    @change="fetchList"
  />
</template>

2. 表单校验

const ruleRef = ref()

const rules = {
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
}

七、面试高频考点(掘金博客可直接写)

  • Vue3 响应式原理(Proxy)
  • Composition API 与 Options API 区别
  • Pinia 与 Vuex 的差异
  • Vue Router 动态路由实现
  • Vite 与 Webpack 对比
  • 组件懒加载、代码分割
  • 前端鉴权方案(JWT + 路由守卫)

——个人观点 · 仅供参考——