如何在Chrome插件中保留Notion SDK类型接口并规避CORS问题?
保留Notion SDK类型化接口的Chrome插件封装方案
由于CORS限制,Content Script无法直接调用Notion官方SDK,通过chrome.runtime.sendMessage通信会丢失原SDK的类型提示与简洁调用体验。以下是基于TypeScript的封装方案,可完全复用Notion SDK的类型系统,让调用方式和原代码保持一致。
1. 定义共享类型
创建跨Content Script和Background的共享类型文件,统一消息结构并复用Notion SDK的类型定义:
// types/notion-proxy.ts import { Client } from "@notionhq/client"; // 定义代理消息结构 export type NotionProxyMessage = { type: "NOTION_PROXY_CALL"; methodPath: string[]; // 例如 ["databases", "retrieve"] args: any[]; }; // 导出Notion Client类型,供Content Script端复用 export type NotionClient = Client;
2. Content Script端:创建Notion代理对象
实现一个代理类,模拟原Notion Client的结构,所有方法调用会自动转为消息发送到Background:
// content_script.tsx import type { NotionClient, NotionProxyMessage } from "./types/notion-proxy"; export function createNotionProxy(): NotionClient { const createProxySegment = (path: string[]): any => { return new Proxy({}, { get(target, prop) { if (typeof prop === "string") { const newPath = [...path, prop]; // 对方法调用进行拦截,转为消息发送 return (...args: any[]) => { return new Promise((resolve, reject) => { chrome.runtime.sendMessage<NotionProxyMessage, any>( { type: "NOTION_PROXY_CALL", methodPath: newPath, args, }, (response) => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(response); } } ); }); }; } return createProxySegment([...path, prop as string]); }, }); }; return createProxySegment([]) as NotionClient; } // 调用示例(和原SDK写法完全一致,保留自动补全) async function saveToNotion() { const { notionToken } = await chrome.storage.local.get("notionToken"); const notion = createNotionProxy(); // 自动补全和类型检查正常工作 const database = await notion.databases.retrieve({ database_id: "your-db-id" }); console.log(database); }
3. Background端:处理代理消息
监听Content Script发送的消息,解析方法路径并调用实际的Notion SDK方法:
// background.tsx import { Client } from "@notionhq/client"; import type { NotionProxyMessage } from "./types/notion-proxy"; // 缓存Notion Client,避免重复初始化 let notionClient: Client | null = null; async function getNotionClient(): Promise<Client> { if (!notionClient) { const { notionToken } = await chrome.storage.local.get("notionToken"); notionClient = new Client({ auth: notionToken }); } return notionClient; } // 监听代理消息并处理 chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.type === "NOTION_PROXY_CALL") { try { const client = await getNotionClient(); // 按路径找到对应的SDK方法 let targetMethod: any = client; for (const segment of request.methodPath) { targetMethod = targetMethod[segment]; } // 调用方法并返回结果 const result = await targetMethod(...request.args); sendResponse(result); } catch (error) { sendResponse({ error: (error as Error).message }); } // 返回true保持异步通信通道打开 return true; } });
关键优势
- 完全复用Notion SDK的类型定义,保留自动补全和类型检查能力
- 无需手动编写每个接口的消息通信逻辑,大幅减少重复代码
- 缓存Notion Client,避免重复初始化提升性能
- 统一的错误处理机制,便于排查问题
内容的提问来源于stack exchange,提问作者David Davó
相关产品推荐
相关产品推荐

