Vite 多页面打包部署

3,923 阅读6分钟

目录结构

根据 Vite 官网多页面应用模式介绍:

├── package.json
├── vite.config.js
├── index.html
├── main.js
└── nested
    ├── index.html
    └── nested.js

这样的目录结构显然不能让人满意。

于是参考网上的做了一番改造,项目最终目录结构大致如下:

├── .husky
├── build
├── node_modules
└── src
    ├── apis
    ├── assets
    ├── components
    └── pages
        └── signin
            ├── App.vue
            ├── index.html
            └── main.ts
        └── about
            ├── App.vue
            ├── index.html
            └── main.ts
    └── router
    ├── types
    ├── utils
    ├── App.vue
    ├── index.html
    ├── main.ts
    ├── style.css
    └── vite-nv.d.ts
└── .commitlintrc.json
├── .edtorconfig
├── .env
├── .env.development
├── .env.production
├── .env.testing
├── .eslintignore
├── .eslintrc
├── .eslintrc-auto-import.json
├── .gitignore
├── .prettierignore
├── .prettier
├── auto-import.d.ts
├── components.d.ts
├── package.json
├── pnpm-lock.yaml
├── README.md
├── stats.html
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.js

打包配置

vite.config.ts 配置,主要配置 build.rollupOptions:

rollupOptions: {
  input: {
    entry: resolve(__dirname, 'src/index.html'),
    signin: resolve(__dirname, 'src/pages/signin/index.html'),
    about: resolve(__dirname, 'src/pages/about/index.html'),
  },
  output: {
    assetFileNames: 'assets/[name]-[hash].[ext]', // 静态资源
    chunkFileNames: 'js/[name]-[hash].js', // 代码分割中产生的 chunk
    entryFileNames: 'js/[name]-[hash].js', // 指定 chunks 的入口文件
    compact: true,
    manualChunks: (id: string) => {
      if (id.includes('node_modules')) {
        return id
          .toString()
          .split('node_modules/')[1]
          .split('/')[0]
          .toString() // 拆分多个 vendors
      }
    },
  },
},

input 是配置打包的入口文件,不过在生成打包的 HTML 资源文件时,使用文件已解析的路径 ID。 output 是配置生成文件的文件名格式,以及分割代码相关配置。

其中 rollup 的相关配置参考:rollupjs.org/configurati…

打包原理

在设置了以上的目录文件之后,还要修改相应的内容。

src/index.html 是最外层的主页,src/pages/signin/index 是子页面入口。相当于每一个页面都可以实例化 Vue。

  • index.html:页面的主 HTML 文件,引入 main.ts。定义 Vue 实例挂载的 DOM 元素。
  • main.ts:页面的入口文件,由 index.html 引入。实例化 Vue 并将其挂载到 index.html DOM 中。
  • App.vue:主组件文件,通常作为其他组件的父组件。

因为每一个页面都相当于一个 Vue 项目,所以又分别可以引入 Router、Vuex 等,这样它们又有自己的路由系统和全局状态管理。如果只是简单的一个页面,没有路由跳转,就不需要 Router 了,App.vue 可以作为页面组件。

打包结果

打包生成的结构如:

image.png

打包后的页面地址:/index.html

子页面地址:/pages/signin/index.html

注意

很多文章都说 base 要设置为 '/',但是如果打包文件不在服务器根目录,应该设置为:'./'。这和网上很多关于打包后白屏问题的原因一样,路径不对。

更新 2023年11月15日19:28:46

更新几点做了又忘了,或者值得说明的:

  1. 在 Vite 文档的开始就说明了在开发期间 Vite 是一个服务器,index.html 是项目的入口文件,默认位于根目录下,而不是在 /src 下
  2. Vite 有“根目录”的概念,即服务文件的位置,在配置文件 vite.config.js 中可以通过 root 进行配置。
  3. 我们可以设置根目录,比如:root: './src/'
  4. 当使用多页面时,文档所说的:

    如果你指定了另一个根目录,请记住,在解析输入路径时,__dirname 的值将仍然是 vite.config.js 文件所在的目录。因此,你需要把对应入口文件的 root 的路径添加到 resolve 的参数中。

指的是如果指定了根目录,路径也需要相对最外层(vite.config.js 文件所在的目录)

input: {
  entry: resolve(__dirname, 'src/index.html'),
  signin: resolve(__dirname, 'src/pages/signin/index.html'),
},

区别于:

