Vue 3 + Pinia + Vite 构建现代化企业级应用架构:从项目搭建到工程化实践

Adam748
Adam748 2026-02-08T23:13:09+08:00
0 0 0

引言

在现代前端开发领域,构建高性能、可维护的企业级应用已成为开发团队的核心挑战。Vue 3作为新一代的前端框架,结合Pinia状态管理库和Vite构建工具,为开发者提供了现代化的解决方案。本文将深入探讨如何基于这些技术栈构建完整的现代化企业级应用架构,涵盖从项目搭建到工程化实践的全过程。

Vue 3生态概述

Vue 3的核心优势

Vue 3作为Vue.js的下一个主要版本,在性能、开发体验和功能特性方面都有显著提升。其核心优势包括:

  1. Composition API:提供更灵活的代码组织方式
  2. 更好的 TypeScript 支持:原生支持TypeScript开发
  3. 性能优化:更小的包体积,更快的渲染速度
  4. 多根节点支持:组件可以返回多个根元素

技术栈选择理由

选择Vue 3 + Pinia + Vite的技术组合主要基于以下考虑:

  • Vue 3:成熟的生态系统,丰富的社区支持
  • Pinia:官方推荐的状态管理方案,比Vuex更轻量
  • Vite:现代构建工具,提供极致的开发体验

项目初始化与基础配置

创建Vue 3项目

使用Vite创建Vue 3项目的最佳实践:

# 使用npm
npm create vite@latest my-vue-app -- --template vue

# 使用yarn
yarn create vite my-vue-app --template vue

# 使用pnpm
pnpm create vite my-vue-app --template vue

项目结构规划

典型的现代化Vue 3项目结构:

src/
├── assets/              # 静态资源
├── components/          # 公共组件
├── composables/         # 可复用逻辑
├── layouts/             # 布局组件
├── pages/               # 页面组件
├── router/              # 路由配置
├── stores/              # 状态管理
├── styles/              # 样式文件
├── utils/               # 工具函数
├── views/               # 视图组件
├── App.vue              # 根组件
└── main.js              # 入口文件

Vite配置优化

基础的vite.config.js配置:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
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(),
    vueJsx(),
    AutoImport({
      resolvers: [ElementPlusResolver()]
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    })
  ],
  server: {
    port: 3000,
    host: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
          element: ['element-plus']
        }
      }
    }
  }
})

Pinia状态管理实践

Pinia核心概念

Pinia是Vue官方推荐的状态管理库,相比Vuex具有更轻量、更易用的特点:

// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: null,
    isLoggedIn: false,
    permissions: []
  }),
  
  getters: {
    userName: (state) => state.userInfo?.name || '',
    hasPermission: (state) => (permission) => {
      return state.permissions.includes(permission)
    }
  },
  
  actions: {
    async login(credentials) {
      try {
        const response = await fetch('/api/login', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(credentials)
        })
        
        const data = await response.json()
        this.userInfo = data.user
        this.isLoggedIn = true
        this.permissions = data.permissions
        
        // 存储到localStorage
        localStorage.setItem('userToken', data.token)
        
        return data
      } catch (error) {
        throw new Error('登录失败')
      }
    },
    
    logout() {
      this.userInfo = null
      this.isLoggedIn = false
      this.permissions = []
      localStorage.removeItem('userToken')
    }
  }
})

多状态模块管理

创建多个状态模块以满足复杂业务需求:

// stores/app.js
import { defineStore } from 'pinia'

export const useAppStore = defineStore('app', {
  state: () => ({
    loading: false,
    theme: 'light',
    language: 'zh-CN'
  }),
  
  actions: {
    setLoading(status) {
      this.loading = status
    },
    
    toggleTheme() {
      this.theme = this.theme === 'light' ? 'dark' : 'light'
    }
  }
})

// stores/setting.js
import { defineStore } from 'pinia'

export const useSettingStore = defineStore('setting', {
  state: () => ({
    sidebarCollapsed: false,
    notifications: []
  }),
  
  actions: {
    toggleSidebar() {
      this.sidebarCollapsed = !this.sidebarCollapsed
    },
    
    addNotification(notification) {
      this.notifications.push({
        id: Date.now(),
        ...notification,
        timestamp: new Date()
      })
    }
  }
})

状态持久化

实现状态的持久化存储:

// utils/persist.js
import { createPinia } from 'pinia'

export function setupPersist(pinia) {
  pinia.use(({ store }) => {
    // 从localStorage恢复状态
    const savedState = localStorage.getItem(`pinia-${store.$id}`)
    if (savedState) {
      store.$patch(JSON.parse(savedState))
    }
    
    // 监听状态变化并保存到localStorage
    store.$subscribe((mutation, state) => {
      localStorage.setItem(`pinia-${store.$id}`, JSON.stringify(state))
    })
  })
}

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { setupPersist } from './utils/persist'

