最近接手了一个Vue3+Vite的单页面应用(SPA)改造项目,需要将其重构为多页面应用(MPA)。这个需求在传统企业级项目中其实很常见——当系统功能模块逐渐增多,不同业务线需要独立部署时,SPA的局限性就显现出来了。
我遇到的具体情况是:原系统包含用户中心、订单管理、商品后台三个主要模块,随着业务发展,每个模块都变得非常庞大。开发团队经常遇到这些问题:
经过技术评估,我们决定保留Vue3的现代化开发体验,同时通过Vite的MPA支持来解决这些问题。下面分享具体改造过程。
首先创建新的pages目录存放各页面入口:
code复制src/
pages/
user/ # 用户中心模块
index.html
main.ts
App.vue
order/ # 订单模块
index.html
main.ts
App.vue
product/ # 商品模块
index.html
main.ts
App.vue
shared/ # 公共代码
components/
utils/
styles/
关键点说明:
修改vite.config.ts支持多入口:
typescript复制import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
// 自动扫描pages目录下的入口文件
const getPages = () => {
return {
user: resolve(__dirname, 'src/pages/user/index.html'),
order: resolve(__dirname, 'src/pages/order/index.html'),
product: resolve(__dirname, 'src/pages/product/index.html')
}
}
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
input: getPages(),
output: {
chunkFileNames: 'shared/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash][extname]'
}
}
}
})
特别注意:
原SPA使用的是vue-router,改造后每个模块需要自己的路由实例:
typescript复制// src/pages/user/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
export default createRouter({
history: createWebHistory('/user'),
routes: [
{
path: '/profile',
component: () => import('../views/Profile.vue')
}
// 其他用户模块路由...
]
})
关键调整:
原项目的Pinia store需要重新组织:
code复制src/
shared/
stores/
auth.ts # 认证相关
common.ts # 公共状态
pages/
user/
stores/ # 用户模块专属store
preferences.ts
使用建议:
将通用组件提取到shared目录:
vue复制// src/shared/components/BaseButton.vue
<script setup>
defineProps({
type: { type: String, default: 'default' }
})
</script>
<template>
<button :class="['btn', `btn-${type}`]">
<slot />
</button>
</template>
使用alias简化引用:
typescript复制// vite.config.ts
resolve: {
alias: {
'@shared': resolve(__dirname, 'src/shared')
}
}
通过manualChunks优化分包:
typescript复制// vite.config.ts
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
ui: ['element-plus', 'vant'],
utils: ['lodash', 'dayjs']
}
}
}
}
效果对比:
配置dev server支持多入口访问:
typescript复制server: {
proxy: {
'/user': { target: 'http://localhost:3000/user.html' },
'/order': { target: 'http://localhost:3000/order.html' }
}
}
这样可以通过:
现象:不同模块的样式互相影响
解决方案:
vue复制<style scoped>
/* 模块私有样式 */
</style>
typescript复制// vite.config.ts
css: {
modules: {
localsConvention: 'camelCase'
}
}
错误做法:
html复制<img src="../assets/logo.png" />
正确方式:
html复制<!-- 使用绝对路径 -->
<img src="/assets/logo.png" />
<!-- 或import方式 -->
<script setup>
import logo from '@shared/assets/logo.png'
</script>
<template>
<img :src="logo" />
</template>
建议方案:
code复制.env # 全局变量
.env.user # 用户模块变量
.env.order # 订单模块变量
访问方式:
typescript复制const apiUrl = import.meta.env.VITE_API_URL
nginx复制server {
listen 80;
location /user {
alias /var/www/html/user;
try_files $uri /user.html;
}
location /order {
alias /var/www/html/order;
try_files $uri /order.html;
}
}
关键调整点:
示例命令:
bash复制# 单独构建用户模块
vite build --config vite.user.config.ts
# 构建所有模块
vite build --mode=production
经过两周的改造,系统获得了显著改进:
这次改造最大的收获是:Vite的MPA支持比预想的要完善很多,配合Vue3的composition API,既能享受现代前端开发的便利,又能满足复杂业务场景的架构需求。对于正在面临类似挑战的团队,不妨参考这个方案进行尝试。