input: {
  main: resolve(__dirname, 'index.html'),
  nested: resolve(__dirname, 'nested/index.html'),
},

vite.config.js 是在最外层,找不到 index.html,而应该找 src/index.html

  1. 这样设置多页面:
input: {
  entry: resolve(__dirname, 'src/index.html'),
  signin: resolve(__dirname, 'src/pages/signin/index.html'),
},

目录结构:

└── src
    └── pages
        └── signin
            ├── App.vue
            ├── index.html
            └── main.ts

打包出来的路径:build/pages/signin/index.html

访问地址:/pages/signin/index.html

如果想要直接访问地址怎么设置?/invite/index.html

input: {
  entry: resolve(__dirname, 'src/index.html'),
  signin: resolve(__dirname, 'src/signin.html'),
},

目录结构:

└── src
    └── pages
        └── signin
            ├── App.vue
            └── main.ts
    ├── index.html
    ├── signin.html
    ├── App.vue
    ├── main.ts
    ├── style.css

这样就可以直接访问:/invite.html 打包出来的路径:build/invite.html

这样地址中就不会带上 pages 这样的路径。缺点是所有的页面都要放在 /src/ 下,比较分散,好在 index.html 都是入口文件而已,几乎没有内容。

  1. 所以 Vite 的多页面设置还是很灵活的,除了上面说的每个页面都是一个 Vue 实例,有自己的路由等等,而且每一个页面又可以多页面,比如 /pages/signin/ 下面不只有 index.html,还可以有其他页面。总之是很自由,可以结合具体项目改造。

更新 2023年11月24日16:28:09 关于根目录关于配置

最近在搞一个奇葩的小问题,APP 调用系统自带的分享功能(反正我不知道是什么,又看不到代码),分享链接。然后分享时,iOS 上会显示连接的预览:图标、标题、简介。安卓上没有那部分。

标题应该就是 HTML 的 title,描述显示的是主域名,图标显示了一个指南针的 icon,类似 iOS 浏览器那个吧。问题是产品要求这个图标要显示业务的图,而且确定可以做到。

问了 APP,确定不是他们要做的,问了之前的开发,也说不知道怎么处理。我的直觉告诉我应该是网页 favicon 相关的东西。favicon 这玩意真的是 10 年都不弄它了。搜了一下怎么添加,用 png, 去转换 ico 图,怎么都不能在浏览器 Tab 上显示出来,如果这个都做不到,也不用去 APP 分享试了。后来新建了 Vite 的项目,用它的 svg,也显示不出来,这就奇怪了。

反复看官网中的静态资源处理-public 目录

目录默认是 <root>/public,但可以通过 publicDir 选项 来配置。

publicDir 选项

该值可以是文件系统的绝对路径,也可以是相对于项目根目录的相对路径。

root 选项

项目根目录(index.html 文件所在的位置)。可以是一个绝对路径,或者一个相对于该配置文件本身的相对路径。

index.html 与项目根目录

注意 Vite 同时会解析项目根目录下的 配置文件(即 vite.config.js,因此如果根目录被改变了,你需要将配置文件移动到新的根目录下。

突然想到我的多页面配置,更改了根目录,root: './src/', base: './',。试着将 /public 移动到 /src/public,然后 favicon 就显示出来了。

但是我一直对 因此如果根目录被改变了,你需要将配置文件移动到新的根目录下 不能理解,因为目前我的配置文件仍然在最外层而不是设置的根目录 ./src/ 下。

然后顺利的,favicon 被打包进根目录之后,就能在 APP 分享预览中显示了~

另外分享个小知识,favicon 并不需要 link 写在页面中,只要网站根目录有,就能读取到。

更新 2024年5月17日18:08:25 无法读取环境变量

Vite 中应用环境变量的步骤:

.env.developement:

VITE_API_BASE_URL=http://localhost:8000

在代码中读取:import.meta.env.VITE_API_BASE_URL

就这么简单,可是却是 undefined。看文档看文章都只是这样而已啊,实在看不出来问题。

打印:import.meta.env,正常的默认字段,没有新设置的变量

由于项目是 Vite 多页面应用,怀疑是多页面相关的问题。

最后又回到了 root 根目录的问题上,由于我已经设置 root: './src/', 所以将环境文件放到 src 下就好了。

.env 文件: 文档第一句话:

Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量:

它默认是 root,既然 root 已经改了,所以 envDir 也改变了。

环境目录也是可以设置的:

envDir: resolve(__dirname),

这样环境文件就可以放到 vite.config.ts 同层级。