Vite 多页面实战
转自稀土掘金 “我想写文章啊” 的《真正优雅的 Vite 多页面实战》
Vite如何支持多页?
官方文档对于多页模式的描述中说到,如果想要新建一个单页,只需要在项目根目录(注意:不是 src 目录!)新建:
- nested
- index.html
- main.js
 
- package.json
- vite.config.js
然后通过访问:http://localhost:5173/nested/即可。
这里要说明两个问题:
为什么在根目录新建目录,而不是 src 目录?
因为vite启动时,会以根目录启动一个开发服务器,可以简单理解为开发服务器托管了整个项目的文件(但其实内部做了一些其他处理如处理public目录等)。
因此,在项目目录下的文件,都可以通过链接访问到,你可以访问http://localhost:5173/package.json 试试。
所以,你可以在任意目录放置自己的单页,但是开发时的访问地址也必须受限于单页的目录结构,如果你的单页入口文件放在src/hello/index.html ,也就只能通过http://localhost:5173/src/hello/ 来进行访问。
为什么使用 /nested/ 访问而不是 nested?
访问/nested/ 相当于访问nested目录下的入口文件(一般静态服务器都会将 index.html 作为入口文件)。
官方例子不符合实际开发场景
vite官方给出的项目模板是这样的:
- src
- main.js
- App.vue
 
- index.html
- package.json
- vite.config.js
按照官方例子,如果要新增一个单页,需要这样组织目录结构:
- src
- main.js
- App.vue
 
- nested
- index.html
 
- index.html
- package.json
- vite.config.js
新的单页居然与src 目录是平行的!这显然不符合正常的项目结构,一般而言,开发相关的源代码都会放在src目录下。因此,这个方案不适用,废弃。
那么,试试看第二种方案:
- index
- index.html
- main.js
- App.vue
 
- nested
- index.html
- main.js
- App.vue
 
- package.json
- vite.config.js
这种方案的好处是,单页互为平行关系,而且可以通过访问/index/与/nested/访问到对应的单页。
但是,其缺点就是:如何管理公共资源?
例如,index 单页与nested单页都使用到同一款公共组件,应该放在哪里管理呢?
显然,这种项目结构缺陷明显,因此,这个方案也不适用,废弃。
项目结构最佳实践
按照以往的开发经验,这样组织项目结构可以让开发的可扩展达到最好:
- src
- components
- pages
- index
- index.html
- main.js
- App.vue
 
- nested
- index.html
- main.js
- App.vue
 
 
- index
 
- package.json
- vite.config.js
这种项目结构,可以在src目录下管理多个单页需要使用到的公共资源,引用非常方便,并且扩展新的单页也非常方便。
但是!这种结构下,访问对应的单页需要使用http://localhost:5173/src/pages/index/ 来访问,这种开发体验虽说不好,但尚且能忍受。最致命的问题是,当我们在vite.config.js中配置多页打包时:
export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        index: path.resolve(__dirname, 'src/pages/index/index.html'),
        nested: path.resolve(__dirname, 'src/pages/nested/index.html'),
      }
    }
  }
})
打出来的包是这样的:
- dist
- src
- pages
- index
- index.html
 
- nested
- index.html
 
 
- index
 
- pages
- assets
 
- src
也就是说,线上用于也必须通过http://localhost/src/pages/index/ 来访问!这显然不符合预期,我们预期的输出应该是:
- dist
- assets
- index.html
- nested.html
 
为了解决这个问题,网上的一篇热门文章,采用了一种更改打包后的文件路径的方法来解决此问题,此方案的基本思路是:
- 将src/pages下所有子目录下的index.html文件复制到根目录,并且改名为其父目录的名称,如src/pages/nested/index.html复制到跟目录,改名为nested.html
- 将nested.html下的样式、js等引用改为正确的引用路径。
这个方案看似解决了打包后的路径问题,但是给开发人员带来了额外的认知成本,虽勉强解决了问题,但思路不够优雅。(居然是google搜索“vite 多页面”的第一篇文章,有点误人子弟了。)
优雅的解决方案
首先,我们的项目结构必须以此为准:
- src
- pages
- index
- nested
 
 
- pages
- package.json
- vite.config.js
其次,打包出来的结构必须以此为准:
- dist
- assets
- index.html
- nested.html
 
为了实现第二点,必须要求项目根目录下至少有这两个文件:
- src
- index.html
- nested.html
- package.json
- vite.config.js
然后,在src/pages/index目录下,不放置index.html,只放main.js、App.vue等,让src/index.html 反向引用src/pages/index/main.js即可:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/pages/index/main.js"></script>
  </body>
</html>
至此,vite多页面架构的问题已完美解决!
总结
- 官方文档虽然权威,但不一定适合所有场景
- 在框架内解决问题,勉强的方案虽能解决问题,但后患无穷
