如何在Next.js的public文件夹内文件中访问环境变量?
解决Next.js中public文件夹内Service Worker无法访问环境变量的问题
问题原因
public目录下的文件属于静态资源,Next.js不会对它们做编译处理,也不会替换其中的process.env变量——因为process是Node.js环境的对象,浏览器/service worker环境里根本不存在,所以直接写会报错。
可行解决方案
1. 构建时自动注入环境变量(推荐)
写一个简单的Node脚本,在项目构建前把环境变量替换到service worker模板里,生成最终的firebase-messaging-sw.js到public目录:
- 第一步:在项目根目录创建模板文件
firebase-messaging-sw.template.js,内容如下:
const firebaseConfig = { apiKey: "{{NEXT_PUBLIC_FIREBASE_API_KEY}}", authDomain: "{{NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}}", projectId: "{{NEXT_PUBLIC_FIREBASE_PROJECT_ID}}", storageBucket: "{{NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET}}", messagingSenderId: "{{NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID}}", appId: "{{NEXT_PUBLIC_FIREBASE_APP_ID}}", measurementId: "{{NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID}}", }; // 后续Firebase初始化代码 importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging.js'); firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); // ...其他messaging逻辑
- 第二步:创建注入脚本
scripts/generate-sw.js:
const fs = require('fs'); const path = require('path'); // 读取模板文件 const templatePath = path.join(__dirname, '../firebase-messaging-sw.template.js'); const templateContent = fs.readFileSync(templatePath, 'utf8'); // 替换环境变量 const replacedContent = templateContent .replace("{{NEXT_PUBLIC_FIREBASE_API_KEY}}", process.env.NEXT_PUBLIC_FIREBASE_API_KEY) .replace("{{NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}}", process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN) .replace("{{NEXT_PUBLIC_FIREBASE_PROJECT_ID}}", process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID) .replace("{{NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET}}", process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET) .replace("{{NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID}}", process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID) .replace("{{NEXT_PUBLIC_FIREBASE_APP_ID}}", process.env.NEXT_PUBLIC_FIREBASE_APP_ID) .replace("{{NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID}}", process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID); // 生成最终sw文件到public目录 const outputPath = path.join(__dirname, '../public/firebase-messaging-sw.js'); fs.writeFileSync(outputPath, replacedContent);
- 第三步:修改
package.json的scripts,在构建/开发前执行脚本:
{ "scripts": { "prebuild": "node scripts/generate-sw.js", "build": "next build", "dev": "node scripts/generate-sw.js && next dev" } }
这样每次开发或构建时,都会自动把环境变量注入到service worker文件里,public目录下的最终文件是已经替换好变量的版本。
2. 从主应用传递配置到Service Worker
在注册service worker的时候,把firebase配置通过postMessage发送过去:
- 主应用代码(比如
pages/_app.js或组件内):
import { useEffect } from 'react'; useEffect(() => { const registerSW = async () => { if ('serviceWorker' in navigator) { const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, // ...其他配置项 }; const swRegistration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); // 向service worker发送配置 swRegistration.active.postMessage({ type: 'SET_FIREBASE_CONFIG', config: firebaseConfig }); } }; registerSW(); }, []);
- 修改
firebase-messaging-sw.js,监听消息接收配置:
let firebaseConfig = null; self.addEventListener('message', (event) => { if (event.data.type === 'SET_FIREBASE_CONFIG') { firebaseConfig = event.data.config; // 初始化Firebase importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging.js'); firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); // ...后续逻辑 } });
这种方法不需要修改构建流程,但要注意service worker的生命周期,确保配置传递完成后再初始化Firebase。
内容的提问来源于stack exchange,提问作者Tsabary
相关产品推荐
相关产品推荐

