170 lines
9.2 KiB
Markdown
170 lines
9.2 KiB
Markdown
# 小工具能力清单
|
||
|
||
> 小工具运行在受限容器中:**纯本地、不联网**,把它当作一个能力受限的浏览器页面。
|
||
> **以本文为基线**:命中「不可用」项必须移除或改用替代写法。
|
||
|
||
## 目录
|
||
|
||
- §1 可用能力
|
||
- §2 Native 能力(JSBridge)
|
||
- §3 不可用能力(Web API)
|
||
- §4 不可用行为
|
||
- §5 WebGL / 图形计算边界
|
||
- §6 常见交互怎么实现
|
||
- §7 能力扫描清单
|
||
|
||
---
|
||
|
||
## 1. 可用能力
|
||
|
||
### 页面与渲染
|
||
|
||
标准 HTML / CSS / JS 可用,但最终产物须满足目标内核基线:JS 见 [js-compatibility.md](./js-compatibility.md),CSS 见 [css-compatibility.md](./css-compatibility.md)。可使用基线内的 Flexbox / Grid / 动画 / 媒体查询、Canvas 2D(`getContext('2d')`)、WebGL(`getContext('webgl'/'webgl2')`,能力边界见 §5、性能与低端机降级见 [performance-budget.md](./performance-budget.md) §4–5),文本选择不限制。
|
||
|
||
### 媒体与文件
|
||
|
||
| 能力 | 用法 | 约束 |
|
||
| --- | --- | --- |
|
||
| 摄像头 | `navigator.mediaDevices.getUserMedia({ video: true })` | 用户手势触发 + 系统弹窗授权 |
|
||
| 麦克风 | `navigator.mediaDevices.getUserMedia({ audio: true })` | 用户手势触发 + 系统弹窗授权 |
|
||
| 选择图片 / 拍照 | `<input type="file">` | 系统选择器接管,**仅能选图片和视频**(无论 `accept` 如何设置) |
|
||
| 音视频播放 | `<video>` / `<audio>` | 内联播放,媒体文件须打包在内 |
|
||
|
||
### 数据存储
|
||
|
||
`localStorage` / `sessionStorage` / `IndexedDB` / `Cookie` / `Cache API` 均可用,按小工具独立隔离,其他小工具与外部无法访问。
|
||
|
||
Cookie 仅作本地存储:可读写、按 origin 隔离,但因不联网**不会随请求发往服务端**,不能用于登录态 / 鉴权。需要本地存储优先用 `localStorage` / `IndexedDB`。数据不保证永久持久化。
|
||
|
||
### 交互
|
||
|
||
`alert()` / `confirm()` 可用,以原生 UI 展示。
|
||
|
||
---
|
||
|
||
## 2. Native 能力(JSBridge)
|
||
|
||
容器会注入 **`window.xhs.miniTool.*`**,通过它调用 Native 能力(发笔记、存相册、跳 App 页等)。
|
||
|
||
| 规则 | 说明 |
|
||
| --- | --- |
|
||
| 唯一入口 | 只用 `window.xhs.miniTool.<apiName>(options)`,**禁止**自行 `postMessage` 到 bridge |
|
||
| 契约来源 | 以 [`jsbridge-api.md`](./jsbridge-api.md) 为准 |
|
||
| 参数校验 | 必填项、长度、数组上限等以 [`jsbridge-api.md`](./jsbridge-api.md) 为准 |
|
||
| 本地路径 | `saveImageToPhotosAlbum.filePath` 不支持网络 URL;base64 可先 `writeTempFile` 换 `filePath` |
|
||
| data:uri | `writeTempFile.data` 必须是完整 `data:<mime>;base64,...`(`canvas.toDataURL()` 原样传),裸 base64 会失败 |
|
||
| 发笔记 | `postNote.mediaInfo` 必填;图片走 `image_resources[].url`,视频走 `video_resources` |
|
||
| 跳原生页 | `openRedPage.type` 须命中 Native 规则表白名单,`params` 为语义参数 |
|
||
|
||
完整 API 列表、字段表与示例 → **[jsbridge-api.md](./jsbridge-api.md)**。
|
||
|
||
---
|
||
|
||
## 3. 不可用能力(Web API)
|
||
|
||
以下 API 已禁用,调用会抛异常、返回空值或被拦截,必须移除或改用替代写法。
|
||
|
||
| 分类 | 涉及 API | 替代方案 |
|
||
| --- | --- | --- |
|
||
| 定位 | `navigator.geolocation.getCurrentPosition` / `watchPosition` | 移除 |
|
||
| 剪贴板 | `navigator.clipboard.readText` / `writeText`、`document.execCommand('copy'/'cut'/'paste')` | 展示可选中文本,引导用户长按 / 选中手动复制 |
|
||
| 硬件连接 | `navigator.bluetooth` / `navigator.usb` / `navigator.hid` / `navigator.serial` | 移除 |
|
||
| 传感器 | `new Accelerometer()` / `new Gyroscope()` / `new Magnetometer()`、环境光、`DeviceMotionEvent` / `DeviceOrientationEvent` | 改用触摸 / 指针手势(见 §5),摇一摇类移除 |
|
||
| 实时通信 | `new WebSocket()`、`new EventSource()`、`new RTCPeerConnection()` | 移除(不联网,无轮询替代) |
|
||
| 后台运行 | Web Worker、SharedWorker、Service Worker(`navigator.serviceWorker.register`) | 移除,逻辑放主线程 |
|
||
| 屏幕 | `getDisplayMedia`(屏幕共享)、`Element.requestFullscreen`(全屏由容器统一管理) | 全屏用 CSS 沉浸式布局实现视觉全屏 |
|
||
| 设备信息 | `navigator.getBattery`、`navigator.connection`、`navigator.mediaDevices.enumerateDevices` | 移除 |
|
||
| 存储进阶 | `navigator.storage.persist`(持久化)、跨域存储访问 | 移除,本地缓存用 `localStorage` / `IndexedDB` |
|
||
| 凭据 | `navigator.credentials.get` / `create`(WebAuthn)、`navigator.locks` | 移除 |
|
||
| 窗口 | `window.open`(弹新窗口)、`window.prompt` | 单页内 JS 切换视图 DOM;输入用页内 Modal |
|
||
|
||
移动端 WebView 本身也不支持:支付 `PaymentRequest`、系统通知 / 推送、NFC、MIDI、XR / AR / VR、后台同步 / 下载、PWA 安装、窗口管理、指针 / 键盘锁定。一律移除。
|
||
|
||
---
|
||
|
||
## 4. 不可用行为
|
||
|
||
| 行为 | 说明 | 替代方案 |
|
||
| --- | --- | --- |
|
||
| 网络请求 | `fetch` / `XMLHttpRequest`、加载外部图片 / 字体 / 媒体等一切联网请求 | 所有资源打包在内,改本地相对引用;仅小型配置 / 数据可随包提供,大型只读数据集不适合小工具,见 [performance-budget.md](./performance-budget.md) §2 |
|
||
| 动态执行代码 | `eval()`、`new Function()` | 改写为静态逻辑 |
|
||
| WebAssembly | WASM 编译执行(依赖 WASM 的库无法运行) | 移除或改用纯 JS 实现 |
|
||
| iframe / object | 内嵌 iframe / object,或被外部页面嵌入 | 内容直接写进页面 |
|
||
| 表单跳转提交 | `<form>` 提交跳转 | `addEventListener('submit', e => e.preventDefault())` 后用 JS 处理 |
|
||
| 文件下载 | `a[download]`、blob 下载 | 移除 |
|
||
| 打开外链 / 新窗口 | `target="_blank"`、`window.open`、跳转站外 URL | 单页内 JS 切换视图 DOM |
|
||
| 跳转其他小工具 | 小工具间互相跳转 | 移除 |
|
||
| 长按菜单 | 系统长按菜单已禁用 | 用自定义交互替代 |
|
||
| 插件 | Flash 等浏览器插件 | 移除 |
|
||
|
||
---
|
||
|
||
## 5. WebGL / 图形计算边界
|
||
|
||
纯 WebGL 渲染可用,组合能力受限:
|
||
|
||
| 场景 | 是否可用 |
|
||
| --- | --- |
|
||
| 包内资源 / Canvas / 内存对象作为纹理 | ✅ |
|
||
| 外部域名图片作为纹理 | 🔴 不联网,纹理须打包在内 |
|
||
| 依赖 WASM 的加速库(Draco / Basis / ONNX / 抠图算法等) | 🔴 |
|
||
| 依赖 Worker 的离屏渲染(OffscreenCanvas + Worker) | 🔴 |
|
||
| SharedArrayBuffer 多线程 | 🔴 |
|
||
|
||
WebGL 适合用包内资源做本地渲染;AI 图像处理等重计算(需联网或 WASM 模型)无法支持。WebGL 可用不等于低端真机性能足够:DPR、像素、纹理、draw call、几何预算、动态降档与兜底必须遵守 [performance-budget.md](./performance-budget.md) §4–5。
|
||
|
||
---
|
||
|
||
## 6. 常见交互怎么实现
|
||
|
||
| 需求 | 实现 |
|
||
| --- | --- |
|
||
| 手势 / 拖拽 / 滑动 | `addEventListener('touchstart'/'touchmove'/'touchend')` 或 Pointer Events(`pointerdown`/`move`/`up`) |
|
||
| 拍照 / 录音 | `getUserMedia(...)`,由按钮点击等用户手势触发 + 授权 |
|
||
| 选择图片 / 视频 | `<input type="file">` |
|
||
| 复制文本 | 展示可选中文本,引导用户长按 / 选中复制 |
|
||
| 视觉全屏 | CSS 布局(`100vh` / flex + 隐藏滚动) |
|
||
| 页面跳转 | 单页内用 JS 切换视图 DOM |
|
||
| 输入弹窗 | 页内 Modal 组件 |
|
||
| 保存图片到相册 | `writeTempFile({ data: canvas.toDataURL(...) })`(须完整 data:uri)→ `saveImageToPhotosAlbum({ filePath })`,见 [jsbridge-api.md](./jsbridge-api.md) |
|
||
| 发布笔记 | `postNote({ mediaInfo, title?, content? })` |
|
||
| 跳转 App 搜索 / 用户页等 | `openRedPage({ type, params? })`,`type` 须在白名单内 |
|
||
|
||
---
|
||
|
||
## 7. 能力扫描清单
|
||
|
||
扫描代码,命中下列模式则**必须删除或改用替代写法**:
|
||
|
||
```
|
||
fetch( / XMLHttpRequest / new WebSocket( / new EventSource( / new RTCPeerConnection(
|
||
navigator.geolocation.getCurrentPosition / watchPosition
|
||
navigator.clipboard.readText / writeText · document.execCommand('copy')
|
||
navigator.bluetooth / navigator.usb / navigator.hid / navigator.serial
|
||
navigator.getBattery / navigator.connection / navigator.credentials / navigator.locks
|
||
navigator.mediaDevices.enumerateDevices / navigator.mediaDevices.getDisplayMedia
|
||
navigator.storage.persist / navigator.serviceWorker.register
|
||
new Worker( / new SharedWorker(
|
||
new Accelerometer() / new Gyroscope() / new Magnetometer()
|
||
DeviceMotionEvent / DeviceOrientationEvent / addEventListener('devicemotion' / 'deviceorientation')
|
||
Element.requestFullscreen / webkitRequestFullscreen
|
||
eval( / new Function( / WebAssembly.
|
||
window.open( / window.prompt(
|
||
location.href = / location.assign( (跳转站外 URL)
|
||
<form ...> 提交跳转 · <a ... download> · target="_blank"
|
||
<iframe> / <object>
|
||
<script src="https://..."> / <link href="https://..."> / <img src="https://..."> / CSS url(https://...)
|
||
—— 外部资源一律加载不到,须下载后打进 zip 改本地相对引用
|
||
```
|
||
|
||
**允许保留**:
|
||
|
||
```
|
||
navigator.mediaDevices.getUserMedia({ video / audio }) // 用户手势 + 授权
|
||
<input type="file"> // 选图片 / 视频
|
||
localStorage / sessionStorage / IndexedDB / Cookie / Cache API // 独立隔离
|
||
alert() / confirm()
|
||
touch / pointer events // 手势交互
|
||
标准 DOM / CSS / Canvas 2D / WebGL 渲染
|
||
```
|