Nuxt3运行时client端环境变量配置

主流的CSR前端SPA项目通常会通过 process.env(构建时) 或 import.meta.env 来访问环境变量,而环境变量通常会使用 .env文件、cicd流程时注入、Dockerfile、cross-env等方式在执行devbuild命令时设定并构建到前端代码中,也就是build后的代码已经不能随部署运行时修改环境变量了,这是一个正常的现象。

我们通常会在执行针对不同部署环境的构建指令时会读取不同的.env文件,或使用不同的ci/cd流程等切换构建时的环境变量。

而部署一个node服务的时候,我们可以在部署执行启动命令时才进行指定环境变量,并通过process.env访问。

Nuxt3通用渲染时环境变量配置

因Nuxt3项目在生产环境部署时需要build的,构建后需要执行 node .output/server/index.mjs 入口文件启动服务 (部署 · 快速入门 Nuxt),则会有构建时运行时 的区别,其中执行build指令时为构建时,执行node .output/server/index.mjs时为运行时。

模拟需求 :当部署项目执行node .output/server/index.mjs时,使用一个环境变量 HOST_ENV 来指定当前部署到哪个环境,默认为dev,部署到预发布环境时值为stag,生产环境为prod

client端直接访问环境变量

如client端代码访问process.env.HOST_ENV

vue 复制代码
<!-- app.vue -->
<template>
  <div></div>
</template>

<script setup lang="ts">
consolt.log('HOST_ENV ---- ', process.env.HOST_ENV) // 打印
</script>

使用cross-env或在目录中加入.env文件,并指定 HOST_ENV=dev,在命令行中执行dev指令启动nuxt服务,并在浏览器进行访问。默认的通用渲染模式下,代码会现在server端执行一次(SSR),在浏览器端水合也会执行一次,所以会在命令行窗口(serve端)和浏览器控制台(client端)各打印一次

可以发现server端执行时值是dev,而到client端时变为undefined。原因很明显,process是node环境下的,在nuxt3中client端执行时访问的process是经过如下处理的,并且process.env中有且仅有NODE_ENV这一个key

json 复制代码
{
    "dev": true,
    "test": false,
    "env": {
        "NODE_ENV": "development"
    },
    "server": false,
    "client": true,
    "browser": true,
    "nitro": false,
    "prerender": false
}

那么如果使用 import.meta.env 来访问呢?在这种情况下实际上是可以访问的。当然,默认情况下只会读取VITE_开头的环境变量,所以需要把环境变量改名为VITE_HOST_ENV进行访问。

但是,这个访问方式和开篇赘述时的情况一致,import.meta.env.xxx 的访问是会在构建时被静态替换的,clinet端访问环境变量的值会停留在执行构建时的值。

runtimeConfig对环境变量和nirto的相关配置

对于上述的情况,已经看到多个平台不少相关的文章均有提到过类似的环境变量访问的问题,但对于nuxt3的runtimeConfig的配置描述得不到位,或使用场景不清晰的问题。

也许是和官方文档的描述不清晰有关系,其环境变量相关的文档描述分了3个部分

其中有一段描述:

特定命名的环境变量可以覆盖运行时配置属性。也就是说,以 NUXT_ 开头的大写环境变量,使用 _ 分隔键和大小写变化。

引出一个普遍的疑惑:既然我需要写 NUXT_ 开头的环境变量去覆盖runtimeConfig中的值,那么我为何不直接为runtimeConfig赋值为访问环境变量? 或者我在代码中直接访问环境变量,为何还须通过useRuntimeConfig()去访问?

对于server端的代码来说是的,环境变量可以直接通过process.env访问。

runtimeConfig可以被NUXT_开头的环境变量覆盖的意义就在于,可在启动服务运行时指定环境变量去进行覆盖,而不仅仅是构建时 。所以对于client端的代码来说,要获取运行时的环境变量配置,只能使用这种途径并通过useRuntimeConfig()进行访问。

注意: 下面这种方式,也只能读取到构建时的值

