移睿云医生官方版
75.17MB · 2025-11-07
在中大型前端项目中,我们常常会遇到多个独立业务模块并存的场景(比如企业内部的抽奖系统、会议系统、生日活动系统等)。如果将它们耦合在一个项目中,会导致代码冗余、构建缓慢、维护困难。Vue3 + Vite 的多入口方案可以完美解决这个问题,让每个业务模块都能独立开发、独立部署、独立迭代。
先看一个典型的业务场景:
某公司需要开发以下几个独立的活动系统:
这些系统技术栈相同,但业务完全独立,如果做成四个独立项目,会存在重复配置(如路由、状态管理、UI 组件)的问题;如果做成单入口项目,又会导致代码耦合、构建产物过大。
多入口方案的优势:
先看多入口项目结构:
src/
├── assets/ # 公共静态资源(图片、字体等)
├── components/ # 公共组件(多个入口共享)
├── packages/ # 业务入口目录
│ ├── lottery/ # 抽奖系统入口
│ │ ├── api/ # 抽奖系统接口
│ │ ├── router/ # 抽奖系统路由
│ │ ├── views/ # 抽奖系统页面
│ │ ├── App.vue # 抽奖系统根组件
│ │ ├── main.js # 抽奖系统入口文件
│ │ └── index.html # 抽奖系统HTML模板
│ ├── meeting/ # 会议系统入口(结构同lottery)
│ ├── birthdayParty/ # 生日派对系统入口
│ └── ygzj/ # 员工激励系统入口
├── vite.config.js # Vite 多入口配置
└── package.json # 依赖和脚本配置
这种结构的核心是每个入口都有独立的 main.js、App.vue、index.html,同时共享 src 下的公共资源和组件。
Vite 基于 Rollup 打包,多入口的核心是通过 build.rollupOptions.input 指定多个入口文件路径,同时配合 npm 脚本实现「独立开发」和「批量打包」。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 1. 定义所有入口(key:入口名称,value:入口文件路径)
const multiEntries = {
lottery: path.resolve(__dirname, 'src/packages/lottery/main.js'),
meeting: path.resolve(__dirname, 'src/packages/meeting/main.js'),
party: path.resolve(__dirname, 'src/packages/birthdayParty/main.js'),
ygzj: path.resolve(__dirname, 'src/packages/ygzj/main.js'),
}
export default defineConfig({
plugins: [vue()],
// 通用别名配置(方便所有入口引入公共资源,非多入口核心,但必备)
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@assets': path.resolve(__dirname, 'src/assets'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
},
// 2. 开发服务器配置(支持多入口独立启动的基础)
server: {
port: 3000,
open: false,
cors: true, // 解决跨域(如果多个入口需要调用同一接口)
},
// 3. 多入口核心打包配置(Rollup 选项)
build: {
rollupOptions: {
// 指定多个入口文件(核心!告诉 Vite 要打包哪些入口)
input: multiEntries,
// 配置输出规则(每个入口独立输出到对应目录)
output: {
// 输出目录:dist/[入口名称]/,如 dist/lottery/、dist/meeting/
dir: 'dist/[name]',
// 静态资源文件名格式(添加哈希,避免缓存)
assetFileNames: 'assets/[name]-[hash][extname]',
// 代码分割后的chunk文件名
chunkFileNames: 'assets/[name]-[hash].js',
// 入口文件文件名
entryFileNames: 'assets/[name]-[hash].js',
},
},
},
})
multiEntries:集中管理所有入口路径,方便维护(新增入口只需在这里添加);build.rollupOptions.input:Rollup 的核心配置,指定多个入口文件,Vite 会根据这个配置打包所有入口;output.dir: 'dist/[name]':每个入口的打包产物独立存放在 dist 下的对应子目录([name] 对应 multiEntries 的 key),避免文件冲突。在 package.json 中配置脚本,支持「单独启动某个入口开发服务」和「批量打包所有入口」:
{
"scripts": {
// 独立开发脚本:启动单个入口的开发服务(--open 可自动打开浏览器)
"dev:lottery": "vite serve src/packages/lottery/ --config ./vite.config.js --open",
"dev:meeting": "vite serve src/packages/meeting/ --config ./vite.config.js",
"dev:party": "vite serve src/packages/birthdayParty/ --config ./vite.config.js",
"dev:ygzj": "vite serve src/packages/ygzj/ --config ./vite.config.js",
// 打包脚本:单独打包某个入口
"build:lottery": "vite build --config ./vite.config.js --mode production",
"build:meeting": "vite build --config ./vite.config.js --mode production",
// 批量打包所有入口(执行一次,打包所有在 multiEntries 中定义的入口)
"build:all": "vite build --config ./vite.config.js --mode production"
}
}
npm run dev:lottery,Vite 会以 src/packages/lottery/index.html 为入口,启动开发服务(端口 3000),修改该入口的代码会热更新,不影响其他入口;npm run build:all,Vite 会根据 multiEntries 配置,批量打包所有入口到 dist 目录下(如 dist/lottery、dist/meeting);npm run build:lottery,只会打包抽奖系统入口(需确保 multiEntries 中包含该入口)。为了保证多入口项目的可维护性,每个入口模块需要遵循「独立配置、共享公共资源」的原则:
每个入口的 main.js 负责自己的 Vue 应用初始化(路由、状态管理等),不影响其他入口:
// src/packages/lottery/main.js(抽奖系统入口)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 抽奖系统独立路由
import '@assets/styles/common.scss' // 引入公共样式
const app = createApp(App)
app.use(router)
app.mount('#app')
每个入口的 index.html 是独立的 HTML 模板,需指定唯一的挂载节点(通常是 #app),并引入当前入口的 main.js:
<!-- src/packages/lottery/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>抽奖系统</title> <!-- 每个入口的标题独立 -->
</head>
<body>
<div id="app"></div>
<!-- 引入当前入口的 main.js(路径需正确) -->
<script type="module" src="/src/packages/lottery/main.js"></script>
</body>
</html>
每个入口的路由独立维护,如需部署到同域名的不同路径,需配置 base 路径:
// src/packages/lottery/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import LotteryHome from '../views/LotteryHome.vue'
const router = createRouter({
// 部署到同域名的 /lottery 路径时,需配置 base
history: createWebHistory(import.meta.env.BASE_URL + 'lottery/'),
routes: [
{ path: '/', component: LotteryHome },
// 其他路由...
]
})
export default router
共享的组件、工具函数、静态资源,通过之前配置的别名引入,无需相对路径层级嵌套:
<!-- 抽奖系统页面中引入公共组件 -->
<template>
<CommonButton @click="startDraw">立即抽奖</CommonButton>
</template>
<script setup>
import CommonButton from '@components/CommonButton.vue' // 共享组件
import { throttle } from '@utils/tool.js' // 共享工具函数
</script>
<style scoped>
@import '@assets/styles/common.scss'; // 共享样式变量
.lottery-container {
background: $primary-color; // 使用共享变量
}
</style>