Vite2.0项目的类mono-repo实践(一仓库多项目,独立打包)

3,033 阅读2分钟

注:本实践不是mono-repo。只是为了大量的组件和逻辑复用,强行将两个项目放在了一个仓库,并做了独立打包。

需求

需求是在一个月之内上线一个公司内部使用的管理平台,包含前台网站和管理后台网站。

实现

由于两个网站有很多相似的地方,出于开发速度、复用、维护性的考虑,就把它们放在了一个git仓库。

目录结构

前台项目:customer(简化名)
管理后台:admin(简化名)

|-- project
    |-- vite.config.admin.ts
    |-- vite.config.ts
    |-- app
    |   |-- admin
    |   |   |-- App.vue
    |   |   |-- index.html
    |   |   |-- main.ts
    |   |   |-- public
    |   |       |-- favicon.ico
    |   |-- customer
    |       |-- App.vue
    |       |-- index.html
    |       |-- main.ts
    |       |-- public
    |           |-- favicon.ico
    |-- env
    |   |-- admin
    |   |   |-- .env
    |   |   |-- .env.development
    |   |   |-- .env.production
    |   |   |-- .env.test
    |   |-- customer
    |       |-- .env
    |       |-- .env.development
    |       |-- .env.production
    |       |-- .env.test
    |-- src
    |   |-- api
    |   |   |-- common.ts
    |   |   |-- admin
    |   |   |-- customer
    |   |   |-- sys
    |   |-- components
    |   |   |-- common
    |   |   |-- admin
    |   |   |-- customer
    |   |-- style
    |   |   |-- useClass.ts
    |   |-- hooks
    |   |-- layouts
    |   |   |-- admin
    |   |   |-- content
    |   |   |-- customer
    |   |   |-- menu
    |   |-- router
    |   |   |-- index.ts
    |   |   |-- types.ts
    |   |   |-- guard
    |   |   |-- menus
    |   |   |   |-- index.ts
    |   |   |   |-- admin
    |   |   |   |-- customer
    |   |   |-- routes
    |   |       |-- common.ts
    |   |       |-- admin
    |   |       |-- customer
    |   |-- views
    |       |-- admin
    |       |-- customer
    |       |-- sys
    |           |-- error
    |           |-- login
    |               |-- Login.vue

vite配置

package.json

主要是scripts的配置,如下:
通过--config来指定配置文件。本仓库前台项目是默认项目,直接用vite脚手架生成的默认配置(即vite.config.ts)。

"scripts": {
    "dev": "vite",
    "test": "vite --mode test",
    "build": "vite build",
    "serve": "vite preview",

    "admin": "vite --config vite.config.admin.ts",
    "atest": "vite --mode test --config vite.config.admin.ts",
    "build:admin": "vite build --config vite.config.admin.ts",
    "serve:admin": "vite preview --config vite.config.admin.ts",
  },

vite.config*.ts

为了兼容两个项目,vite配置需要修改的地方如下:

  1. 入口
  2. 环境变量目录
  3. 打包输出目录
customer的vite.config.ts:
export default ({ mode, command }: ConfigEnv): UserConfig => {
  const env = loadEnv(mode, pathResolve('env/customer/'))
  return {
    root: pathResolve('app/customer'),//1. 修改入口,默认是项目根目录下的index.html
    base: env.VITE_PUBLIC_PATH,
    envDir: pathResolve('env/customer/'),//2. 修改环境变量入口,默认是根目录下的env目录
    build: {
      outDir: pathResolve('dist'),//3. 修改打包输出,默认是root文件夹下面的dist目录,比如/app/customer/dist
      emptyOutDir: true
    },
  }
}
admin的vite.config.admin.ts:
export default ({ mode, command }: ConfigEnv): UserConfig => {
  const env = loadEnv(mode, pathResolve('env/admin/'))
  return {
    root: pathResolve('app/admin'),
    base: env.VITE_PUBLIC_PATH,
    envDir: pathResolve('env/admin/'),
    build: {
      outDir: pathResolve('dist-admin'),
      emptyOutDir: true
    },
  }
}

经过上面的配置,就能独立的跑起来两个项目了。

一些实践

区分项目

在项目中如何区分运行的是哪个项目呢?可以通过import.meta.env.VITE_GLOBAL_APP_NAME
当然env里面的变量是可以在env目录中自定义的,比如:

# env/admin/.env
VITE_GLOBAL_APP_NAME = ADMIN

# env/customer/.env
VITE_GLOBAL_APP_NAME = CUSTOMER

条件导入路由表

两个项目的路由表是不一样的,条件导入如下:

if (import.meta.env.VITE_GLOBAL_APP_NAME === AppNameEnum.CUSTOMER) {
  routesModule = import.meta.globEager('./routes/customer/index.ts');
} else {
  routesModule = import.meta.globEager('./routes/admin/index.ts');
}

css类名

对于各自私有的类名可通过如下方法生成:

import { AppNameEnum } from '@/enums/appEnum';

export const prefixMap = {
  [AppNameEnum.CUSTOMER]: 'customer',
  [AppNameEnum.ADMIN]: 'admin'
};

export function useClass(scope: string) {
  const prefix = prefixMap[import.meta.env.VITE_GLOBAL_APP_NAME];
  return `${prefix}-${scope}`;
}

总结

本实践相对于mono-repo还是有不少缺点的,比如:不能独立的版本管理;两个项目共用一个package.json,造成依赖冗余...
建议大型项目使用lerna来生生成和管理mono-repo。