js 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public:{
      HOST_ENV: process.env.HOST_ENV // 读取不到运行时的环境变量
    }
  }
})

英文版文档中有这一段描述了上面的情况: Runtime Config · Nuxt Advanced

Setting the default of runtimeConfig values to differently named environment variables (for example setting myVar to process.env.OTHER_VARIABLE) will only work during build-time and will break on runtime. It is advised to use environment variables that match the structure of your runtimeConfig object.

所以我们需要依赖NUXT_开头的环境变量进行运行时覆盖,我们需要把变量名HOST_ENV更名为NUXT_PUBLIC_HOST_ENV,根据转换规则,会覆盖runtimeConfig.public.hostEnv,并且必须要预先声明这个key,否则不会被运行时环境变量注入

js 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public:{
      hostEnv: 'dev' // 必须在代码中先声明这个key,否则不会被覆盖,可以设定默认值
    }
  }
})

client端访问

vue 复制代码
<!-- app.vue -->
<template>
  <div></div>
</template>

<script setup lang="ts">
// consolt.log('HOST_ENV ---- ', process.env.HOST_ENV)
// 更换为对runtimeConfig的访问
console.log('HOST_ENV ----', useRuntimeConfig().public.hostEnv) // 打印
</script>

执行build进行构建后,这里为方便测试,在scripts中新增启动服务指令start,其中直接使用cross-env指定运行时环境变量NUXT_PUBLIC_HOST_ENV=prod

json 复制代码
// package.json
"scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "start": "cross-env NUXT_PUBLIC_HOST_ENV=prod node .output/server/index.mjs"
  }

启动服务后查看打印结果,server端执行时和client端执行时该值均被成功设置为prod

优化实践

若希望改变注入的环境变量前缀,不使用NUXT_开头,可以进行如下配置(nuxt、nitro文档均没有提及这一点)

js 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    nitro: {
      envPrefix: 'Prefix_' // 更改为自定义的前缀,替换NUXT_这个匹配规则
    },
  }
})

如果使用上述的环境变量名转换的话,可能在使用中会有3个问题

  1. 在别的项目迁移到NUXT3时,或之前的版本中已经编写好多个环境变量,现在要进行运行时覆盖的改造等情况时,需要强制更改环境变量名或新增一个业务意义相同的NUXT_变量名。
  2. 当在runtimeConfig中对象层级深的情况下,要设置的环境变量名可能会非常长,增加了拼写错误的风险并增加了复杂性
  3. 限制了runtimeConfig中key的命名

为解决这个问题,nitro在2.9.0版本后新增了一个特性(需要把nuxt项目升级到对应的支持版本),可以让任意的环境变量名覆盖到runtimeConfig中的任意key和值中。PR描述

  1. 首先在配置中声明开启这个功能,在nitro配置项中,或在runtimeConfig中的nirto配置项中均可开启实验性的envExpansion功能
  2. runtimeConfig中使用{{ENV_NAME}}的插值语法声明要读取的环境变量名
js 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    experimental: {
      envExpansion: true, // 可以在这里开启
    },
  },
  runtimeConfig: {
    nitro: {
      envExpansion: true // 也可以在这里开启
    },
    public: {
      // 声明这个位置要的字符串要使用环境变量HOST_ENV的值
      HOST_ENV: '{{HOST_ENV}}',
      
      // 插值语法的灵活性,可以拼接字符串和其他环境变量
      COMBIN_ENV: '{{HOST_ENV}} ---- {{APP_NAME}}'
    },
  },
})

同样使用cross-env指定环境变量并运行start启动服务

json 复制代码
// package.json
{
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "start": "cross-env HOST_ENV=prod APP_NAME=juejin node .output/server/index.mjs"
  }
}

查看打印结果无误

使用这种方法时,就不能直接在runtimeConfig里面设定默认值了,在本地开发时,可以使用开发环境的.env文件进行指定环境变量默认值

相关推荐
黄尚圈圈28 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇6 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器