vite打包构建时环境变量(env)生成可配置的js文件

现实需求

在vite开发过程中,一些变量可以放在.env(基础公共部分变量).env.dev(开发环境)、.env.production(生产环境)中管理,通常分成开发和生产两个不同的配置文件管理,方便调试和部署。但是在应用部署在若干个不同的运行环境,则需要修改.env.production中的部分变量(如api地址)重新打包会比较麻烦。

怎么样实现不重新打包的前提下修改配置呢?

答:在build打包时将.env.production和.env文件统一打包成额外的配置js文件,在通过外部挂载的方式做成全局变量即可。

文件结构如下:

1、构建脚本的文件夹结构

在这里插入图片描述

程序内部调用的文件结构

在这里插入图片描述
前提条件
.env文件

VITE_GLOB_APP_TITLE=测试平台
VITE_BASE_URL=/newpath/
VITE_GLOB_APP_SHORT_NAME=GIS_APP

.env.production文件

VITE_API_BASE_URL=/
VITE_LOGIN_API_URL=https://www.baidu.com/login

一、新建打包脚本(build.ts及依赖文件)

脚本目的是在打包目录下生成_app.config.js,效果如下:
在这里插入图片描述
_app.config.js结构如下:

window.__PRODUCTION__APP__CONF__ = {"VITE_GLOB_APP_TITLE": "测试平台","VITE_BASE_URL":"/newpath/","VITE_GLOB_APP_SHORT_NAME": "APP","VITE_API_BASE_URL":"/","VITE_LOGIN_API_URL": "https://www.baidu.com/login"
};
Object.freeze(window.__PRODUCTION__APP__CONF__);
Object.defineProperty(window, "__PRODUCTION__APP__CONF__", {configurable: false,writable: false,
});
1、build.ts
import { runBuildConfig } from "./buildConf"
import pkg from "../../package.json"export const runBuild = async () => {try {const argvList = process.argv.splice(2)if (!argvList.includes("disabled-config")) {runBuildConfig()}console.log(`[${pkg.name}] - 构建成功!`)} catch (error) {console.log("虚拟构建错误:\n" + error)process.exit(1)}
}
runBuild()
2、buildConf.ts文件
/*** 用于打包时生成额外的配置文件。该文件可以配置一些全局变量,这样就可以直接在外部修改,而无需重新打包*/
import fs, { writeFileSync } from "fs-extra"
import { getEnvConfig, getRootPath } from "../utils"
import { getConfigFileName } from "../getConfigFileName"
import pkg from "../../package.json"// 打包脚本的名称
const GLOB_CONFIG_FILE_NAME = "_app.config.js"
// 输出文件的根目录
const OUTPUT_DIR = "dist"interface CreateConfigParams {configName: string;config: any;configFileName?: string;
}function createConfig(params: CreateConfigParams) {const { configName, config, configFileName } = paramstry {const windowConf = `window.${configName}`// 确保变量不会被修改const configStr = `${windowConf}=${JSON.stringify(config)};Object.freeze(${windowConf});Object.defineProperty(window, "${configName}", {configurable: false,writable: false,});`.replace(/\s/g, "")// 拼接新的输出根目录地址const filePath = `${OUTPUT_DIR}${config.VITE_BASE_URL || "/"}`// 创建根目录fs.mkdirp(getRootPath(filePath))writeFileSync(getRootPath(filePath + configFileName), configStr)console.log(`✨ [${pkg.name}]` + ` - 配置文件构建成功:`)console.log(filePath + "\n")} catch (error) {console.log("配置文件配置文件打包失败:\n" + error)}
}export function runBuildConfig() {const config = getEnvConfig()const configFileName = getConfigFileName(config)createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME })
}
3、getConfigFileName.ts文件
/*** 获取配置文件变量名* @param env*/
export const getConfigFileName = (env: Record<string, any>) => {return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || "__APP"}__CONF__`.toUpperCase().replace(/\s/g, "")
}
4、utils.ts文件
import fs from "fs"
import path from "path"
import dotenv from "dotenv"export function isDevFn(mode: string): boolean {return mode === "development"
}export function isProdFn(mode: string): boolean {return mode === "production"
}/*** 是否生成包预览*/
export function isReportMode(): boolean {return process.env.REPORT === "true"
}/*** 读取所有环境变量配置文件到process.env* @param envConf* @returns*/
export function wrapperEnv(envConf: any) {const ret: any = {}for (const envName of Object.keys(envConf)) {let realName = envConf[envName].replace(/\\n/g, "\n")realName = realName === "true" ? true : realName === "false" ? false : realNameif (envName === "VITE_PORT") {realName = Number(realName)}if (envName === "VITE_PROXY" && realName) {try {realName = JSON.parse(realName.replace(/'/g, '"'))} catch (error) {realName = ""}}ret[envName] = realNameif (typeof realName === "string") {process.env[envName] = realName} else if (typeof realName === "object") {process.env[envName] = JSON.stringify(realName)}}return ret
}/*** 获取当前环境下生效的配置文件名*/
function getConfFiles() {const script = process.env.npm_lifecycle_script// eslint-disable-next-line prefer-regex-literalsconst reg = new RegExp("--mode ([a-z_\\d]+)")const result = reg.exec(script as string) as anyif (result) {const mode = result[1] as stringreturn [".env", `.env.${mode}`]}return [".env", ".env.production"]
}/*** 获取以指定前缀开头的环境变量* @param match prefix* @param confFiles ext*/
export function getEnvConfig(match = "VITE_", confFiles = getConfFiles()) {let envConfig = {}confFiles.forEach((item) => {try {const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)))envConfig = { ...envConfig, ...env }} catch (e) {console.error(`解析错误:${item}`, e)}})const reg = new RegExp(`^(${match})`)Object.keys(envConfig).forEach((key) => {if (!reg.test(key)) {Reflect.deleteProperty(envConfig, key)}})return envConfig
}/*** 获取用户根目录* @param dir file path*/
export function getRootPath(...dir: string[]) {return path.resolve(process.cwd(), ...dir)
}

二、修改vite.config.ts文件

脚本目的是将生成_app.config.js在index.html文件中添加引用,效果如下:
在这里插入图片描述

1、安装 vite-plugin-html
yarn add vite-plugin-html -D 

npm i vite-plugin-html -D
2、vite.config.ts中引用createHtmlPlugin和package.json,并添加createHtmlPlugin
import createHtmlPlugin from "vite-plugin-html"
import pkg from "./package.json"

在这里插入图片描述

  const getAppConfigSrc = () => {return `${ENV.VITE_BASE_URL || "/"}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`}
      createHtmlPlugin({minify: isBuild,inject: {data: {title: ""},// Embed the generated app.config.js filetags: isBuild? [{tag: "script",attrs: {src: getAppConfigSrc()}}]: []}})

三、代码内部引用

1、env.ts文件
import { getConfigFileName } from "../../../build/getConfigFileName"
import pkg from "../../../package.json"export function getCommonStoragePrefix() {const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig()return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase()
}/*** 根据版本生成缓存键* @returns*/
export function getStorageShortName() {return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase()
}export function getAppEnvConfig() {const ENV_NAME = getConfigFileName(import.meta.env)// 根据当前运行状态判断取值,如果是开发环境取.env.dev配置,如果是生产环境取_app.config.js引用的全局变量 const ENV = (import.meta.env.DEV ? (import.meta.env as unknown as any) : window[ENV_NAME as any]) as unknown as any// 此处可以进行过滤判断根据业务需求处理,示例未做任何处理const { VITE_GLOB_APP_SHORT_NAME } = ENVif (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {console.log(`VITE_GLOB_APP_SHORT_NAME变量只能是字符/下划线,请在环境变量中修改后重新运行。`)}return ENV
}/*** @description: Development mode*/
export const devMode = "development"/*** @description: Production mode*/
export const prodMode = "production"/*** @description: Get environment variables* @returns:* @example:*/
export function getEnv(): string {return import.meta.env.MODE
}/*** @description: Is it a development mode* @returns:* @example:*/
export function isDevMode(): boolean {return import.meta.env.DEV
}/*** @description: Is it a production mode* @returns:* @example:*/
export function isProdMode(): boolean {return import.meta.env.PROD
}
2、业务调用index.ts文件
import { getAppEnvConfig } from "@mars/hooks/setting/env"export const useGlobSetting = (): Readonly<any> => {const setting = getAppEnvConfig()return setting
}

四、修改package.json文件build命令

1、安装cross-env和esno
yarn add cross-env -D
yarn add esno -D 

npm i cross-env -D
npm i esno -D
1、修改build命令,增加"&& esno ./build/script/postBuild.ts"
"build": "cross-env npm run lint && vite build && esno ./build/script/postBuild.ts",

五、测试命令

在命令行执行下面的语句可以测试生成_app.config.js文件

yarn esno ./build/script/postBuild.ts

以上操作就能生成外部配置并加载了,使用时在开发环境、演示环境、生产环境部署修改_app.config.js中对应的api地址即可!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2822122.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯算法题汇总

一.线性表&#xff1a;链式 例题&#xff1a;旋转链表 二.栈&#xff1a; 例题&#xff1a;行星碰撞问题 三.队列 三.数组和矩阵 例题&#xff1a;

SCP命令行向服务器端上传文件或下载文件

环境要求 使用scp&#xff08;Secure Copy Protocol&#xff09;命令在本地和远程系统之间安全地复制文件和目录&#xff0c;需要满足以下环境要求&#xff1a; SSH服务&#xff1a;scp依赖于SSH&#xff08;Secure Shell&#xff09;协议来安全地传输文件。因此&#xff0c;…

2.2_2 进程调度的时机、切换与过程、调度方式

文章目录 2.2_2 进程调度的时机、切换与过程、调度方式&#xff08;一&#xff09;进程调度的时机&#xff08;二&#xff09;进程调度的方式&#xff08;三&#xff09;进程的切换与过程 总结 2.2_2 进程调度的时机、切换与过程、调度方式 &#xff08;一&#xff09;进程调度…

作业1-224——P1927 防护伞

思路 遍历一下找到两点间的最远距离&#xff0c;直接公式算结果&#xff0c;控制输出位数 参考代码 #include<iostream> #include<iomanip> #include<cmath> using namespace std; int main() { int n; cin>>n; int x[n],y[n]; do…

【开源】JAVA+Vue.js实现APK检测管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 开放平台模块2.3 软件档案模块2.4 软件检测模块2.5 软件举报模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 开放平台表3.2.2 软件档案表3.2.3 软件检测表3.2.4 软件举报表 四、系统展示五、核心代…

第六十七天 APP攻防-Frida反证书抓包移动安全系统资产提取评估扫描

第67天 APP攻防-Frida反证书抓包&移动安全系统&资产提取&评估扫描 知识点&#xff1a; 1、资产提权-AppinfoScanner 2、评估框架-MobSF&mobexler 3、抓包利器-Frida&rOcapture 章节点&#xff1a; 1、信息收集-应用&资产提取&权限等 2、漏洞发现…

Python Web开发记录 Day5:jQuery(JavaScript库)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 五、jQuery1、jQuery-选择器和菜单案例①快速上…

2024年制造业,智能控制与材料科学国际会议(ICMICMS 2024)

全称&#xff1a;2024年制造业&#xff0c;智能控制与材料科学国际会议&#xff08; ICMICMS 2024&#xff09; 会议网址:http://www.icmicms.com会议时间: 2024.03.25截稿时间&#xff1a;2024.03.15会议地点: 昆明投稿邮箱&#xff1a;icmicms_info163.com投稿标题&#xff1…

vue3的router

需求 路由组件一般放在&#xff0c;pages或views文件夹, 一般组件通常放在component文件夹 路由的2中写法 子路由 其实就是在News组件里面&#xff0c;再定义一个router-view组件 他的子组件&#xff0c;机会渲染在router-view区域 路由传参 <RouterLink :to"/news…

退休开便利店真的靠谱吗?2024比较赚钱的创业项目排行

近日多个退休后开便利店赚钱的新闻登上热搜&#xff0c;但是&#xff0c;小编对此有疑问&#xff0c;退休的老年人开便利店真的是一个好选择吗&#xff1f; 第一、便利店最基本的转让费&#xff0c;装修费&#xff0c;进货等等&#xff0c;这笔开支非常大&#xff0c;足以掏空老…

IDC 中搭建 Serverless 应用平台:通过 ACK One 和 Knative 玩转云资源

作者&#xff1a;元毅、庄宇 如何打造云上&#xff08;公共云&#xff09;、云下&#xff08;IDC 数据中心&#xff09;统一的云原生 Serverless 应用平台&#xff0c;首先我们来看一下 ChatGPT 4 会给出什么样的答案&#xff1a; 如何打造云上、云下统一的云原生 Serverless…

Android修行手册-Chaquopy中opencv、numpy的初步应用

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

C/C++ Zlib库调用Minzip来封装MyZip压缩类

文章目录 1、C/C Zlib库调用Minzip来封装MyZip压缩类1.1、类的功能实现1.1.1、ZIP压缩函数 Compress1.1.2、ZIP解压函数 UnCompress1.1.3、代码如下1.1.4、如何使用类 1、C/C Zlib库调用Minzip来封装MyZip压缩类 Zlib是一个开源的数据压缩库&#xff0c;提供了一种通用的数据压…

Mybatis | Mybatis的核心配置

目录: Mybatis的核心配置 :一、MyBatis的 “核心对象”1.1 SqlSessionFactory1.2 SqlSession :SqlSession对象中的操作数据库的方法 :\<T> T selectOne ( String statement )\<T> T selectOne( String statement , Object parameter )\<E> List\<E> se…

android程序员面试笔试宝典,Android开发社招面试总结

部分面试常问的面试专题 一、Java篇 1.多线程并发&#xff1b; sleep 和 wait 区别join 的用法线程同步&#xff1a;synchronized 关键字等线程通信线程池手写死锁 2.Java 中的引用方式&#xff0c;及各自的使用场景 3.HashMap 的源码 4.GC(垃圾回收)是什么&#xff1f;如何…

日本极致产品力|如何利用招牌业务,打造占据63%市场份额的咖喱品牌

招牌产品是品牌的代表性产品&#xff0c;行业中领先品牌的招牌产品往往可以代表整个品类。在内线产品竞争中&#xff0c;通过在顾客心智中确立一个强势招牌产品形象可以提高品牌势能。好侍能够成为日本调味品头部品牌&#xff0c;通过香辛料招牌业务获取增长是其成功的关键。 从…

分享6个解决msvcp110.dll丢失的方法,全面解析msvcp110.dll文件

msvcp110.dll 是一个动态链接库 (DLL) 文件&#xff0c;属于 Microsoft Visual C 库的一部分&#xff0c;具体来说是 Microsoft Visual C 2012 版本的运行时组件。这个 DLL 文件包含了在 Windows 环境下运行用 C 编写的程序所必需的一些函数和资源。当一个应用程序是使用 Visua…

基于Python3的数据结构与算法 - 07 归并排序

一、归并 引入 假设现在的列表分两段有序&#xff0c;如何将其合并成为一个有序列表。 这种操作成为一次归并。 归并的思路 分别对两个列表进行遍历&#xff0c;比较两个列表中的最小值&#xff0c;将更小的取出来。取出后一次进行上操作&#xff0c;直到其中一个列表中的元…

1分钟学会Python字符串前后缀与编解码

1.前缀和后缀 前缀和后缀指的是&#xff1a;字符串是否以指定字符开头和结尾 2.startswith() 判断字符串是否以指定字符开头&#xff0c;若是返回True&#xff0c;若不是返回False str1 "HelloPython"print(str1.startswith("Hello")) # Trueprint…

【转载】深度学习笔记——详解损失函数

原文链接: https://blog.csdn.net/weixin_53765658/article/details/136360033 CSDN账号: Purepisces github账号: purepisces 希望大家可以Star Machine Learning Blog https://github.com/purepisces/Wenqing-Machine_Learning_Blog 损失函数 根据您使用的神经网络类型和数…