昨天的延伸設計有提到可以建立一個 訊息路由器 (message router),集中管理不同 action。
這個做法有點類似前端框架中 store 的感覺,以下的方法如果專案成長得更大時,可以試試看~
// messageHandler.js
/**
* 註冊訊息處理器
* @param {Object} handlers - key 為 action,value 為處理函式
*/
export function registerMessageHandlers(handlers) {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const { action, data } = message;
if (handlers[action]) {
// 支援同步或非同步處理
const result = handlers[action](data, sender);
if (result instanceof Promise) {
result.then(sendResponse).catch((err) => {
console.error("Message Handler Error:", err);
sendResponse({ error: err.message });
});
return true; // 啟用 async sendResponse
} else {
sendResponse(result);
}
}
});
}
/**
* 傳送訊息給 background
*/
export function sendToBackground(action, data) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({ action, data }, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(response);
}
});
});
}
/**
* 傳送訊息給指定 tab (通常用於 content_script)
*/
export function sendToTab(tabId, action, data) {
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage(tabId, { action, data }, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(response);
}
});
});
}
// background.js
import { registerMessageHandlers, sendToTab } from "./messageHandler.js";
registerMessageHandlers({
FETCH_KEY: async (_, sender) => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
return sendToTab(tab.id, "GET_PROJECT_KEY");
},
});
// content_script.js
import { registerMessageHandlers } from "./messageHandler.js";
registerMessageHandlers({
GET_PROJECT_KEY: () => {
const url = window.location.href;
const match = url.match(/\/([^/]+)\/-\/merge_requests/);
return { projectKey: match ? match[1] : "未知專案" };
},
});
// popup.html
import { sendToBackground } from "./messageHandler.js";
document.getElementById("getKeyBtn").addEventListener("click", async () => {
try {
const resp = await sendToBackground("FETCH_KEY");
document.getElementById("output").innerText = resp.projectKey;
} catch (err) {
document.getElementById("output").innerText = "錯誤:" + err.message;
}
});