API v1
Documentation/开发指南

请求数据与调用服务

从超级块请求一次数据,或在后台调用另一插件的功能。

Plugin API v1

Documentation content is currently available in Simplified Chinese.

在实际开发中,并非所有数据都需要像广播电台一样不间断地推送。

例如:

  • 按需拉取:天气卡片通常只需在用户点击“刷新”或定时触发时单次拉取最新预报;
  • 插件间协作:一个汇率转换动作,可以在执行时直接向专门的金融数据插件请求最新的货币换算结果。

这两种典型场景在飞块中分别通过**数据提供者(DataProvider)和插件服务(Service)**来优雅实现。


#1. 从前端页面单次请求数据(requestData)

当超级块页面需要主动从后台拉取一次数据时,先在清单的 dataProviders 中声明该能力,并在后台入口中编写对应的响应函数。

在前端 HTML/JavaScript 页面中,直接调用页面 SDK 的 requestData():

javascript
async function refreshWeather() {
  try {
    // 向后台请求 'current' 数据源,可传入自定义查询参数
    const data = await window.FlyonDeckWidget.requestData('current', {});
    document.querySelector('#value').textContent = data.message;
  } catch (error) {
    // 优雅捕获网络异常或后台错误并提示用户
    document.querySelector('#value').textContent = `刷新失败:${error.message}`;
  }
}

后台的处理函数将安全接收到前端页面传递的输入参数、当前实例 ID 以及该实例保存的用户设置:

js
const { t: tr } = require(process.env.FLYONDECK_SDK_PATH || "@flyondeck/plugin-sdk");
"use strict";
const { definePlugin } = require(process.env.FLYONDECK_SDK_PATH);

definePlugin({
  dataProviders: {
    async current({ settings }, context) {
      // privateNote 只属于后台,绝不把 settings 整体返回或记录到日志。
      const todo = await context.network.getJson(
        `https://jsonplaceholder.typicode.com/todos/${settings.todoId || 1}`,
      );
      if (
        !todo ||
        typeof todo.title !== "string" ||
        typeof todo.completed !== "boolean"
      )
        throw new Error(tr("messages.index.external-service-returned-an-invalid-format"));
      return { message: todo.title, completed: todo.completed };
    },
  },
});

架构设计优势:

  • 天然免除跨域(CORS)烦恼:请求是由插件的后台 Node.js 进程发起的,前端页面无需配置复杂的代理或规避浏览器的跨域拦截。
  • 保护核心敏感信息:私密 API Key 或身份令牌可安全保存在后台内存或配置中,只将过滤后的干净数据返回给前端展示,杜绝凭证泄露。

📚 深入参考:清单字段规范见 DataProvider 参考文档,前端 API 细节见 requestData() 参考。


#2. 桌面预览与真实数据策略

很多开发者容易陷入一个误区:“只要处于桌面端编辑器的预览模式,就一律显示假数据”。

请牢记:桌面端已放置在画布上的组件同样可以且应当拉取真实数据。

  • getContext().mode === 'preview' 仅说明当前组件处于桌面编辑环境,而不是指“当前组件没有数据或只是假人”。只要实例已经放置在画布上,请照常调用 requestData() 并优先渲染真实结果。
  • 只有在组件选择目录卡片中(尚未拖入画布、确实没有实例 ID 和配置),或者遇到硬件能力无法在电脑端模拟时,才展示带有“示例”字样的模拟内容。

🎯 详细设计指南:请完整阅读桌面端与移动端数据同步的最佳实践。你也可以在网络组件示例中观察完整的拉取与容灾处理范式。


#3. 插件间跨界协作:提供与调用服务

飞块拥有开放繁荣的插件生态。你的插件不仅可以独立工作,还能像微服务一样对外提供接口,或是调用其它成熟插件的现成能力。

#第一步:服务提供方声明与实现

提供方在 manifest.json 的 contributes.services 中声明服务 ID、接口版本及开放的方法,并在 definePlugin 中注入实现。

例如,一个工具插件开放了一个加法计算服务 sum:

js
const { t: tr } = require(process.env.FLYONDECK_SDK_PATH || "@flyondeck/plugin-sdk");
"use strict";
const { definePlugin } = require(process.env.FLYONDECK_SDK_PATH);

definePlugin({
  services: {
    numbers: {
      sum({ values }) {
        if (
          !Array.isArray(values) ||
          values.length > 100 ||
          values.some((v) => typeof v !== "number" || !Number.isFinite(v))
        )
          throw new Error(tr("messages.index.values-must-be-a-list-of-up-to-100-finite-numbers"));
        const total = values.reduce((sum, v) => sum + v, 0);
        if (!Number.isFinite(total))
          throw new Error(tr("messages.index.sum-exceeds-the-finite-number-range"));
        return { total };
      },
    },
  },
});

#第二步:服务消费方声明与调用

想要使用该服务的插件,首先在 package.json 或 manifest.json 的 dependencies 中指定提供方插件的包版本范围;然后在 manifest.json 的 uses 列表中精准声明所需调用的服务标识与接口大版本:

js
const { t: tr } = require(process.env.FLYONDECK_SDK_PATH || "@flyondeck/plugin-sdk");
"use strict";
const { definePlugin } = require(process.env.FLYONDECK_SDK_PATH);

definePlugin({
  actions: {
    async sum({ values }, context) {
      try {
        return await context.plugins.call(
          "example.service-provider",
          "numbers",
          "sum",
          { values },
        );
      } catch (error) {
        throw new Error(
          tr("messages.index.number-service-call-failed-check-that-the-provider-is-installed-and-runn", [error.message]),
        );
      }
    },
  },
});

在业务逻辑中,通过 context.plugins.call(providerId, serviceId, method, args) 即可实现高内聚、低耦合的进程间函数调用!

💡 健康检查与优雅提示:跨插件调用要求提供方插件已处于安装且激活运行状态。如果提供方尚未启动,调用会抛出明确的未就绪错误;消费方可据此向用户提示“请先在飞块中启用 XX 辅助插件”。详细字段见 Service 参考 与 context.plugins 参考。


#4. 触发一次性系统事件(context.events)

如果插件后台在完成某个长耗时任务后(如批量压缩图片完成、下载大文件成功),需要向飞块宿主汇报一条瞬时通知,可以通过事件通道发送:

javascript
await context.events.emit('downloadComplete', { taskId: '123', status: 'success' });

事件会被飞块宿主捕获并分发,它与“状态”不同,不会在宿主内存中长期保留快照。如果你的目的只是把执行结果交由自动化工作流的下一步骤读取,直接在动作(Action)的处理函数中通过 return 返回结果才是最地道、最便捷的做法。详细 API 见 context.events.emit() 参考。

Updated 2026-11-04
View implementation sources