【大厂企业级项目架构】之vite基础配置与多环境区分

2,638 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

这是大厂企业级项目架构系列的第三篇,这系列的文章如下:

之前的文章详细讲解了如何接入代码和提交规范,其实这些对我们项目的最终构建结果影响都不算大,目前已经完成的内容如下,如果有不清楚的,可以点上面的链接看相关内容。

  • 首先是技术选型,比如框架选型(Vue|React等),构建工具,包管理工具等等。
  • 代码规范,提交规范
  • 环境区分,包括开发、测试、生产的构建区分等
  • 样式方案,图标方案,布局方案,组件库等
  • 基础能力,包括路由、状态管理、持久化、网络请求等
  • mock方案
  • 根据自身的业务类型来封装相应的公共模块
  • 单元测试
  • 自动化构建部署
  • ....

从这篇文章开始正式进入正题,接下来的内容都是会对我们项目最终的构建结果产生影响的。本篇先讲解vite的基础配置以及如何区分环境构建和开发。

为什么用vite

毫无疑问,vite是当下最热门和最高效的前端构建工具之一。

从开发角度来讲,相较于传统老大哥webpackvite大大地提高了开发体验和效率。不管是冷启动还是热更新,vite都比webpack快上不少。因为webpack必须对整个工程进行构建后才能启动,而vite借助浏览器原生支持的ESM,使得源码在真正被使用时才做处理。而对于项目依赖,vite使用esbuild进行与构建,同样比js编写的打包器快上很多。

而从生产的角度来讲,vite使用rollup进行生产的构建打包,同样可以像webpack一样配合babel等工具使用,对于构建出来的生产包质量是可以信赖的。

当然webpack依然是最流行的构建工具,生态完善,有各种各样的插件、loader,对于生产的构建仍然还是比vite有优势的,毕竟生产环境我们要求稳,确保质量。所以有很多人也会用vite做开发构建,用webpack做生产打包

最后,我们作为一个新项目,没有什么负担和迁移成本,而且只要测试做的足够充分,同样可以保证线上的质量,所以大胆上vite吧。

开始配置

我不会从头到尾介绍一遍vite是如何使用的,这些都可以在vite官网上自行查阅。也不会一开始就把vite配置的很完整,因为项目刚开始,后面可能会遇到各种业务场景,会根据需要做相应的调整和配置。这里只做较为通用和基础的配置。

通过脚手架生成的vite配置只有以下短短几行,虽然本身vite已经内置支持了很多特性,可以开箱即用,但是还不足以应对后续日益复杂的项目,我们需要增加一些基础配置。

// vite.config.ts 
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
    plugins: [vue()],
});

多环境区分的必要性

通常我们一个项目版本的完整周期,至少都需要经过开发、测试、发布上线这几个阶段。开发阶段我们在自己的本地环境进行开发调试,测试阶段就会发布到测试环境进行各种测试,到最后测试完成则正式发布到生产环境,当然,再完整点的还会有预发布环境等。

因此,环境区分是很有必要的,举几个例子:

  • 测试环境和生产环境的域名和接口路径不一样
  • 测试环境我们可能需要vConsole定位问题,生产环境则不能把vConsole打到发布包里
  • 测试环境的图片我们发到CDN上,但是生产环境我们可能会发到专用的图片存储服务,所以这里的路径也要区分
  • ....

注入环境变量

区分环境的起点是什么呢?通常我们是通过命令参数去注入环境,比如说

# 测试环境 sit这个名字根据你自己的叫法定义即可
vite build --mode sit
# 生产环境
vite build --mode prod

此时vite就可以根据这个环境是sit还是prod去读取相应的配置文件了。

vite环境模式

vite使用dotenv来从我们的项目目录下读取相应的配置文件,从而获取额外的环境变量。什么意思呢?就是我们需要在项目根目录下新建以下几个.env配置文件

.env        # 所有情况下都会加载
.env.sit    # 只在 --mode sit的时候加载,对应测试环境的配置
.env.prod   # 只在 --mode prod的时候加载,对应生产环境的配置

然后就可以在里面写我们的环境变量了,举例如下:

# .env.sit
# 静态资源部署路径
VITE_PUBLIC_PATH = /
# 测试环境使用vConsole
VITE_USE_VCONSOLE = true
# 测试环境域名
VITE_API_DOMAIN = https://yoururl.test.com
# 测试环境api前缀
VITE_API_PREFIX = /testApi
....
# .env.prod
# 静态资源部署路径
VITE_PUBLIC_PATH = /vueApp
# 生产环境不使用vConsole
VITE_USE_VCONSOLE = false
# 生产环境域名
VITE_API_DOMAIN = https://yoururl.com
# 生产环境api前缀
VITE_API_PREFIX = /api
....

注意,只有VITE_开头的变量才会暴露给经过vite构建处理的代码

经过上面的配置后,我们的代码在运行时就可以拿到注入的环境变量了,同时,在构建的时候我们也可以拿到这些环境变量,就可以根据环境做差异化处理了。比如在vite.config.ts中,我们可以拿到环境变量,根据环境去做不同的配置。如下所示:

import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
    // 项目根路径
    const root = process.cwd();
    // 通过vite提供的工具方法去加载相应环境的配置
    // 这里的mode其实就是我们 --mode prod的prod
    const { VITE_PUBLIC_PATH } = loadEnv(mode, root);
    return {
        // 这里根据环境不同,设置不同的静态资源部署路径
        base: VITE_PUBLIC_PATH,
    };
});

假如我们在源码里也需要根据环境做差异化处理,则可以像下面一样使用

