流星雨模块框架文档中心

界面开发

模块界面是可选的。没有页面也能靠 set_card / push_fields / push_titlebar 在主界面展示数据(见 外壳集成);有页面时,它跑在宿主主界面的 iframe 里,技术栈不限。

页面怎么被加载

多页面注册

ui/ 目录下的所有 HTML 都会自动注册为该模块的可打开页面,不限于 manifest 指定的那一个。manifest.ui 指定的页面排在首位、作为默认首页。

text
my_module/ui/
├── index.html      # 默认页(manifest.ui 指定)
└── fastboot.html   # 自动注册的第二个页面

用户在外壳侧边栏展开你的模块,就能在子页面之间切换。页面之间共享同一个后端(RPC、事件都一样),适合"同一设备的不同视图"这类场景。

桥接 SDK

一行引入:

html
<script src="/jade-bridge.js"></script>
前端 API说明
await jade.call(module, method, ...args)调用任意模块(含自己)的注册方法,返回 Promise;失败时 reject
jade.on(topic, fn)订阅事件,fn(payload, source);返回取消订阅的函数
jade.dialog(如环境提供)原生对话框等宿主注入能力,SDK 不会覆盖它

建议页面一加载就订阅自己的事件主题。模块在后台推数据,页面自动刷新,不用轮询。

原生 HTML(零依赖)

html
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <script src="/jade-bridge.js"></script>
</head>
<body>
  <button id="btn">刷新</button>
  <pre id="out">—</pre>
  <script>
    async function refresh() {
      try {
        const data = await jade.call('my_module', 'snapshot');
        document.getElementById('out').textContent = JSON.stringify(data, null, 2);
      } catch (e) {
        document.getElementById('out').textContent = '错误: ' + e.message;
      }
    }
    document.getElementById('btn').onclick = refresh;

    jade.on('my_module/update', (payload, source) => {
      document.getElementById('out').textContent = JSON.stringify(payload);
    });
    refresh();
  </script>
</body>
</html>

Vue 3(本地文件,离线可用)

text
my_module/ui/
├── index.html
└── vendor/vue.global.prod.js     # 自行下载放入,随模块一起打包
html
<div id="app">
  <button @click="load">刷新</button>
  <pre>{{ text }}</pre>
</div>
<script src="vendor/vue.global.prod.js"></script>
<script src="/jade-bridge.js"></script>
<script>
  Vue.createApp({
    data: () => ({ text: '—' }),
    methods: {
      async load() {
        try { this.text = JSON.stringify(await jade.call('my_module', 'snapshot')); }
        catch (e) { this.text = '错误: ' + e.message; }
      }
    },
    mounted() {
      this.load();
      jade.on('my_module/update', (p) => this.text = JSON.stringify(p));
    }
  }).mount('#app');
</script>

React(UMD 本地文件)

html
<div id="root"></div>
<script src="vendor/react.production.min.js"></script>
<script src="vendor/react-dom.production.min.js"></script>
<script src="/jade-bridge.js"></script>
<script>
  const { useState, useEffect } = React;
  function App() {
    const [text, setText] = useState('—');
    const load = async () => {
      try { setText(JSON.stringify(await jade.call('my_module', 'snapshot'))); }
      catch (e) { setText('错误: ' + e.message); }
    };
    useEffect(() => {
      load();
      jade.on('my_module/update', p => setText(JSON.stringify(p)));
    }, []);
    return React.createElement('div', null,
      React.createElement('button', { onClick: load }, '刷新'),
      React.createElement('pre', null, text));
  }
  ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));
</script>

几条硬规矩