去年接手一个企业后台管理系统时,遇到了一个典型场景:原本基于Vue3+Vite构建的单页面应用(SPA)随着业务扩展,需要拆分为多个独立子系统的多页面架构。这种改造在中小型项目迭代中非常常见,尤其是当不同业务模块需要独立部署、独立升级时。
单页面改多页面的核心诉求通常来自三个方面:
传统多页面方案通常有两种实现路径:
对于大多数后台管理系统,方案一更具性价比。我们最终采用的技术栈组合:
改造前后的典型结构对比:
bash复制# 改造前(SPA)
src/
├── main.js
├── App.vue
├── views/
# 改造后(MPA)
src/
├── modules/
│ ├── admin/ # 子系统1
│ │ ├── main.js
│ │ ├── App.vue
│ ├── report/ # 子系统2
│ │ ├── main.js
│ │ ├── App.vue
├── shared/ # 公共代码
关键设计原则:
shared目录软链接引用核心配置项修改(vite.config.js):
javascript复制import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import mpa from 'vite-plugin-mpa'
export default defineConfig({
plugins: [
vue(),
mpa({
open: false,
scanFile: 'src/modules/*/main.js',
filename: 'index.html',
template: 'src/template.html'
})
],
build: {
rollupOptions: {
input: {
admin: resolve(__dirname, 'src/modules/admin/index.html'),
report: resolve(__dirname, 'src/modules/report/index.html')
}
}
}
})
每个子系统需要独立的路由实例:
javascript复制// src/modules/admin/router.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/admin',
component: () => import('./views/Dashboard.vue')
}
]
export default createRouter({
history: createWebHistory('/admin'),
routes
})
特别注意点:
推荐采用Pinia的模块化方案:
javascript复制// src/modules/admin/stores/user.js
import { defineStore } from 'pinia'
export const useAdminUserStore = defineStore('admin-user', {
state: () => ({
token: null
})
})
通过manualChunks优化依赖拆分:
javascript复制// vite.config.js
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'element-plus': ['element-plus']
}
}
}
}
开发环境下通过环境变量控制编译范围:
bash复制# .env.development
VITE_BUILD_MODULES=admin,report
对应vite配置动态生成input:
javascript复制const activeModules = process.env.VITE_BUILD_MODULES.split(',')
const input = activeModules.reduce((acc, name) => {
acc[name] = `src/modules/${name}/index.html`
return acc
}, {})
现象:子系统间CSS样式互相影响
解决方案:
css复制/* 推荐写法 */
.admin-container {
/* 子系统专属样式 */
}
典型报错:[vite] page reload xxx.js
处理方法:
优化方案:
javascript复制resolve: {
alias: {
'@shared': path.resolve(__dirname, 'src/shared')
}
}
改造前后的关键指标对比(基于实际项目测量):
| 指标 | SPA模式 | MPA模式 |
|---|---|---|
| 首屏加载时间 | 2.8s | 1.2s |
| 热更新速度 | 1.4s | 0.6s |
| 构建时间 | 45s | 28s |
| 内存占用 | 1.2GB | 680MB |
实测发现子系统独立构建后,开发体验有明显提升。特别是当团队并行开发不同模块时,不会因为一个成员的修改触发全量编译。
创建cli工具自动初始化子系统:
javascript复制// scripts/new-module.js
const fs = require('fs')
const path = require('path')
function generateModule(name) {
const targetDir = `src/modules/${name}`
fs.mkdirSync(targetDir)
// 生成标准文件结构
fs.writeFileSync(
path.join(targetDir, 'main.js'),
`import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')`
)
}
实现运行时按需加载模块:
javascript复制// main.js
const loadModule = async (name) => {
const { default: setup } = await import(`./modules/${name}/bootstrap.js`)
return setup()
}
// URL参数控制加载模块
const moduleName = new URLSearchParams(location.search).get('module')
loadModule(moduleName || 'admin')
制定跨模块组件协议:
src/shared/componentsS-(如S-Table)javascript复制// vite.config.js
Components({
dirs: ['src/shared/components'],
resolvers: [
(name) => {
if (name.startsWith('S'))
return `src/shared/components/${name.slice(2)}.vue`
}
]
})
对于正在运行的老系统,推荐分阶段迁移:
准备阶段(1-2周)
并行阶段(2-4周)
收尾阶段(1周)
在最近的项目实践中,这种渐进式迁移方案平均可减少30%的改造成本,同时保持业务连续性。关键是要建立完善的模块通信机制和版本管理策略,避免迁移过程中出现系统断层。