const app = createApp(App)
const pinia = createPinia()

setupPersist(pinia)
app.use(pinia)

组件化设计模式

可复用逻辑组合式函数

使用Composition API创建可复用的逻辑:

// composables/useApi.js
import { ref, reactive } from 'vue'

export function useApi() {
  const loading = ref(false)
  const error = ref(null)
  
  const request = async (url, options = {}) => {
    try {
      loading.value = true
      error.value = null
      
      const response = await fetch(url, {
        headers: {
          'Content-Type': 'application/json',
          ...options.headers
        },
        ...options
      })
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      
      const data = await response.json()
      return data
    } catch (err) {
      error.value = err.message
      throw err
    } finally {
      loading.value = false
    }
  }
  
  return {
    loading,
    error,
    request
  }
}

// composables/usePagination.js
import { ref, computed } from 'vue'

export function usePagination(initialPage = 1, initialPageSize = 10) {
  const currentPage = ref(initialPage)
  const pageSize = ref(initialPageSize)
  const total = ref(0)
  
  const totalPages = computed(() => Math.ceil(total.value / pageSize.value))
  
  const setPage = (page) => {
    if (page >= 1 && page <= totalPages.value) {
      currentPage.value = page
    }
  }
  
  const setPageSize = (size) => {
    pageSize.value = size
    currentPage.value = 1
  }
  
  return {
    currentPage,
    pageSize,
    total,
    totalPages,
    setPage,
    setPageSize
  }
}

组件通信模式

实现多种组件间通信方式:

<!-- components/DataTable.vue -->
<template>
  <div class="data-table">
    <el-table :data="tableData" v-loading="loading">
      <el-table-column prop="name" label="姓名"></el-table-column>
      <el-table-column prop="email" label="邮箱"></el-table-column>
      <el-table-column label="操作">
        <template #default="{ row }">
          <el-button @click="handleEdit(row)">编辑</el-button>
          <el-button type="danger" @click="handleDelete(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    
    <el-pagination
      v-model:current-page="pagination.currentPage"
      v-model:page-size="pagination.pageSize"
      :total="pagination.total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { useApi } from '@/composables/useApi'

const props = defineProps({
  apiPath: {
    type: String,
    required: true
  }
})

const emit = defineEmits(['row-edit', 'row-delete'])

const { loading, request } = useApi()
const tableData = ref([])
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0
})

// 加载数据
const loadData = async () => {
  try {
    const response = await request(`${props.apiPath}?page=${pagination.currentPage}&size=${pagination.pageSize}`)
    tableData.value = response.data
    pagination.total = response.total
  } catch (error) {
    console.error('加载数据失败:', error)
  }
}

// 监听分页变化
watch([() => pagination.currentPage, () => pagination.pageSize], loadData)

// 处理分页事件
const handleSizeChange = (size) => {
  pagination.pageSize = size
  loadData()
}

const handleCurrentChange = (page) => {
  pagination.currentPage = page
  loadData()
}

const handleEdit = (row) => {
  emit('row-edit', row)
}

const handleDelete = (row) => {
  emit('row-delete', row)
}

// 初始化加载数据
loadData()
</script>

路由系统设计

路由配置最佳实践

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'

const routes = [
  {
    path: '/',
    redirect: '/dashboard'
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { requiresAuth: false }
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: { requiresAuth: true, title: '仪表板' }
  },
  {
    path: '/users',
    name: 'Users',
    component: () => import('@/views/Users.vue'),
    meta: { requiresAuth: true, permissions: ['user:view'], title: '用户管理' }
  },
  {
    path: '/settings',
    name: 'Settings',
    component: () => import('@/views/Settings.vue'),
    meta: { requiresAuth: true, permissions: ['setting:view'], title: '系统设置' }
  }
]

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

// 路由守卫
router.beforeEach((to, from, next) => {
  const userStore = useUserStore()
  
  // 需要认证的路由
  if (to.meta.requiresAuth && !userStore.isLoggedIn) {
    next('/login')
    return
  }
  
  // 权限检查
  if (to.meta.permissions && to.meta.permissions.length > 0) {
    const hasPermission = to.meta.permissions.some(permission => 
      userStore.hasPermission(permission)
    )
    
    if (!hasPermission) {
      next('/403')
      return
    }
  }
  
  next()
})

export default router

动态路由加载

实现懒加载和动态路由:

// router/dynamic.js
import { defineAsyncComponent } from 'vue'

const asyncRoute = (path, component) => {
  return defineAsyncComponent(() => import(`@/views/${path}/${component}.vue`))
}