<script setup lang="ts">
import { ref } from 'vue';
defineProps<{ msg: string }>();
// 通过import.meta.env.XXX拿到环境变量
const apiUrl = ref(`${import.meta.env.VITE_API_DOMAIN}`);
if (import.meta.env.VITE_XXX) {
    // 做差异化处理
}
</script>

同时,为了有良好的ts提示,我们在src目录下新增types目录,然后新增global.d.ts类型定义文件,内容如下:

interface ImportMetaEnv {
    readonly VITE_PUBLIC_PATH: string;
    readonly VITE_API_PATH: string;
    // 更多环境变量...
}

interface ImportMeta {
    readonly env: ImportMetaEnv;
}

此时我们就可以在编辑器里有只能提示了。

其他配置

上面做好了多环境区分之后,我们就可以来做一些基础配置了

首先我们需要支持jsx的写法,所以需要增加相应的插件@vitejs/plugin-vue-jsx

import { defineConfig, loadEnv } from 'vite';
// 默认生成的配置只支持模板语法
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
// 通过defineConfig可以给我们提供类型提示
export default defineConfig(({ mode }) => {
    // 项目根路径
    const root = process.cwd();
    // 通过vite提供的工具方法去加载相应环境的配置
    // 这里的mode其实就是我们 --mode prod的prod
    const { VITE_PUBLIC_PATH } = loadEnv(mode, root);
    return {
        // 这里根据环境不同,设置不同的静态资源部署路径
        base: VITE_PUBLIC_PATH,
        // 增加jsx插件来支持jsx的写法
        plugins: [vue(), vueJsx()]
    };
});
路径别名

然后我们可以通过alias配置一些路径别名,如下

return {
    // 这里根据环境不同,设置不同的静态资源部署路径
    base: VITE_PUBLIC_PATH,
    // 增加jsx插件来支持jsx的写法
    // 后面如果需要增加更多的插件,我们会单独抽离出一个方法来获取插件
    plugins: [vue(), vueJsx()],
    resolve: {
        alias: [
            // import xxx from '@/path' -> import xxx from 'src/path'
            {
                find: /@/,
                replacement: resolvePath('src'),
            },
            // ...其他更多的路径别名可以在这里继续配
        ],
    },
};
构建选项

继续配置构建选项,如下:

{
    // ...
    build: {
        // 我们的构建产物需要兼容到es6
        target: 'es2015',
        // 假如要兼容安卓微信的webview
        cssTarget: 'chrome61',
        // 非生产环境下生成sourcemap
        sourcemap: mode !== 'prod',
        // 禁用gzip 压缩大小报告,因为压缩大型文件可能会很慢
        reportCompressedSize: false,
        // chunk大小超过1500kb是触发警告
        chunkSizeWarningLimit: 1500,
    },
}
less配置

本项目打算使用less作为预处理器,本身vitesass|less内置就支持了,其实我们什么都不用做就可以使用,后续如果需要修改主题等,再修改配置。

开发服务器配置

我们需要配置开发服务器来帮我们做请求代理,解决跨域问题

server: {
    // 开启https
    https: true,
    // 监听所有ip地址
    host: true,
    // 端口默认是5173
    port: 6666,
    // 配置代理帮我们转发请求,解决跨域问题
    proxy: {
        // api/开头的请求将被转发到下面的target的地址
        'api/': {
            target: 'https://mock.com',
            // 改变请求头的origin
            changeOrigin: true,
            // 支持代理websocket
            ws: true,
            // 路径重写 相当于把api/去掉
            rewrite: (path) => path.replace(new RegExp(`^api/`), ''),
        }
    },
},
最终配置
import { defineConfig, loadEnv } from 'vite';
// 默认生成的配置只支持模板语法
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
// 通过defineConfig可以给我们提供类型提示
export default defineConfig(({ mode }) => {
    // 项目根路径
    const root = process.cwd();
    // 通过vite提供的工具方法去加载相应环境的配置
    // 这里的mode其实就是我们 --mode prod的prod
    const { VITE_PUBLIC_PATH } = loadEnv(mode, root);
    return {
        // 这里根据环境不同,设置不同的静态资源部署路径
        base: VITE_PUBLIC_PATH,
        // 增加jsx插件来支持jsx的写法
        plugins: [vue(), vueJsx()],
        resolve: {
            alias: [
                // import xxx from '@/path' -> import xxx from 'src/path'
                {
                    find: /@/,
                    replacement: resolvePath('src'),
                },
                // ...其他更多的路径别名可以在这里继续配
            ],
        },
        build: {
            // 我们的构建产物需要兼容到es6
            target: 'es2015',
            // 假如要兼容安卓微信的webview
            cssTarget: 'chrome61',
            // 非生产环境下生成sourcemap
            sourcemap: mode !== 'prod',
            // 禁用gzip 压缩大小报告,因为压缩大型文件可能会很慢
            reportCompressedSize: false,
            // chunk大小超过1500kb是触发警告
            chunkSizeWarningLimit: 1500,
        },
        server: {
            // 开启https
            https: true,
            // 监听所有ip地址
            host: true,
            // 端口默认是5173
            port: 6666,
            // 配置代理帮我们转发请求,解决跨域问题
            proxy: {
                // api/开头的请求将被转发到下面的target的地址
                'api/': {
                    target: 'https://mock.com',
                    // 改变请求头的origin
                    changeOrigin: true,
                    // 支持代理websocket
                    ws: true,
                    // 路径重写 相当于把api/去掉
                    rewrite: (path) => path.replace(new RegExp(`^api/`), ''),
                }
            },
        },
    };
});

总结

经过上面的配置,目前项目已经支持多环境区分以及vite的基础配置了。当然,后面项目开发过程中会遇到更多的业务场景,到时候会对配置做出相应的修改,并且后续会增加很多插件来支撑我们的项目开发,这些留到后续处理具体的场景再说。

文章推荐: