如果應用程式使用第 1 代函式,建議按照本指南的操作說明,遷移至第 2 代。第 2 代函式使用 Cloud Run,提供更優異的效能、設定、監控等功能。
本文件中的範例假設您使用 JavaScript 搭配 CommonJS 模組 (require 樣式匯入),但相同原則也適用於 JavaScript 搭配 ESM (import … from 樣式匯入) 和 TypeScript。
遷移程序
第 1 代和第 2 代函式可以並存於同一個來源檔案中。這樣一來,您就能在準備就緒時,逐步遷移程式碼集。不過請注意,這種套件混合方式不適用於單一、獨立的函式。
建議您一次遷移一個函式,並在測試和驗證後再繼續。
驗證 Firebase CLI 和 firebase-functions 版本
請確認您使用的是 Firebase CLI 12.00 以上版本和 firebase-functions 4.3.0 以上版本。任何較新版本都會支援第 2 代和第 1 代。
更新匯入作業
第 2 代函式會從 firebase-functions SDK 的 v2 子套件匯入。Firebase CLI 只需要這個不同的匯入路徑,就能判斷要將函式程式碼部署為第 1 代或第 2 代函式。
v2 子套件是模組化套件,建議您只匯入所需的特定模組。
變更前:第 1 代
const functions = require("firebase-functions/v1");
之後:第 2 代
// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
更新觸發條件定義
由於第 2 代 SDK 偏好模組化匯入,因此請更新觸發條件定義,反映上一步變更的匯入內容。
傳遞給某些觸發條件回呼的引數已變更。在這個範例中,請注意 onDocumentCreated 回呼的引數已合併為單一 event 物件。此外,部分觸發條件提供便利的新設定功能,例如 onRequest 觸發條件的 cors 選項。
變更前:第 1 代
const functions = require("firebase-functions/v1");
exports.date = functions.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions.firestore
.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
之後:第 2 代
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
使用 JavaScript 解構功能,盡可能減少重寫作業
如果函式主體複雜,且高度依賴第 1 代結構定義或供應商專屬參數 (例如 message 或 snapshot),可以使用第 2 代 SDK 內建的第 1 代相容性輔助函式。
第 2 代 SDK 會自動使用與第 1 代簽章相符的 getter 修補事件物件。這樣一來,您就能使用 JavaScript 解構,直接在處理常式簽章中擷取這些屬性,盡可能減少重寫函式邏輯的需求。
提供者對應參考資料
| 供應商 | 第 1 代引數 | 第 2 代修補事件解構 |
| Pub/Sub | (message, context)
|
({ message, context }) => { ... }
|
| Cloud Firestore | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Cloud Storage | (object, context)
|
({ object, context }) => { ... }
|
| Realtime Database | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Remote Config | (version, context)
|
({ version, context }) => { ... }
|
| Cloud Scheduler | (context)
|
({ context }) => { ... }
|
| 工作佇列 | (data, context)
|
({ data, context }) => { ... }
|
變更前 (第 1 代):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
新替代方案 (第 2 代,使用解構):
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
// No need to change the function body!
const data = message.json; // Uses v1 Message wrapper
const eventId = context.eventId; // Uses v1 EventContext map
// ... rest of the logic
});
使用參數化設定
第 2 代函式不再支援 functions.config,改用更安全的介面,在程式碼集中以宣告方式定義設定參數。有了新的 params 模組,除非所有參數都有有效值,否則 CLI 會封鎖部署作業,確保函式不會在缺少設定的情況下部署。
變更前:第 1 代
const functions = require("firebase-functions/v1");
exports.getQuote = functions.https.onRequest(async (req, res) => {
const quote = await fetchMotivationalQuote(functions.config().apiKey);
// ...
});
之後:第 2 代
const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");
// Define the secret parameter
const apiKey = defineSecret("API_KEY");
exports.getQuote = onRequest(
// make the secret available to this function
{ secrets: [apiKey] },
async (req, res) => {
// retrieve the value of the secret
const quote = await fetchMotivationalQuote(apiKey.value());
// ...
}
);
如果您現有的環境設定使用 functions.config,請在升級至第 2 代時一併遷移這項設定。
functions.config API 已淘汰,將於 2027 年 3 月停用。
上述日期過後,使用 functions.config 的部署作業將會失敗。
為避免部署失敗,請使用 Firebase CLI 將設定遷移至 Cloud Secret Manager。強烈建議您採用這種做法,以最有效率且安全的方式遷移設定。
使用 Firebase CLI 匯出設定
使用
config export指令,將現有環境設定匯出至 Cloud Secret Manager 中的新密鑰:$ firebase functions:config:export i This command retrieves your Runtime Config values (accessed via functions.config()) and exports them as a Secret Manager secret. i Fetching your existing functions.config() from your project... ✔ Fetched your existing functions.config(). i Configuration to be exported: ⚠ This may contain sensitive data. Do not share this output. { ... } ✔ What would you like to name the new secret for your configuration? RUNTIME_CONFIG ✔ Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```更新函式程式碼以繫結 Secret
如要使用儲存在 Cloud Secret Manager 新密鑰中的設定,請在函式來源中使用
defineJsonSecretAPI。此外,請務必將 Secret 繫結至所有需要 Secret 的函式。之前
const functions = require("firebase-functions/v1"); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().someapi.key; // ... });之後
const { onRequest } = require("firebase-functions/v2/https"); const { defineJsonSecret } = require("firebase-functions/params"); const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = onRequest( // Bind secret to your function { secrets: [config] }, (req, res) => { // Access secret values via .value() const apiKey = config.value().someapi.key; // ... });部署函式
部署更新後的函式,套用變更並繫結 Secret 權限。
firebase deploy --only functions:<your-function-name>
設定執行階段選項
執行階段選項的設定在第 1 代和第 2 代之間有所變更。第 2 代也新增了為所有函式設定選項的功能。
變更前:第 1 代
const functions = require("firebase-functions/v1");
exports.date = functions
.runWith({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
})
// locate function closest to users
.region("asia-northeast1")
.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions
// locate function closest to users and database
.region("asia-northeast1")
.firestore.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
之後:第 2 代
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");
// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });
exports.date = onRequest({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
更新預設服務帳戶 (選用)
第 1 代函式會使用 Google App Engine 預設服務帳戶,授權存取 Firebase API,而第 2 代函式則使用 Compute Engine 預設服務帳戶。如果您已將特殊權限授予第 1 代服務帳戶,這項差異可能會導致遷移至第 2 代的函式發生權限問題。如果您未變更任何服務帳戶權限,可以略過這個步驟。
建議的解決方案是明確將現有的第 1 代 App Engine 預設服務帳戶指派給要遷移至第 2 代的函式,覆寫第 2 代預設值。方法是確保每個遷移的函式都為 serviceAccountEmail 設定正確的值:
const {onRequest} = require("firebase-functions/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions");
// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});
// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
// ...
});
或者,您也可以修改服務帳戶詳細資料,確保 App Engine 預設服務帳戶 (第 1 代) 和 Compute Engine 預設服務帳戶 (第 2 代) 都具備所有必要權限。
使用並行
第 2 代函式的一大優勢,在於單一函式執行個體能同時處理多項要求。這可大幅減少使用者遇到的冷啟動次數。並行預設值為 80,但您可以設為 1 到 1000 之間的任何值:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// set concurrency value
concurrency: 500
},
(req, res) => {
// ...
});
調整並行設定可提升效能並降低函式成本。如要進一步瞭解並行,請參閱「允許並行要求」。
稽核全域變數使用情形
編寫第 1 代函式時,如果未考量並行,可能會使用在每個要求中設定及讀取的全域變數。啟用並行功能後,單一執行個體會開始同時處理多個要求,這可能會導致函式出現錯誤,因為並行要求會同時設定及讀取全域變數。
升級時,您可以將函式的 CPU 設為 gcf_gen1,並將 concurrency 設為 1,還原第 1 代行為:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// TEMPORARY FIX: remove concurrency
cpu: "gcf_gen1",
concurrency: 1
},
(req, res) => {
// ...
});
不過,我們不建議將此做法視為長期修正,因為這會失去第 2 代函式的效能優勢。請改為稽核函式中全域變數的使用情形,並在準備就緒時移除這些暫時設定。
將流量遷移至新的第 2 代函式
就像變更函式的區域或觸發條件類型時一樣,您必須為第 2 代函式指定新名稱,並慢慢將流量遷移至該函式。
您無法將函式從第 1 代升級至第 2 代,並使用相同名稱和執行 firebase deploy。否則會發生錯誤:
Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.
遷移策略取決於函式使用的觸發條件類型。
遷移可呼叫、工作佇列和 HTTP 觸發條件
這些觸發條件是直接叫用。由於第 2 代函式會有新名稱 (以及 HTTP 觸發條件的新網址),因此您可以更新用戶端來遷移流量。
- 重新命名程式碼中的函式 (例如將
myCallable重新命名為myCallableV2)。 - 部署函式。第 1 代和第 2 代函式現在都正在運作。
- 更新用戶端程式碼或呼叫端,指向新的第 2 代函式名稱或網址。
- 所有流量都轉移至新函式後,請使用 Firebase CLI 的
firebase functions:delete指令刪除第 1 代函式。
遷移背景觸發條件
背景觸發條件 (例如 Pub/Sub、Cloud Firestore 和 Cloud Storage 觸發條件) 會回應專案中的事件。為避免在轉換期間遺漏任何事件,您必須暫時同時執行第 1 代和第 2 代函式。
轉換期間,兩個函式都會在同一個事件觸發。這表示每個事件都會執行兩次商業邏輯。請先確認函式冪等,再繼續操作。
在程式碼中保留現有的第 1 代函式,並新增第 2 代函式,監聽相同的事件來源。
import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing 1st gen function --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("V1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 passthrough function --- export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => { console.log("v2 handler triggering V1 for event:", context.eventId); // Call the v1 function's handler await myPubSub.run(message, context); });執行
firebase deploy。 這兩個函式現在都已啟用,並監聽相同的事件。確認第 2 代函式是否接收流量。監控這兩項函式的記錄。確認所有事件都會叫用第 2 代函式,且呼叫成功。
確認函式運作正常後,請將實際的商業邏輯從第 1 代函式移至第 2 代函式的主體。如果您使用直通方法,請移除對
myPubSub.run()的呼叫。import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing v1 function (to be removed next) --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("v1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 function with full logic --- export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => { console.log("v2 handler running for event:", context.eventId); // ... existing v1 function logic WAS MOVED HERE ... });部署這項變更。
從程式碼中移除第 1 代函式定義,然後重新部署。CLI 會提示您從 Google Cloud 刪除第 1 代函式。