ctx.extensionResource 将当前扩展发行目录里的文件映射成当前宿主可以访问的 URL。扩展不需要知道 Studio 使用的随机端口、安装目录或 Player 的加载位置。
使用此接口的扩展应在 extension.json 中声明 "sdkVersion": ">=1.9.9"
ctx.asset 访问当前游戏项目的素材;ctx.extensionResource 访问当前扩展包自己的文件。两者的路径空间不同。

发行目录

需要在运行时读取的文件应放在以下目录:
my-extension/
├── extension.json
├── assets/
│   ├── cover.png
│   ├── theme.css
│   └── data.json
└── dist/
    ├── index.js
    ├── calculator.js
    ├── parser-worker.js
    └── runtime.wasm
  • assets/:图片、CSS、JSON、字体等静态文件;
  • dist/:构建生成的 JavaScript、Worker、WASM 和关联分块。
Studio 保存项目发行物和构建 Player 时会带上 extension.jsonassets/ui/dist/。如果 entry 指向其他顶层构建目录,例如 build/runtime/index.js,该顶层目录也会作为构建产物带上。除此之外,不要把运行时文件放在普通的自定义顶层目录:它可能在本机开发预览中可用,但不会进入项目发行物。

API

方法返回值说明
url(path)string生成扩展资源的绝对 URL,适合图片、CSS、fetch() 和 WASM
importModule<T>(path)Promise<T>动态导入扩展内的 ESM 模块
createWorker(path, options?)Worker创建扩展内的 Worker;默认使用 module Worker

路径规则

路径始终相对扩展发行根目录:
// 正确
ctx.extensionResource.url("assets/data.json");
ctx.extensionResource.url("dist/parser-worker.js");

// 错误:不要添加 ./、前导 /、协议或 ..
ctx.extensionResource.url("./assets/data.json");
ctx.extensionResource.url("/assets/data.json");
ctx.extensionResource.url("../data.json");
ctx.extensionResource.url("file:///tmp/data.json");
路径中的中文和空格会自动编码。Studio 还会自动附加当前扩展版本,用于开发时刷新缓存;不要拼接或缓存随机端口和版本参数。
url() 只校验路径写法并生成地址,不会发起网络请求,因此不能证明文件存在。需要检查文件时,请使用 fetch() 并判断 response.ok

图片和 CSS

图片 URL 可以直接交给浏览器元素:
function Cover() {
  const ctx = useExtensionContext();
  const coverUrl = ctx.extensionResource.url("assets/cover.png");

  return <img src={coverUrl} alt="扩展封面" />;
}
动态挂载样式表时,在组件卸载时移除 <link>
function ThemedPanel() {
  const ctx = useExtensionContext();

  useEffect(() => {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = ctx.extensionResource.url("assets/theme.css");
    document.head.append(link);

    return () => link.remove();
  }, [ctx]);

  return <section className="extension-panel">内容</section>;
}

JSON 和 WASM

使用 fetch() 读取 JSON,并显式处理 404 等响应:
const url = ctx.extensionResource.url("assets/data.json");
const response = await fetch(url);

if (!response.ok) {
  throw new Error(`读取扩展资源失败:${response.status} ${url}`);
}

const data = await response.json();
WASM 响应会使用 application/wasm MIME,可以直接流式实例化:
const wasmUrl = ctx.extensionResource.url("dist/runtime.wasm");
const { instance } = await WebAssembly.instantiateStreaming(
  fetch(wasmUrl),
  {},
);

动态导入 JavaScript

importModule() 用于加载扩展构建产物中的 ESM 模块:
type CalculatorModule = {
  calculate(input: number): number;
};

const calculator =
  await ctx.extensionResource.importModule<CalculatorModule>(
    "dist/calculator.js",
  );

const result = calculator.calculate(21);
目标文件必须是浏览器可执行的 ESM。第三方依赖应由扩展的构建流程打进 dist/,不要在这里填写 npm 包名或电脑上的文件路径。

Worker

使用 createWorker(),不要把 url() 的结果直接传给 new Worker()
function ParserPanel() {
  const ctx = useExtensionContext();

  useEffect(() => {
    const worker = ctx.extensionResource.createWorker(
      "dist/parser-worker.js",
      { name: "parser" },
    );

    worker.postMessage({ text: "待分析文本" });
    worker.addEventListener("message", (event) => {
      console.log(event.data);
    });

    return () => worker.terminate();
  }, [ctx]);

  return null;
}
默认 type"module"。确实需要旧式 Worker 时可以传入 { type: "classic" } Electron 的 renderer 仍执行浏览器的同源与 Worker 加载规则。createWorker() 会通过宿主提供的同源引导脚本加载真实入口,避免扩展作者依赖 Studio 的端口或 file: 地址。

错误排查

现象检查项
提示“路径不需要 ./ 前缀”./assets/data.json 改为 assets/data.json
fetch() 返回 404确认文件存在,且大小写与路径完全一致
Studio 可用,打包后找不到把文件移动到 assets/,或让构建流程输出到 dist/
动态模块导入失败确认目标是 ESM,并且它的相对依赖也已输出
Worker 启动失败监听 Worker 的 error 事件,并确认入口没有依赖 Node.js API
Studio 的程序预览会优先显示可操作的错误摘要;完整的 React 和宿主堆栈可以在“技术堆栈”中展开或复制。