1. 概述与架构
JJTalk 轻应用采用 「统一容器版」 架构,共四层:
| 层级 | 职责 | 说明 |
|---|---|---|
| 运行层 | 原生 WebView 容器壳 | 顶栏(最小化 / 应用名 / 官方徽标 / 分享)、加载进度条、深色模式跟随,由客户端提供,应用无需实现 |
| 桥接层 | JSBridge 白名单接口 | L0 基础 / L1 聊天 / L2 身份(见第 4 节),按注册表 scopes 声明授权 |
| 接入层 | 服务端注册表 | appId → 名称 / 图标 / 入口 / 域名 / 权限 / 状态,客户端据此渲染列表与分享卡片 |
| 安全层 | 三方隔离 | 第三方只获得 appId 与本文档;平台服务端地址、存储、密钥一概不下发(见第 7 节) |
为什么是一套代码?
轻应用是标准 H5:安卓与 iOS 共用同一份页面与逻辑,未来桌面端 / Web 端容器就绪后同样直接运行。开发体验与写普通网页完全一致,不需要学习专有 DSL。2. 快速开始
- 编写页面:一个标准的
index.html(单文件,或连同 css / js / 图片打包为 zip,根目录必须有index.html)。页面需同时适配移动端窄屏与深色模式。 - 提交接入:v1.0 阶段由官方在管理后台上架(提供
appId、名称、描述、入口);自助提交与审核流在 v1.x 规划中。 - 出现在列表:客户端「发现 → 轻应用」列表按注册表
sort排序展示,点开即由容器壳加载你的入口 URL。 - 可被分享:用户点容器顶栏「分享」,会把
{type:13, appId}卡片发进任意会话;对方点卡片,客户端按appId查注册表打开应用——卡片不带 URL,防篡改防钓鱼。
最小可用页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>我的轻应用</title><!-- 容器内会隐藏页内标题;浏览器直访时兜底 -->
<style>body{font-family:-apple-system,"PingFang SC",sans-serif;padding:16px}
@media (prefers-color-scheme:dark){body{background:#17181C;color:#F2F0EC}}</style>
</head>
<body>
<h1>Hello JJTalk</h1>
<script>
if (window.__JJTALK__) {
// 运行在 JJTalk 容器内:可调用 JSBridge(见第 4 节)
console.log('host =', window.__JJTALK__.client);
}
</script>
</body>
</html>
3. 运行环境与容器
3.1 容器行为
- 容器以完整入口 URL 加载页面(由注册表下发),并自动追加
jjtalk=app参数;页面检测到该参数即知运行于容器内,应隐藏自有标题区/底边距,让容器顶栏接管。 - 容器在页面加载完成后注入全局标志
window.__JJTALK__:
window.__JJTALK__ = { ver: 1, client: 'android' | 'ios' }
- 容器自带:加载进度条、返回轻应用列表(最小化)、深色模式跟随、分享(type=13 卡片)。应用不需要也不应该自绘顶栏。
- 容器外(浏览器直接访问入口 URL)页面必须可独立运行——所有 JJTalk 专属能力都要做特性检测与优雅降级。
3.2 环境约束
| 项 | 要求 |
|---|---|
| 屏幕适配 | 移动端竖屏优先,使用 rem/vw 或流式布局;禁用横向滚动 |
| 深色模式 | 跟随系统 prefers-color-scheme;品牌强调色建议 #F0B429,黄底文字固定深金 #96690A |
| 存储 | 应用数据自行存 localStorage;平台不提供云端存储 |
| 网络 | 仅允许 HTTPS;域名需在注册表 domains 声明 |
| 包体(托管模式) | 单文件 ≤5MB;zip ≤10MB(解压后 ≤20MB、≤200 个文件);文件名仅允许字母 / 数字 / ._- |
4. JSBridge 接口
JSBridge 按白名单 + 分级授权设计。L0 默认开放;L1 需注册表声明 scopes;L2 涉及用户身份,调用时客户端弹窗向用户请求授权。v1.0 随客户端版本逐步可用,调用前务必做特性检测。
L0 · 基础 默认开放
| 接口 | 说明 |
|---|---|
window.__JJTALK__ | 容器标志与客户端类型(已可用) |
jjtalk.setTitle(text) | 自定义顶栏标题(容器默认显示注册表应用名) |
jjtalk.toast(text) | 系统级轻提示 |
jjtalk.getClipboard() / setClipboard(text) | 剪贴板读写(读操作向用户提示) |
jjtalk.close() / minimize() | 关闭应用 / 收起到轻应用列表 |
jjtalk.onThemeChange(cb) | 深色模式切换回调 |
L1 · 聊天 需 scopes 声明
| 接口 | 说明 |
|---|---|
jjtalk.shareAppCard() | 把本应用以 type=13 卡片分享到会话(与容器顶栏分享等效) |
jjtalk.shareResult(imageData) | 把应用结果图(Canvas 导出)分享到会话 |
jjtalk.openChat() | 拉起会话选择器 |
L2 · 身份 授权弹窗
| 接口 | 说明 |
|---|---|
jjtalk.getAuthCode(scopes) | 用户授权后获得一次性 code;你的服务器持密钥调用平台兑换接口换取用户标识(与 wx.login 同款设计,密钥永不进入 H5) |
5. 分享卡片协议(type=13)
轻应用在会话中以 type=13 消息卡片流通。payload 结构:
{
"type": 13, // 消息类型:轻应用卡片
"content": "星座运势", // 应用名(旧版客户端 / 通知预览兜底显示)
"appId": "horoscope" // 唯一应用标识
}
- payload 不携带 URL。接收端收到卡片后,按本地快照或
/api/lightapp/registry?appId=解析名称、图标与入口——注册表是唯一入口权威,杜绝卡片被篡改跳转钓鱼页。 - 应用已下架 / 不存在时,接收端卡片显示「应用不存在或已下架」,不可打开。
- 单聊卡片走端到端加密管道(与 type=10 同构),群聊为普通消息。
- 红线:涉支付、抽奖类应用不做。
6. 注册表接口
6.1 应用列表
GET https://chat.jjtalk.cn/api/lightapp/list
→ { "ok": true, "apps": [ { ...app }, ... ] }
6.2 单应用解析
GET https://chat.jjtalk.cn/api/lightapp/registry?appId=horoscope
→ { "ok": true, "app": { ...app } } // 应用不存在或已下架 → 404
6.3 app 对象字段
| 字段 | 类型 | 说明 |
|---|---|---|
appId | string | 全局唯一标识,[a-z0-9_-]{2,32},创建后不可改 |
name | string | 应用名(≤32 字),列表 / 卡片 / 容器顶栏显示 |
desc | string | 一句话描述(≤200 字),列表副标题 |
iconUrl | string | 图标直链(方形,建议 128×128 PNG);可为空,空则端上用首字/默认图标占位 |
entryUrl | string | 应用入口(HTTPS);容器加载时自动追加 jjtalk=app |
domains | string[] | 应用可访问的业务域名白名单 |
scopes | string[] | 申请的 JSBridge 权限(L1/L2),如 ["share","auth"] |
official | bool | 是否官方应用(显示官方徽标;第三方应用恒为 false) |
state | int | 1=上线 0=下架;list 只返回上线应用 |
客户端策略:列表页先以本地快照即时渲染再异步刷新(约 1 分钟 TTL);卡片收到未知 appId 时实时查注册表并落盘缓存。管理端变更近实时生效。
7. 安全规范
三方隔离原则(重要)
- 第三方开发者只获得
appId与本文档;JJTalk 平台的服务端地址、数据库、内部 API、密钥一概不提供、不经手、不落库。 - 应用业务数据由开发者自己的服务器(HTTPS)承载,平台不做代理。
- 用户身份通过 L2
getAuthCode一次性 code + 服务器端兑换获得,密钥只存在你自己的服务器。
- 密钥不进 H5:任何第三方 API 的 key/secret 都不得写进前端代码或请求直连第三方(可被抓包提取);需要第三方数据时由你自己的服务器中转。
- 域名白名单:入口与业务域名需在注册表声明;未声明的域名访问会被限制(v1.x 强制)。
- 敏感接口二次授权:L1/L2 调用时客户端向用户弹窗确认,用户可拒绝。
- 内容红线:不做支付 / 抽奖 / 博彩类;不做诱导分享;遵守当地法律法规。
- 降级兜底:依赖外部 API 的应用必须设计降级方案(如官方星座运势:第三方接口失败回落内置文案库),保证应用永远可用。
8. 发布流程
- 准备:确定
appId、名称、描述、图标(128×128 方形 PNG)、入口 URL 或托管包。 - 接入评审:v1.0 阶段联系官方接入(自助提交流与审核后台在 v1.x 开放)。
- 上架:官方配置注册表并上架;客户端列表近实时更新。
- 迭代:托管模式重新上传包即全量替换(客户端静态资源约 1 分钟缓存);外链模式改你的服务器即时生效。
- 下架:官方可随时下架应用,下架后列表隐藏、历史分享卡片不可打开。
9. 兼容性与 FAQ
各端支持
| 端 | v1.0 支持 |
|---|---|
| Android / iOS | 完整:列表、容器、分享卡片收发 |
| 桌面 / Web | 兜底:收到 type=13 显示纯文本提示(容器在后续版本支持) |
| 浏览器直访 | 支持:入口 URL 可直接打开(无容器顶栏与 JSBridge) |
FAQ
- Q:轻应用和小程序的区别?
A:哲学相同(一套代码、多端运行、平台托管分发),但轻应用基于标准 WebView + JSBridge,无专有框架、无包体审核工具链,更轻量,适合娱乐 / 工具类应用。 - Q:能用 Cookie / localStorage 吗?
A:可以,数据存用户设备本地,随 WebView 管理策略存活;重要数据请同步到你自己的服务器。 - Q:分享卡片可以带参数吗?
A:v1.0 的 type=13 payload 仅含appId,不含 URL 与参数;应用内状态分享(如测试结果)建议走 L1shareResult图片结果卡。 - Q:应用更新需要发版吗?
A:不需要。托管模式重新上传即生效;外链模式改自己的服务器即时生效。仅 JSBridge 新能力需要客户端发版。
路线图
v1.x:自助提交流 + 审核后台、桌面 / Web 容器、L1/L2 接口全量开放、openChat / 会话内卡片交互。当前 v1.0 的接口形态即最终形态的子集,按本文档开发的应用可直接平滑过渡。