如何实现Nuxt.js 3多环境部署运行同一编译产物
在公司的项目里,我们使用Nuxt.js 3架构搭建了前端项目,并且部署到多个不同的环境中,像开发环境(dev)、测试环境(test)、预发布环境(staging)、集成测试环境(sit)、用户验收测试环境(uat)以及生产环境(prod)。
以往在这些环境部署时,我们会借助dotenv工具,为每个环境配置对应的.env.*
文件。每次部署都要针对不同环境执行一次编译打包流水线,虽说部署流程已经实现了自动化,但编译打包的过程耗时很长。
于是,我们就有了一个新需求:只进行一次编译打包,让所有环境都使用这同一批编译产物。这样一来,不仅能大幅节省部署时间,还能减少因不同编译环境带来的差异问题。
要实现这个目标,难点在于.env.*
文件里的配置在编译阶段就会被固定到产物代码中,运行时无法更改。所以,关键就是把配置从编译阶段传入改为运行阶段传入。同时,我们也不能再采用编译成静态文件运行的方式,而是需要借助node或者pm2这类管理软件来运行Nuxt.js项目。
下面我分享的方法,不一定是每个项目的最佳解决方案,仅供大家参考。在这个方案里,我们会利用Nuxt.js的runtime-config
功能来达成上述需求。项目代码托管在nuxtjs – demo 。
一、理解环境变量在项目中的作用
在Node.js中,获取环境变量使用process.env.*
这种方式,但前提是操作系统里已经配置好了相应的环境变量。比如在类Unix操作系统里,执行export API_PREFIX=/api
这条命令,就能设置API_PREFIX
这个环境变量的值为/api
,之后在Node.js代码里,就可以通过process.env.API_PREFIX
获取到这个值。Nuxt.js正是利用了Node.js的这个特性来实现环境变量的注入。在代码里,获取环境变量就像这样:
const apiPrefix = process.env.API_PREFIX
不过,这里有个问题。在编译阶段,代码里所有的process.env.*
都会被编译成对应的字符串。这里我们不讨论webpack中的NODE_ENV
,只聚焦Nuxt.js里的环境变量。
二、Nuxt.js配置文件的设置
Nuxt.js架构提供了nuxt.config.ts
这个配置文件供开发者进行配置。其中runtimeConfig
的配置要遵循Nuxt的规范:runtimeConfg.public
里的键采用小驼峰命名法,值先留空。这样,Nuxt会自动注入带有NUXT_PUBLIC_
前缀的环境变量,而且这些变量需要配置在.env.*
文件里。
要是不按照Nuxt的规范来配置,那就只能通过process.env.*
的方式获取配置,可这样一来,配置在编译阶段就会被编译到产物代码中,又回到了最初的问题。下面看看规范配置和不规范配置环境变量的示例。
# .env文件配置示例 NUXT_PUBLIC_API_PREFIX=/api NUXT_PUBLIC_BASE_SERVICE_URL=http://192.168.1.20:8080/ NUXT_PUBLIC_USER_SERVICE_URL=http://192.168.1.21:8080/ # 不按规范,没有NUXT_PUBLIC前缀 OFFICIAL_WEBSITE_URL=https://www.example.com
私密配置规范示例:
# 按照规范配置NUXT前缀 NUXT_PUBKEY=公钥
nuxt.config.js
文件里的配置如下:
{ // 添加runtimeConfig配置 runtimeConfig: { public: { // @see https://nuxt.com/docs/guide/going-further/runtime-config#example apiPrefix: '', // 可以被NUXT_PUBLIC_API_PREFIX环境变量覆盖 baseServiceUrl: '', // 可以被NUXT_PUBLIC_BASE_SERVICE_URL环境变量覆盖 userServiceUrl: '', // 可以被NUXT_PUBLIC_USER_SERVICE_URL环境变量覆盖 official_website_url: process.env.OFFICIAL_WEBSITE_URL, // 仅能够通过process.env来获取,并在编译阶段编译进代码 }, }, }
三、ecosystem.config.cjs配置文件
ecosystem.config.cjs
是docker运行pm2时读取的配置文件。apps.env
会把docker中的环境变量注入进来,这里env里的键要和nuxt.config.ts
里的runtimeConfig
对应使用。如果是runtimeConfig.public
里的变量,注入时必须带上NUXT_PUBLIC_
前缀。配置示例如下:
module.exports = { apps: [ { name: 'website-nuxt', exec_mode: 'cluster', instances: '1', script: '.output/server/index.mjs', args: 'start', env: { NUXT_PUBLIC_API_PREFIX: process.env.API_PREFIX, NUXT_PUBLIC_COLLEGE_URL: process.env.COLLEGE_URL, NUXT_PUBLIC_LOGIN_REGISTER_URL: process.env.LOGIN_REGISTER_URL, }, }, ], };
四、客户端与组件中的配置使用
客户端会把配置渲染到html里,示例如下:
<script> window.__NUXT__.config = { public: { apiPrefix: '/api', collegeUrl: 'http://192.168.0.212:85/', loginRegisterUrl: 'http://192.168.0.212:82/', } } </script>
在组件里编写代码时,正常使用这些配置就行。示例代码如下:
<template> <div> {{ message }} </div> </template> <script setup lang="ts"> import { onMounted } from 'vue' const message = ref('') const runtimeConfig = useRuntimeConfig() onMounted(() => { console.log(runtimeConfig.public) message.value = runtimeConfig.public // test() }) </script>
五、本地开发环境配置
在本地开发时,还是可以正常使用dotenv。建议从.env.example
复制一份.env.local
文件,在.env.local
里配置参数。它会和.env
合并,而且.env.local
的优先级更高。不过要注意,.env.local
不要提交到代码仓库,提交到仓库的.env.*
文件里不应该包含敏感信息。示例配置如下:
NUXT_PUBLIC_API_PREFIX=/api NUXT_PUBLIC_COLLEGE_URL=http://192.168.0.212:85/ NUXT_PUBLIC_LOGIN_REGISTER_URL=http://192.168.0.212:82/
六、多环境部署
(一)docker部署多环境
运行时的环境变量参考ecosystem.config.cjs
里的env配置,而ecosystem.config.cjs
里的env又参考nuxt.confg.ts
里runtimeConfig
中的public变量。
(二)编译docker镜像
编译docker镜像的命令如下:
docker build --pull --rm -f "Dockerfile" -t nuxtjs-demo:latest "."
(三)运行docker容器
运行示例:
# 环境1 docker run -e "API_PREFIX=/api" -e "COLLEGE_URL=http://192.168.0.212:85/" -e "LOGIN_REGISTER_URL=http://192.168.0.212:82/" -it -p 127.0.0.1:3002:3000 -d --rm --name nuxtjs-demo nuxtjs-demo:latest # 环境2 docker run -e "API_PREFIX=/api" -e "COLLEGE_URL=地址1" -e "LOGIN_REGISTER_URL=地址 2" -it -p 127.0.0.1:3002:3000 -d --rm --name nuxtjs-demo1 nuxtjs-demo:latest
这里需要注意,如果使用的是arm系统,比如苹果芯片的mac,可能会遇到平台兼容的问题。运行时可能会出现警告:
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
可以通过以下方式指定平台:
- 设置Docker默认平台:为了在运行或构建镜像时默认使用AMD64平台,可以设置环境变量
DOCKER_DEFAULT_PLATFORM
。在终端执行命令:
export DOCKER_DEFAULT_PLATFORM=linux/amd64
也可以把这条命令添加到~/.bashrc
或~/.zshrc
文件里,这样每次启动终端时就会自动设置。
2. 运行或构建指定平台的Docker镜像:设置好默认平台后,就可以运行或构建指定平台的Docker镜像了。比如运行一个AMD64的Ubuntu镜像:
docker run --rm -it --platform=linux/amd64 ubuntu:latest
构建镜像时指定平台为AMD64的命令如下:
docker build --platform=linux/amd64 -t myimage:latest.
通过上述一系列操作,就能实现用同一编译产物在多个环境中运行Nuxt.js 3项目,希望对大家有所帮助。