一次构建多处部署 - Next.js Runtime Env
我们一般通过控制 env 的方式去做到 "Build once, deploy many" 哲学。但是在 Next.js 中,环境变量分为两种,一个种是可被用于 Client 侧的 NEXT_PUBLIC_
开头的环境变量,另一个种是只能被用于 Server 侧的环境变量。前者会在 Next.js 构建时被注入到客户端代码中,导致原有代码被替换,那么也就意味着我们控制 env 并不能做到一次构建多处部署。一旦需要部署到不同的环境并且修改 env,我们就需要重新构建一次。
今天的文章,我们将会探讨如何通过 Next.js 的 Runtime Env 来实现一次构建多处部署。
Next.js Runtime Env
今天的主角是 next-runtime-env
这个库,它可以让我们在 Next.js 中使用 Runtime Env。我们可以通过它来实现一次构建多处部署。
更换 Client 侧的环境变量使用方式:
然后在 app/layout.tsx
上增加环境变量注入 Script。
那么这样就可以了。
现在我们来试一试。我们有这样页面,直接渲染上述 API_URL
的响应数据。
现在我们使用 next build
构建项目,然后在构建之后,修改 .env
中的 NEXT_PUBLIC_API_URL
,然后使用 next start
启动项目,观察实际请求的接口是否随着 .env
的修改而变化。
现在我们的 NEXT_PUBLIC_API_URL=https://jsonplaceholder.typicode.com/todos/2
,启动项目之后,浏览器请求的是 https://jsonplaceholder.typicode.com/todos/2
。
当我们修改 .env
中的 NEXT_PUBLIC_API_URL
为 https://jsonplaceholder.typicode.com/todos/3
,然后重启项目,浏览器请求的是 https://jsonplaceholder.typicode.com/todos/3
。
这样我们就实现了一次构建多处部署,只需要修改 env 即可。
深入了解 Runtime Env
其实 next-runtime-env
的实现原理非常简单,<PublicEnvScript />
实际就是在 <head>
中注入了一个 <script />
类似这样。
由于 <head />
中的 script 会在页面水合前被执行,所以我们可以在 Client 侧通过 window['__ENV']
来获取环境变量,而 next-runtime-env
提供 env()
正是这样实现的。而这个环境变量在 Server Side 都是动态的,所以在 Server Side 的取值永远都是通过 process.env[']
。
下面的简略的代码展示了 env()
的实现。
构建一个无环境变量依赖的产物
一个项目中,一般都会存在大量的环境变量,有部分环境变量只会在 Client Side 使用,在项目 build 过程中,必须要正确的注入环境变量,否则会导致项目无法通过构建。
例如常见的 API_URL
变量,是请求接口的地址,在构建中,如果没有值,就会导致预渲染中的接口请求错误导致构建失败。比如在 Route Handler 中,我们有这样一个函数。
当 API_URL
为空时,fetchJson
会报错,导致构建失败。
这是因为在 Next.js 中,默认对 Route handler 进行了预渲染,而在预渲染过程中,fetchJson
会被执行,而 API_URL
为空,导致请求失败。
只需要使用 noStore()
或者改变 dynamic 的方式,就可以解决这个问题。
那么,在其他的页面构建中,如果也遇到类似的问题,也修改这个地方就可以了。
构建的时候,我们没有注入任何的环境变量,在启动构建后的服务之前,记得一定要在当前目录下创建一个 .env
文件,并且正确填写变量值,这样才能保证项目正常运行。
通过 Dockerfile 构建无环境变量依赖的镜像
在上节的基础上,对整个构建过程进一步封装,使用 Docker 完成整个构建然后发布到 Docker Hub,真正意义上实现一次构建多处部署。
创建一个 Dockerfile
文件。
上面的 dockerfile 在官网版本的基础上做了修改,已在 Shiro 中落地使用。
由于 Next.js standalone build 中并不包含 sharp 依赖,所以在 Docker 构建中我们首先全局安装了 sharp,并且在后续注入了 sharp 的安装位置的环境变量。
这样构建的 Docker 镜像也不依赖于环境变量,并且 standalone build 让 Docker image 的占用空间更小。
通过 Docker 容器的路径映射,我们只需要把当前目录下的 .env
映射到容器内部的 /app/.env
即可。
这里编写一个简单的 Docker compose 实例。
大功告成,后续任何人只需要通过 Docker pull 取得构建后的镜像然后再修改本地 .env
就能够运行属于自己环境的项目了。
最后更新于 2024/7/26 16:15:59
本书还在编写中..
前往 https://innei.in/posts/tech/my-first-nextjs-book-here#comment 发表你的观点吧。