export const dynamicRoutes = [
  {
    path: '/reports',
    name: 'Reports',
    component: asyncRoute('Reports', 'Index'),
    meta: { requiresAuth: true, title: '报表中心' }
  },
  {
    path: '/analytics',
    name: 'Analytics',
    component: asyncRoute('Analytics', 'Dashboard'),
    meta: { requiresAuth: true, title: '数据分析' }
  }
]

工程化配置与优化

构建优化策略

// vite.config.js (构建优化)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    visualizer({
      filename: 'dist/stats.html',
      open: true
    })
  ],
  build: {
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        // 分块策略
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia', 'element-plus'],
          utils: ['axios', 'lodash', 'dayjs'],
          charts: ['echarts', 'chart.js']
        }
      }
    },
    // 生产环境压缩
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
})

环境变量管理

// .env.development
VITE_API_BASE_URL=http://localhost:8080/api
VITE_APP_NAME=My Enterprise App
VITE_APP_VERSION=1.0.0

// .env.production
VITE_API_BASE_URL=https://api.myapp.com/api
VITE_APP_NAME=My Enterprise App
VITE_APP_VERSION=1.0.0

// utils/config.js
export const config = {
  apiUrl: import.meta.env.VITE_API_BASE_URL,
  appName: import.meta.env.VITE_APP_NAME,
  appVersion: import.meta.env.VITE_APP_VERSION
}

TypeScript配置

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "types": ["vite/client"],
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*", "src/**/*.vue"],
  "exclude": ["node_modules"]
}

性能监控与调试

开发环境调试工具

// utils/debug.js
export const debug = {
  log: (message, data) => {
    if (import.meta.env.DEV) {
      console.log(`[DEBUG] ${message}`, data)
    }
  },
  
  error: (message, error) => {
    if (import.meta.env.DEV) {
      console.error(`[ERROR] ${message}`, error)
    }
  },
  
  time: (label) => {
    if (import.meta.env.DEV) {
      console.time(label)
    }
  },
  
  timeEnd: (label) => {
    if (import.meta.env.DEV) {
      console.timeEnd(label)
    }
  }
}

性能监控实现

// composables/usePerformance.js
import { ref, onMounted } from 'vue'

export function usePerformance() {
  const performanceData = ref({
    memory: null,
    fps: 0,
    loadTime: 0
  })
  
  const startMonitoring = () => {
    if ('performance' in window) {
      const observer = new PerformanceObserver((list) => {
        list.getEntries().forEach((entry) => {
          if (entry.entryType === 'navigation') {
            performanceData.value.loadTime = entry.loadEventEnd - entry.loadEventStart
          }
        })
      })
      
      observer.observe({ entryTypes: ['navigation'] })
    }
  }
  
  onMounted(() => {
    startMonitoring()
  })
  
  return {
    performanceData
  }
}

安全性最佳实践

XSS防护

// utils/sanitize.js
import DOMPurify from 'dompurify'

export function sanitizeHtml(html) {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li'],
    ALLOWED_ATTR: ['class', 'id']
  })
}

// 在组件中使用
import { sanitizeHtml } from '@/utils/sanitize'

const processedContent = computed(() => {
  return sanitizeHtml(props.content)
})

请求安全

// utils/api.js
import axios from 'axios'
import { useUserStore } from '@/stores/user'

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

// 请求拦截器
api.interceptors.request.use(
  (config) => {
    const userStore = useUserStore()
    if (userStore.isLoggedIn) {
      config.headers.Authorization = `Bearer ${localStorage.getItem('userToken')}`
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// 响应拦截器
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // 未授权,跳转到登录页
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

export default api

部署与运维

Docker部署配置

# Dockerfile
FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

RUN npm run build

EXPOSE 3000

CMD ["npm", "run", "serve"]

CI/CD配置

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run tests
      run: npm test
      
    - name: Build project
      run: npm run build
      
    - name: Deploy to production
      run: |
        # 部署逻辑
        echo "Deploying to production..."

总结

通过本文的详细阐述,我们全面介绍了如何基于Vue 3、Pinia和Vite构建现代化企业级应用架构。从项目初始化到工程化实践,涵盖了状态管理、组件设计、路由系统、性能优化、安全性等多个关键方面。

这套技术栈组合不仅提供了优秀的开发体验,还具备良好的可维护性和扩展性。通过合理的架构设计和最佳实践的应用,能够有效支撑复杂的企业级应用开发需求。

在实际项目中,建议根据具体业务场景灵活调整架构方案,持续优化和迭代,以构建出真正符合企业需求的现代化前端应用。随着技术的不断发展,这套架构也将不断完善和演进,为企业的数字化转型提供强有力的技术支撑。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000