@htyf-mp/rn-sdk
v0.0.1
Published
React Native App SDK — unified native capabilities via ref API
Readme
@htyf-mp/rn-sdk
React Native App SDK — 为红糖云服小程序容器提供统一的原生能力封装。宿主通过 AppSdk 组件挂载 SDK,业务方通过 ref 调用能力;小程序侧通过 @htyf-mp/js-sdk 间接使用同一套引擎。
定位与关系
┌─────────────────────────────────────────────────────────────┐
│ 宿主 App(app-core / MiniAppsContainer) │
│ ┌─────────────┐ ref / hostAdapter ┌─────────────────┐ │
│ │ AppSdk │ ◄────────────────────── │ 宿主业务逻辑 │ │
│ └──────┬──────┘ └─────────────────┘ │
│ │ AppSdkFacade + SDKFuncs │
│ ▼ │
│ ┌─────────────┐ 全局注入 ┌───────────────┐ │
│ │ 沙盒 proxy │ ───────────────────────► │ @htyf-mp/js-sdk│ │
│ └─────────────┘ DGZ_GLOBAL_CURRENT_ │ (小程序代码) │ │
│ MP_CLIENT └───────────────┘ │
└─────────────────────────────────────────────────────────────┘| 包 | 角色 |
|---|---|
| @htyf-mp/rn-sdk | RN 侧 SDK 实现:组件、模块、原生封装、UI 能力 |
| @htyf-mp/js-sdk | 小程序 JS 侧 SDK,调用全局注入的 SDKFuncs 实例 |
| app-core | 宿主容器,组装 AppSdk、沙盒、胶囊栏等 |
设计原则:
- 业务方优先通过
AppSdkRef调用能力,避免直接 import 内部modules//services/。 - 禁止业务代码直接调用
NativeModules;统一走 SDK 的native/与bridge/层。 - UI 类能力(播放器、浏览器、扫码等)通过
HostAdapter或createSdkHostAdapter注入,宿主可覆盖默认实现。
安装
本包为 monorepo workspace 成员,在宿主 package.json 中声明:
{
"dependencies": {
"@htyf-mp/rn-sdk": "workspace:*"
}
}重要:
@htyf-mp/rn-sdk不会自动安装原生依赖。宿主 App 必须自行安装下方peerDependencies,否则运行时会出现模块找不到、能力降级或报错。在 monorepo 中,这些包应安装在原生 App 目录(以便 autolinking 生效),版本以根 lockfile 为准。
Peer Dependencies(宿主需额外安装)
与 package.json 中 peerDependencies 对齐。必装依赖:
# pnpm 示例(版本请与 monorepo 根 lockfile 对齐,勿随意升级)
pnpm add react-native-mmkv \
react-native-permissions \
react-native-image-picker \
@react-native-camera-roll/camera-roll \
@react-native-clipboard/clipboard \
@react-native-community/netinfo \
@dr.pogodin/react-native-fs \
react-native-safe-area-context \
react-native-orientation-locker \
react-native-localize \
react-native-nitro-haptics \
react-native-nitro-unzip \
react-native-nitro-http-server \
react-native-volume-manager \
react-native-ble-manager \
@react-native-documents/picker \
react-native-quick-base64 \
react-native-quick-crypto \
md5| 分类 | 包名 |
|---|---|
| 核心 | react, react-native(宿主通常已有) |
| 存储 | react-native-mmkv |
| 权限 | react-native-permissions |
| 媒体 | react-native-image-picker, @react-native-camera-roll/camera-roll |
| 系统 | @react-native-clipboard/clipboard, @react-native-community/netinfo |
| 文件 | @dr.pogodin/react-native-fs, @react-native-documents/picker |
| 布局 | react-native-safe-area-context, react-native-orientation-locker |
| 本地化 | react-native-localize |
| 触觉 | react-native-nitro-haptics |
| 压缩 | react-native-nitro-unzip |
| 网络 | react-native-nitro-http-server |
| 音量 | react-native-volume-manager |
| 蓝牙 | react-native-ble-manager |
| 编码 | react-native-quick-base64, react-native-quick-crypto |
| 工具 | md5 |
安装原生依赖后需重新执行 iOS pod install / Android 同步,并重新编译原生 App。
快速开始
1. 挂载 AppSdk
import React, { useRef, useCallback } from 'react';
import {
AppSdk,
createSdkHostAdapter,
type AppSdkRef,
type AppSdkConfig,
} from '@htyf-mp/rn-sdk';
const config: AppSdkConfig = {
apiBaseUrl: 'https://api.example.com',
token: '',
userId: '',
debug: __DEV__,
};
function MiniAppHost() {
const sdkRef = useRef<AppSdkRef>(null);
const handleReady = useCallback((ref: AppSdkRef) => {
sdkRef.current = ref;
}, []);
return (
<AppSdk
ref={sdkRef}
appId="my-mini-app"
config={config}
launchOptions={{ path: '/index', query: {} }}
hostAdapter={createSdkHostAdapter()}
onReady={handleReady}
setGlobal
currentGlobal={sandboxProxy}
>
{/* 小程序 Root 组件,需等 SDK ready 且沙盒配置完成后再渲染 */}
</AppSdk>
);
}2. 通过 ref 调用能力
const sdk = sdkRef.current;
if (!sdk) return;
await sdk.showToast('操作成功');
await sdk.showModal({
title: '确认',
description: '是否继续?',
onConfirm: () => {},
onCancel: () => {},
});
const qr = await sdk.scanQRCode({ text: '请扫描二维码' });
const image = await sdk.pickImage({ mediaType: 'photo', includeBase64: true });
const device = await sdk.getDeviceInfo();3. 监听 SDK 事件
const unsubscribe = sdk.on('networkChange', ({ state }) => {
console.log(state.type, state.isConnected);
});
sdk.on('appStateChange', ({ state }) => {
console.log('AppState:', state);
});
// 组件卸载时
unsubscribe();AppSdk Props
| Prop | 类型 | 说明 |
|---|---|---|
| config | AppSdkConfig | API 地址、token、debug 等运行时配置 |
| appId / appid | string | 小程序唯一 ID |
| hostAdapter | HostAdapter \| SDKHostAdapter | 宿主能力注入(登录、分享、UI 弹层等) |
| adAdapter | MiniAppAdAdapter | 广告 Banner / 激励视频 / 插屏 |
| launchOptions | Partial<LaunchOptions> | 启动参数(path、query、isVip 等) |
| capsuleBarLayout | Partial<CapsuleBarLayout> | 胶囊栏布局,会同步到引擎 |
| currentGlobal | Record<string, unknown> | 注入 SDK 的全局对象(通常为沙盒 proxy) |
| setGlobal / setGloabl | boolean | 是否向 currentGlobal 写入 DGZ_GLOBAL_CURRENT_MP_CLIENT(默认 true) |
| storage | StorageInterface | 自定义存储;默认按 appId 创建 MMKV 实例 |
| isNative | boolean | 是否原生小程序模式(影响 MagicModal 实例隔离) |
| isVip | boolean | VIP 标识,透传给引擎 |
| getAppConfig | (appUrl) => Promise<AppInfoType> | 拉取应用配置 |
| handleOpenApp | (appInfo) => Promise<boolean> | 打开其他小程序 |
| handleCloseApp | () => Promise<boolean> | 关闭当前小程序 |
| handleRestartApp | () => void | 重启当前小程序 |
| onReady | (ref: AppSdkRef) => void | SDK 初始化完成回调 |
| children | ReactNode | 子树(SDK ready 后才渲染) |
AppSdkConfig
interface AppSdkConfig {
apiBaseUrl?: string;
baseUrl?: string;
token?: string;
userId?: string;
env?: 'development' | 'staging' | 'production';
debug?: boolean; // true 时开启 SDK 日志
theme?: { mode?: 'light' | 'dark' | 'system'; primaryColor?: string };
locale?: string;
headers?: Record<string, string>;
}AppSdkRef API
AppSdkRef 是推荐对外使用的统一接口,由 AppSdkFacade 实现,内部按职责拆分为多个 Module。
生命周期
| 方法 | 说明 |
|---|---|
| initialize() | 初始化(挂载时自动调用) |
| destroy() | 销毁模块与事件总线 |
| reload() | 销毁后重新注册模块 |
| pause() / resume() | 暂停 / 恢复 |
| updateConfig(partial) | 热更新配置 |
用户与路由(依赖 hostAdapter)
| 方法 | 说明 |
|---|---|
| login(credentials?) | 登录 |
| logout() | 登出 |
| share(options) | 系统分享 |
| openPage(options) / closePage() | 打开 / 关闭宿主页面 |
| navigate(route, params?) | 路由跳转 |
| openApp(appInfo) / closeApp() / restartApp() | 小程序生命周期 |
UI 与交互
| 方法 | 说明 |
|---|---|
| showToast(msg \| ToastOptions) | Toast 提示 |
| showLoading(msg?) / hideLoading() | 全局 Loading |
| showModal(options) | 确认对话框 |
| scanQRCode(options?) | 二维码扫描 |
| openCamera() | 相机拍照(返回 base64 或 uri) |
媒体与文件
| 方法 | 说明 |
|---|---|
| pickImage(options?) | 相册选图 |
| launchCamera(options?) | 调起相机 |
| getPhotos(params) | 分页读取相册 |
| uploadFile(file) | 上传文件 |
| downloadFile(options) | 下载并保存到本地 |
设备与权限
| 方法 | 说明 |
|---|---|
| requestPermission(permission) | 请求权限(含说明弹窗) |
| getDeviceInfo() | 语言、时区、安全区等 |
| getLocation() | 定位 |
| getNetworkType() | 当前网络状态 |
| callNative(module, method, params?) | 通用原生桥接 |
事件与扩展
| 方法 | 说明 |
|---|---|
| on(event, handler) | 订阅事件,返回取消函数 |
| off(event, handler) | 取消订阅 |
| emit(event, payload) | 派发事件 |
| registerModule(module) | 注册自定义模块 |
| unregisterModule(name) | 移除模块 |
| getEngine?() | 获取底层 SDKFuncs(mp-jssdk 兼容,标注为 internal) |
SDK 事件
| 事件 | Payload |
|---|---|
| sdkReady | { appId: string } |
| sdkDestroyed | {} |
| configUpdated | { config } |
| networkChange | { state: NetInfoState } |
| appStateChange | { state: AppStateStatus } |
| analytics | { name, params? } |
SDKFuncs(引擎层)
SDKFuncs 是底层能力引擎,承载全部原生 API。小程序通过 @htyf-mp/js-sdk 访问的正是该实例(经全局 key 注入)。
新宿主代码应优先使用 AppSdkRef;仅在需要与 jssdk 对齐或访问完整原生 API 时使用 getEngine()。
UI 能力(也可由 HostAdapter 覆盖)
await engine.openVideoPlayer({ url: 'https://...', title: '视频' });
await engine.openAudioPlayer({ type: 'open', items: [...] });
await engine.openBrowser('https://example.com');
await engine.openImages({ images: [{ uri }], imageIndex: 0, visible: true });
await engine.openQR({ text: '扫码' });
await engine.showToast({ type: 'success', title: '完成' });
await engine.showModal({ title, description, onConfirm, onCancel });
await engine.openCamera();
await engine.puppeteer({ url, jscode }); // WebView 执行脚本原生能力一览
完整参数与返回值见 SDK_NATIVE_APIS.md。
| 分类 | 代表方法 |
|---|---|
| 图片 / 相册 | launchImageLibrary, launchCamera, getPhotos, saveImageToAlbum, saveVideoToAlbum |
| 文档 | pickDocument |
| 剪贴板 | setClipboardString, getClipboardString |
| 网络 | getNetworkState, addNetworkListener |
| 文件系统 | getDocumentDir, getCachesDir, fsReadFile, fsWriteFile, fsReadDir, fsMkdir, fsExists, fsUnlink, fsCopyFile, fsMoveFile |
| 下载 / 服务 | downloadAndSaveFile, createStaticServer |
| 触觉 | triggerHaptic |
| 本地化 | getLocales, getCurrencies, getCountry, getTimeZone |
| 压缩 | unzip, zip |
| 音量 | getVolume, setVolume, addVolumeListener |
| 编码 | encodeBase64, decodeBase64, getRandomBytes |
| 屏幕方向 | lockToPortrait, lockToLandscapeLeft, unlockAllOrientations, getOrientation, addOrientationListener 等 |
| 安全区 | getSafeAreaInsets |
| 广告 | showRewardedAd, showInterstitialAd, AdBanner |
| BLE | bleStart, bleScan, bleConnect, bleRead, bleWrite, bleStartNotification, bleAddListener 等 |
| 应用 | openApp, closeApp, restartApp, getAppConfig |
| 存储 | getStorage, storage(MMKV) |
Host Adapter
SDKHostAdapter 定义宿主可注入的 UI 弹层 能力。HostAdapter 在其基础上扩展登录、分享、路由等宿主业务方法。
SDKHostAdapter 接口
interface SDKHostAdapter {
openVideoPlayer?: (options: unknown) => Promise<() => void>;
openAudioPlayer?: (items: unknown) => Promise<() => void>;
puppeteer?: (items: unknown) => Promise<unknown>;
openBrowser?: (url, options?) => Promise<() => void>;
openQR?: (props?: { text?: string }) => Promise<unknown>;
openPermissions?: (info: unknown) => Promise<boolean>;
openImages?: (data: unknown) => Promise<() => void>;
showToast?: (options: unknown) => Promise<() => void>;
showModal?: (options: unknown) => Promise<() => void>;
openCamera?: () => Promise<unknown>;
}多数 UI 方法返回 关闭函数 () => void,用于主动 dismiss 弹层。
createSdkHostAdapter
工厂函数,供 app-core 侧生成默认 Host Adapter(基于 MagicModal + features/*):
import { createSdkHostAdapter } from '@htyf-mp/rn-sdk';
// 方式一:使用内置默认实现(需传入 modal 上下文,AppSdk 内部已自动处理)
const adapter = createSdkHostAdapter({
magicModal,
useMagicModal,
getSDK: () => engineRef.current,
});
// 方式二:完全自定义
const adapter = createSdkHostAdapter({
adapter: myCustomHostAdapter,
});
// 方式三:不传参 — 返回 undefined,由 SDKFuncs 内置逻辑兜底
const adapter = createSdkHostAdapter();AppSdk 在挂载时会自动调用 createSdkHostAdapter:若传入 hostAdapter prop 则优先使用;否则在具备 magicModal 上下文时生成默认实现。
与 mp-jssdk 的全局契约
SDK 初始化后,会将 SDKFuncs 适配实例写入全局对象:
import { DGZ_GLOBAL_CURRENT_MP_CLIENT, SDK_GLOBAL_KEYS } from '@htyf-mp/rn-sdk';
// 等价常量
SDK_GLOBAL_KEYS.CURRENT_MP_CLIENT; // '__DGZ_GLOBAL_CURRENT_MP_CLIENT__'小程序代码通过 @htyf-mp/js-sdk 读取该全局变量并调用方法,例如:
import jssdk from '@htyf-mp/js-sdk';
await jssdk.showToast({ type: 'success', title: 'Hello' });
const result = await jssdk.launchImageLibrary({ mediaType: 'photo' });注意: 沙盒环境下应将 currentGlobal 设为沙盒 proxy,并确保 setGlobal={true},以便每个小程序实例拥有隔离的全局作用域。
自定义模块
实现 ISdkModule 可扩展 SDK 能力:
import type { ISdkModule, SdkContextValue } from '@htyf-mp/rn-sdk';
class MyModule implements ISdkModule {
readonly name = 'myFeature';
private ctx!: SdkContextValue;
initialize(ctx: SdkContextValue) {
this.ctx = ctx;
}
async doSomething() {
this.ctx.logger.info('hello');
}
destroy() {
// 清理
}
}
sdkRef.current?.registerModule(new MyModule());内置模块包括:toast, loading, modal, qrcode, camera, image, file, upload, download, share, clipboard, permission, network, device, location, storage, auth, router, media, browser, analytics, notification, bridge, ble。
存储
默认按 appId 创建 MMKV 加密存储:
import { NewMMKVStorage, createStorageForApp } from '@htyf-mp/rn-sdk';
const storage = createStorageForApp('my-app-id');
// 或
const storage = new NewMMKVStorage('__dgz996_app_appid_[my-app-id]_for__');实现 StorageInterface 可注入自定义存储到 AppSdk 的 storage prop。
日志
import { logger, createComponentLogger } from '@htyf-mp/rn-sdk';
logger.configure({ enabled: true });
const log = createComponentLogger('MyFeature');
log.info('ready');
log.warn('something odd');
log.error('failed', error);config.debug === true 时 AppSdk 会自动开启 SDK 日志。
错误类型
import {
SdkError,
NativeError,
PermissionError,
NetworkError,
} from '@htyf-mp/rn-sdk';| 类 | code | 场景 |
|---|---|---|
| SdkError | 多种 | 通用 SDK 错误 |
| NativeError | NATIVE_ERROR | 原生模块调用失败 |
| PermissionError | PERMISSION_DENIED | 权限被拒绝 |
| NetworkError | NETWORK_ERROR | 网络相关错误 |
导出说明
推荐入口(@htyf-mp/rn-sdk)
| 导出 | 用途 |
|---|---|
| AppSdk, AppSdkFacade | 主组件与 Facade 类 |
| AppSdkRef, AppSdkConfig, HostAdapter | 类型 |
| createSdkHostAdapter | Host Adapter 工厂 |
| NewMMKVStorage, createStorageForApp | 存储 |
| logger, createComponentLogger | 日志 |
| SDK_GLOBAL_KEYS, DGZ_GLOBAL_CURRENT_MP_CLIENT | 全局契约 |
| features/* | UI 组件与 API(宿主集成 Modal、Toast 等) |
| contracts/* | SDKHostAdapter, SDKClientLike 等契约类型 |
稳定子入口(publicApi.ts 语义)
面向后续独立 npm 包抽离,新代码可优先从主入口导入;publicApi.ts 聚合了 SDKFuncs、createSdkHostAdapter、contracts、features 等稳定符号。
已废弃
| 符号 | 替代 |
|---|---|
| SDKPortal | AppSdk |
| 直接依赖 app-core/features/* | @htyf-mp/rn-sdk 的 features 导出 |
目录结构
src/
├── AppSdk.tsx # 主组件:Provider、EventBus、全局注入
├── AppSdkFacade.ts # AppSdkRef 实现,模块编排
├── SDKFuncs.tsx # 底层引擎,全量原生 API
├── createSdkHostAdapter.tsx# 默认 Host Adapter 工厂
├── ModuleRegistry.ts # 模块注册表
├── modules/ # 按职责拆分的能力模块
├── services/ # ApiService、Storage、File、Router 等
├── native/ # 原生模块懒加载
├── bridge/ # NativeBridge 通用桥接
├── features/ # UI 能力(播放器、浏览器、扫码、Toast…)
├── contracts/ # 与 mp-jssdk 共享的运行时契约
├── compat/ # SDKPortal、SDKFuncsAdapter 兼容层
├── types/ # sdk 引擎类型、AppSdkRef、Config、事件、错误类型
├── events/ # EventBus
├── providers/ # SdkProviders(Context)
└── SDK_NATIVE_APIS.md # 原生库 API 对照文档架构要点
- 双 API 层:
AppSdkRef(Facade + Modules)面向宿主新业务;SDKFuncs(Engine)面向 jssdk 与历史代码。 - Host Adapter 模式:UI 弹层与宿主业务解耦,默认实现可替换。
- 模块注册表:核心能力模块化,支持
registerModule扩展。 - 事件总线:网络、前后台、配置变更等通过
on/emit统一分发。 - 全局契约:
DGZ_GLOBAL_CURRENT_MP_CLIENT连接 RN SDK 与 JS SDK,保证小程序代码零改动迁移。
从 SDKPortal 迁移
- import { SDKPortal } from '@htyf-mp/rn-sdk';
+ import { AppSdk, type AppSdkRef } from '@htyf-mp/rn-sdk';
- <SDKPortal appid="x" launchOptions={...} onReady={(sdk) => ...} />
+ <AppSdk appId="x" config={{ debug: __DEV__ }} launchOptions={...} onReady={(ref) => ...} />onReady 回调参数由 SDKFuncs 变为 AppSdkRef;若仍需引擎实例,使用 ref.getEngine?.()。
相关文档
- SDK_NATIVE_APIS.md — 原生库与方法对照、jssdk 调用示例
- @htyf-mp/js-sdk — 小程序侧 SDK
app-core中的MiniAppsContainer— 完整集成参考
开发
# 类型检查
pnpm --filter @htyf-mp/rn-sdk type-checkLicense
MIT
