1. 项目概述为什么我们需要多入口配置如果你是从 Vue CLI 或者 Webpack 时代过来的前端开发者第一次接触 Vite 时可能会被它简洁的vite.config.js搞得有点懵。特别是当你接到一个需求不是做一个单页应用SPA而是要做一个包含多个独立页面的网站比如一个公司官网有首页、关于我们、产品介绍、联系我们等多个独立的 HTML 页面。在 Webpack 里我们可能会用html-webpack-plugin配多个实例或者用MPA的配置思路。但在 Vite 的世界里这个配置逻辑变得更直观却也因为文档的分散和默认的单入口设定让不少新手感到困惑。这个项目要解决的就是如何在 Vue3 Vite 的项目中优雅地配置多个 HTML 入口文件并且为每个独立的页面尤其是那些不需要复杂前端路由的页面配置好 Vue 路由。听起来好像是把简单问题复杂化其实不然。想象一下你有一个后台管理系统主应用SPA和一个对外展示的落地页独立页面你肯定不希望为了改个落地页的标题就去动主应用的index.html更不希望落地页的打包产物混在主应用的dist文件夹里难以管理。多入口配置就是将它们物理隔离、独立开发、独立构建的最佳实践。我自己在从零搭建一个中台项目时就遇到了这个需求主应用是一个复杂的 SPA同时需要几个独立的、轻量级的活动页面。如果全塞进一个 SPA 里路由和状态管理都会变得臃肿而且活动页面的频繁迭代会影响到主应用的发布流程。多入口配置完美地解决了这个问题让不同功能模块的开发和部署可以解耦。接下来我会手把手带你从零配置确保你看完就能在自己的项目里用起来。2. 核心思路与项目结构设计在动手写配置之前我们先要把思路理清楚。Vite 的多入口配置核心是理解“入口”的定义。在 Vite 中一个入口通常对应一个 HTML 文件。Vite 的开发服务器和构建过程都会根据这些入口来工作。2.1 多入口配置的核心逻辑Vite 的构建配置项build.rollupOptions.input是控制多入口的关键。这个配置直接传递给底层的 Rollup 打包器。默认情况下它的值是当前目录下的index.html。当我们需要多个入口时就需要把它改成一个对象对象的键key是入口的名称值value是该入口对应的 HTML 文件的路径。这个“名称”非常重要它会直接影响打包后产物的目录结构。例如如果你设置input: { main: src/main.html, about: src/about.html }那么构建后dist目录下就会生成main.html和about.html而它们对应的 JS、CSS 资源可能会被放在以入口名命名的子目录下取决于其他配置从而实现资源的隔离。2.2 项目结构规划一个清晰的项目结构是成功的一半。我推荐下面这种结构它兼顾了清晰度和灵活性your-vite-project/ ├── public/ # 静态资源不经过Vite处理 ├── src/ │ ├── entries/ # 存放所有入口文件 │ │ ├── main/ # 主应用入口 │ │ │ ├── index.html │ │ │ ├── main.js # 主应用入口JS │ │ │ └── App.vue # 主应用根组件 │ │ └── about/ # “关于我们”页面入口 │ │ ├── index.html │ │ ├── about.js # 关于页面入口JS │ │ └── About.vue # 关于页面组件 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置主应用使用 │ │ └── index.js │ ├── views/ # 主应用的页面组件 │ └── assets/ # 模块化资源图片、样式等 ├── vite.config.js # Vite 配置文件 ├── package.json └── index.html # 可选默认入口可删除或指向主入口为什么要把入口文件放在src/entries下而不是项目根目录主要有两个原因一是为了项目结构更干净根目录只留配置文件二是方便统一管理所有需要被 Vite 处理的入口都聚集在一起逻辑清晰。每个入口目录如main,about都自成一体包含自己的 HTML、JS 入口和根组件独立性非常强。注意很多教程会把 HTML 放在项目根目录这在入口不多时没问题。但一旦入口多起来根目录会变得非常混乱。将入口归集到src下是更工程化的做法也符合 Vite 推荐将源码放在src目录的惯例。2.3 单页面路由配置的定位这里的“单页面路由配置”需要特别理解。它并不是指整个项目是一个 SPA虽然主应用可能是而是指每个独立的入口 HTML 页面内部可以拥有自己的 Vue Router 实例和路由配置。例如你的main入口是一个复杂的后台管理系统它内部需要 Vue Router 来实现页面切换。而about入口就是一个简单的静态展示页它可能根本不需要路由或者只需要一个最简单的、只有一个路由的路由器。因此我们的配置需要支持两种模式多页面应用MPA模式项目由多个独立的 HTML 文件构成它们之间通过a标签跳转每次跳转都是完整的页面重载。混合模式项目整体是 MPA但其中的某个或某几个入口内部是 SPA拥有自己的前端路由。我们的配置方案将同时覆盖这两种场景。3. 从零开始基础配置与多入口实现现在我们开始动手。首先确保你有一个基于 Vue3 和 Vite 的项目。如果还没有可以使用官方命令快速创建一个npm create vitelatest my-project -- --template vue。创建好后我们按照前面规划的结构来改造它。3.1 调整项目结构在src目录下创建entries文件夹。将项目根目录的index.html移动到src/entries/main/目录下。同时将src/main.js和src/App.vue也移动到这个目录并分别改名为main.js和App.vue原名也可以这里为了清晰。修改src/entries/main/index.html中的脚本引用路径。原来可能是script typemodule src/src/main.js/script现在需要根据新的位置调整。因为 HTML 文件在src/entries/main/下而入口 JS 在同一个目录所以可以改为相对路径script typemodule src./main.js/script或者使用绝对路径/src/entries/main/main.jsVite 能正确解析。创建第二个入口。在src/entries/about/目录下创建index.html、about.js和About.vue。src/entries/about/index.html内容示例!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title关于我们 - 我的网站/title /head body div idapp/div script typemodule src./about.js/script /body /htmlsrc/entries/about/about.js内容示例import { createApp } from vue import About from ./About.vue createApp(About).mount(#app)src/entries/about/About.vue内容就是一个简单的 Vue 组件。3.2 配置 vite.config.js这是最关键的一步。打开vite.config.js我们需要配置build.rollupOptions.input。import { defineConfig } from vite import vue from vitejs/plugin-vue import { resolve } from path // 引入 path 模块用于解析路径 // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], build: { rollupOptions: { // 多入口配置 input: { main: resolve(__dirname, src/entries/main/index.html), about: resolve(__dirname, src/entries/about/index.html), // 可以继续添加更多入口... } } } })这里使用了 Node.js 的path.resolve方法来获取 HTML 文件的绝对路径这是最可靠的方式。__dirname代表当前配置文件所在的目录。3.3 开发服务器与路径别名配置好后运行npm run dev你会发现 Vite 默认可能还是打开根目录的index.html如果还存在的话。我们需要告诉 Vite 开发服务器我们的入口已经变了。一种方法是直接访问http://localhost:5173/src/entries/main/index.html但这很不优雅。更好的方式是在vite.config.js中配置root选项并设置一个路径别名让开发体验更顺畅。import { defineConfig } from vite import vue from vitejs/plugin-vue import { resolve } from path export default defineConfig({ // 设置项目根目录为 src这样默认索引文件就在 src 下找 // root: resolve(__dirname, src), // 可选根据习惯调整 plugins: [vue()], resolve: { alias: { // 设置 指向 src 目录方便在组件中引用 : resolve(__dirname, src), }, }, build: { rollupOptions: { input: { main: resolve(__dirname, src/entries/main/index.html), about: resolve(__dirname, src/entries/about/index.html), }, // 输出配置让打包后的资源结构更清晰 output: { // 入口块的命名格式[name] 会被替换为 input 对象的 key如 main, about entryFileNames: assets/js/[name]-[hash].js, // 块非入口如动态导入的组件的命名格式 chunkFileNames: assets/js/[name]-[hash].js, // 资源文件如图片、字体的命名格式 assetFileNames: assets/[ext]/[name]-[hash].[ext] } } } })现在你可以删除项目根目录的index.html了。启动开发服务器后直接访问http://localhost:5173可能会报错因为 Vite 找不到默认的index.html。你需要通过http://localhost:5173/src/entries/main/index.html来访问主应用。为了更方便可以在package.json的scripts里添加一个自定义命令或者使用 Vite 的server.open配置来自动打开指定页面。一个更实用的技巧是保持根目录有一个最简单的index.html作为导航页里面列出所有入口的链接这在开发多入口项目时非常方便。4. 为特定入口配置 Vue Router现在我们已经有了两个独立的入口。假设我们的main入口是一个后台管理系统需要复杂的前端路由。而about入口是静态页不需要路由。我们来为main入口配置 Vue Router。4.1 安装与创建路由文件首先在主入口目录下安装 Vue Routernpm install vue-router4。在src/entries/main/目录下或者按照习惯放在src/router/下创建路由配置文件。这里我们放在入口目录下以保持其独立性命名为router.js。// src/entries/main/router.js import { createRouter, createWebHistory } from vue-router // 假设你的页面组件放在 src/views/ 或 src/entries/main/views/ 下 import Dashboard from /views/Dashboard.vue // 使用了路径别名 import UserList from /views/UserList.vue const routes [ { path: /, name: Dashboard, component: Dashboard }, { path: /users, name: UserList, component: UserList }, // ... 其他路由 ] const router createRouter({ // 使用 history 模式。注意对于多入口项目每个入口的 base URL 可能需要特别处理。 // 这里我们假设主应用部署在网站的根路径所以用 /。 // 如果你的主应用部署在 /admin 子路径下则需要设置 history: createWebHistory(/admin) history: createWebHistory(), routes }) export default router4.2 在主入口中集成路由接下来修改src/entries/main/main.js文件将路由实例挂载到 Vue 应用上。// src/entries/main/main.js import { createApp } from vue import App from ./App.vue import router from ./router // 引入路由配置 const app createApp(App) app.use(router) // 使用路由插件 app.mount(#app)同时需要修改src/entries/main/App.vue加入router-view来显示路由对应的组件。!-- src/entries/main/App.vue -- template div idapp !-- 这里可以放导航栏等公共布局 -- nav router-link to/首页/router-link | router-link to/users用户管理/router-link /nav !-- 路由出口匹配的组件将渲染在这里 -- router-view / /div /template script setup // 使用 Composition API /script4.3 关于路由 History 模式的注意事项在单入口 SPA 中我们常使用createWebHistory()来获得干净的 URL。但在多入口项目中特别是当你的不同入口可能部署在同一域名的不同路径下时需要格外小心。场景一所有入口部署在根目录。就像我们上面配置的每个入口都是一个独立的 HTML 文件如main.html和about.html。此时主应用main入口内部的路由使用createWebHistory()是没问题的。用户访问http://your-site.com/main.html进入应用然后在前端路由间切换如/users。但是如果你直接刷新http://your-site.com/users这个页面服务器会返回 404因为服务器上不存在/users这个文件或路由。这就需要服务器配置如 Nginx 的try_files将所有前端路由请求都回退到main.html。场景二入口部署在子路径。例如主管理后台部署在/admin路径下。那么你的main入口最终访问地址可能是http://your-site.com/admin/main.html。此时Vue Router 的 base 需要与之匹配createWebHistory(/admin)。同时Vite 的base配置项在vite.config.js中也应该设置为/admin/以确保资源路径正确。实操心得对于多入口项目我个人更倾向于在每个入口内部使用Hash 模式createWebHashHistory()尤其是在开发环境或对 SEO 要求不高的后台系统。因为 Hash 模式URL 带#完全由前端控制不会和服务器路径产生任何冲突部署起来最简单无需额外的服务器配置。你可以根据项目实际部署环境灵活选择。5. 进阶优化自动化生成入口与公共代码抽离当入口页面越来越多手动在vite.config.js里添加每一个input项会变得非常繁琐。我们可以利用 Node.js 脚本自动扫描src/entries目录来动态生成配置。5.1 动态生成多入口配置修改vite.config.jsimport { defineConfig } from vite import vue from vitejs/plugin-vue import { resolve } from path import fs from fs import path from path // 自动扫描 entries 目录下的所有 index.html 作为入口 function getEntryPoints() { const entriesDir resolve(__dirname, src/entries) const entryPoints {} // 递归或非递归读取目录这里用非递归示例 const entryFolders fs.readdirSync(entriesDir, { withFileTypes: true }) .filter(dirent dirent.isDirectory()) .map(dirent dirent.name) for (const folder of entryFolders) { const indexPath path.join(entriesDir, folder, index.html) if (fs.existsSync(indexPath)) { // 使用文件夹名作为入口名 entryPoints[folder] resolve(__dirname, indexPath) } } return entryPoints } export default defineConfig({ plugins: [vue()], resolve: { alias: { : resolve(__dirname, src), }, }, build: { rollupOptions: { input: getEntryPoints(), // 使用函数返回值 output: { entryFileNames: assets/js/[name]-[hash].js, chunkFileNames: assets/js/[name]-[hash].js, assetFileNames: assets/[ext]/[name]-[hash].[ext] } } } })这样以后你只需要在src/entries下新建一个文件夹如contact/并放入index.html和对应的 JS 文件它就会自动被纳入构建无需修改 Vite 配置。5.2 公共依赖抽离在多入口项目中不同的页面可能会用到相同的第三方库如 Vue、Lodash、Axios。如果不做处理每个入口打包出来的 JS 都会包含这些库的代码导致总体积变大。我们可以利用 Rollup 的manualChunks功能来手动分割这些公共模块。修改vite.config.js中的build.rollupOptions.outputexport default defineConfig({ // ... 其他配置 build: { rollupOptions: { input: getEntryPoints(), output: { entryFileNames: assets/js/[name]-[hash].js, chunkFileNames: assets/js/[name]-[hash].js, assetFileNames: assets/[ext]/[name]-[hash].[ext], // 手动分包策略 manualChunks(id) { // 将 node_modules 中的依赖包单独打包 if (id.includes(node_modules)) { // 可以进一步细化将特定的、较大的库单独打包 if (id.includes(vue)) { return vendor-vue } if (id.includes(lodash)) { return vendor-lodash } // 其他依赖打包到一个通用的 vendor 块中 return vendor } // 如果你的项目有大量的公共工具函数或组件也可以在这里进行抽离 // if (id.includes(src/utils) || id.includes(src/components)) { // return common // } } } } } })这样配置后打包会生成vendor-vue.js、vendor-lodash.js、vendor.js等文件。浏览器在加载不同页面时如果这些公共 chunk 已经缓存过就无需重复下载显著提升加载速度。6. 开发、构建与部署实战6.1 开发环境运行配置完成后运行npm run dev。Vite 会启动开发服务器。由于我们移除了根目录的index.html直接访问localhost:5173会列出项目目录。你有几种选择访问具体的入口文件如http://localhost:5173/src/entries/main/index.html。在根目录保留一个简单的index.html作为导航页里面用链接指向各个入口。配置 Vite 的server.open和server.proxy如果需要来提升开发体验。例如可以在vite.config.js中添加export default defineConfig({ // ... 其他配置 server: { open: /src/entries/main/index.html, // 启动后自动打开主入口 // proxy: { ... } // 配置 API 代理 } })6.2 生产环境构建运行npm run build。Vite 会根据配置为每个入口单独打包。构建产物默认输出到dist目录结构大致如下dist/ ├── assets/ │ ├── js/ │ │ ├── main-xxx.js # 主入口代码 │ │ ├── about-xxx.js # about入口代码 │ │ ├── vendor-vue-xxx.js # 抽离的 Vue 库 │ │ └── vendor-xxx.js # 其他公共依赖 │ └── css/ ... # 样式文件 ├── main.html # 主入口 HTML └── about.html # about 入口 HTML你可以清晰地看到每个入口都有自己独立的 HTML 和对应的 JS 资源公共库也被抽离出来。6.3 部署注意事项部署时你需要将整个dist目录上传到你的 Web 服务器如 Nginx、Apache。对于纯静态页面如about.html直接访问对应 URL 即可例如https://your-domain.com/about.html。对于带有前端路由的 SPA 入口如main.html如果使用Hash 模式部署最简单无需任何服务器配置。如果使用History 模式必须配置服务器将所有非静态资源文件的请求重定向到该入口的 HTML 文件。以 Nginx 为例针对main.html入口的配置可能如下location / { # 首先尝试作为文件或目录访问如果找不到则重写到 main.html try_files $uri $uri/ /main.html; }这意味着当用户访问https://your-domain.com/users时Nginx 会先查找dist/users文件或目录找不到则返回dist/main.html然后由前端路由接管。如果你的多个 SPA 入口部署在同一域名下服务器配置会复杂一些可能需要根据路径前缀如/admin/,/app/进行不同的重写规则配置。7. 常见问题与排查技巧实录在实际操作中你肯定会遇到一些坑。这里我总结几个最常见的问题和解决方法。7.1 开发服务器热更新HMR失效问题描述修改了某个入口的 Vue 组件浏览器没有自动刷新。排查思路检查文件路径确保你的组件文件在正确的目录下并且被入口 JS 正确导入。Vite 的 HMR 基于模块依赖图如果文件没有被任何活动入口引用HMR 不会触发。检查 Vite 插件确保vitejs/plugin-vue已正确安装和配置。这个插件负责处理.vue文件的 HMR。检查 HTML 文件确认 HTML 文件中的script标签src属性指向了正确的入口 JS 文件并且使用的是相对路径或能被 Vite 正确解析的路径。路径错误会导致整个入口模块不被 Vite 正确追踪。7.2 构建后资源路径 404问题描述本地开发正常但构建后上传到服务器页面可以打开但图片、字体等资源加载失败404。排查思路检查public目录放在public目录下的静态资源在代码中应该使用绝对路径引用如/img/logo.png。构建时public下的文件会被原封不动地复制到dist根目录。如果你的项目部署在子路径如/my-app/则需要使用base公共路径或者将资源放在assets目录下通过模块化引入。检查assets目录引用在 Vue 组件或 JS 中通过模块化导入的assets资源Vite 会处理并生成带哈希的文件名。确保你在模板或样式中引用的是导入后的变量而不是硬编码的字符串路径。正确示例Vue SFC:template img :srclogoUrl altlogo /template script setup import logoUrl from /assets/logo.png // 正确模块化导入 /script错误示例:template img src/assets/logo.png altlogo !-- 错误模板中的 别名在构建后可能无法解析 -- /template检查vite.config.js中的base配置如果你的项目部署在非根路径如https://example.com/my-project/必须在vite.config.js中设置base: /my-project/。这个值会被 Vite 用于所有资源 URL 和打包产物的路径前缀。7.3 路由跳转或页面刷新报错 404History 模式问题描述在使用了createWebHistory()的 SPA 入口内点击页面内链接跳转正常但手动刷新浏览器或直接输入 URL 访问某个子路由如/users时服务器返回 404。根本原因这个路径/users在你的服务器上并不存在对应的物理文件。服务器在收到这个请求时试图去寻找dist/users文件或目录当然找不到。解决方案如第 6.3 节所述必须在 Web 服务器Nginx、Apache、Express 等上配置“回退路由”将所有非静态文件请求都指向该 SPA 入口的 HTML 文件。这是使用 History 模式必须做的服务器端配置。7.4 多个入口间如何共享状态或组件问题描述main入口和about入口是完全独立的 Vue 应用它们无法直接共享 Vuex/Pinia 状态或通过事件总线通信。解决方案共享逻辑而非状态将可复用的工具函数、自定义 Hook、业务逻辑封装成独立的 ES 模块放在src/utils或src/composables目录下供各个入口的 JS 文件导入使用。共享 UI 组件将通用的 Vue 组件放在src/components/common目录下各个入口按需导入。状态共享需求如果两个独立页面间真的有强烈的状态同步需求这种情况较少可能需要重新评估架构。或许它们本应属于同一个 SPA 入口下的不同路由。如果必须独立可以考虑使用浏览器本地存储LocalStorage/SessionStorage或URL 查询参数进行简单的数据传递或者使用window.postMessage进行跨页面通信前提是页面间有明确的打开关系。7.5 打包后入口 HTML 文件中的资源引用路径不对问题描述构建后的dist/main.html中script或link标签的src/href路径不正确缺少assets目录或哈希值。排查思路这通常由vite.config.js中build.rollupOptions.output的配置与 Vite 的base配置不匹配导致。确保你的output.entryFileNames等路径配置与最终访问的 URL 结构一致。Vite 会自动根据这些配置和base设置在 HTML 中注入正确的资源路径。一个常见的错误是在配置了base: /sub-path/后output配置却写成了entryFileNames: js/[name].js这可能导致构建时代码生成在dist/js/但 HTML 引用的是/sub-path/assets/js/...路径对不上。检查构建日志和最终的dist/index.html文件内容是最直接的调试方法。配置多入口的过程就像搭积木一开始可能会觉得步骤繁多但一旦理解了每个配置项的作用和它们之间的关联就会变得非常清晰。这种架构为项目提供了极大的灵活性特别适合那些由多个相对独立功能模块组成的中大型项目。从简单的活动页面到复杂的后台系统都可以被很好地组织在一起又能保持开发和部署的独立性。