微信小程序 API 接入
微信小程序 API 接入
阅读对象
本文面向使用 Kuikly 开发微信小程序页面的业务开发者。阅读之前,建议先完成 微信小程序工程接入 与 微信小程序平台开发方式,并已了解 微信小程序内置组件接入。
1. 背景:为什么还需要对微信小程序 API 单独封装?
Kuikly 已经在 commonMain 提供了一整套跨端 Module(RouterModule / NetworkModule / SharedPreferencesModule / CalendarModule / NotifyModule 等),这些 Module 由各平台(Android/iOS/HarmonyOS/H5/微信小程序)各自实现,能满足"写一次,到处跑"。
但在微信小程序平台上,仍然有一些能力是跨端 Module 无法覆盖、或直接使用会损失原生能力的:
- 小程序专属能力:
wx.login的code、wx.getUserProfile、wx.scanCode、wx.showShareMenu、wx.requestPayment、蓝牙 / NFC 等,完全是微信平台的私有 API,其它平台没有等价实现。 - 更原生的体验:
wx.showToast / wx.showModal / wx.showActionSheet是小程序的原生弹窗,样式、动画、无障碍、性能都优于 Kuikly 跨端组件用 Dom 拼出来的弹层;wx.setStorageSync也比 Kuikly 跨端的SharedPreferencesModule更原生(后者小程序上其实也是 localStorage 薄包装,只支持字符串 KV)。 - 系统 / 启动信息:
wx.getWindowInfo / getLaunchOptionsSync是小程序感知宿主环境、场景值、携带参数的唯一手段,没有跨端等价物。
如果让业务自己在 core-render-web/miniapp 里手写桥接,会出现:
- 业务破坏了跨端架构(要在 commonMain 以外的地方
js("wx.xxx")); - 每家业务各写一套
KRxxxModule,重复造轮子; - 参数、回调格式不统一,文档散落。
因此 Kuikly 在组件封装(参见 WX 组件接入文档)之外,再提供一层微信小程序 API 封装:在 commonMain 提供强类型 WXxxxModule DSL,业务侧只关心 Kotlin,不碰 JS / wxml。
一句话:WX 组件封装让 UI 跑得够原生,WX API 封装让业务调
wx.xxx调得够规范。
2. 方案总览:三层结构
Kuikly 提供三种使用微信小程序 API 的姿势,按业务需求选择:
┌──────────────────────────────────────────────────────────────────┐
│ ① 强类型 Module(优先使用) │
│ WXApiModule / WXStorageModule / WXUIModule / WXSystemModule │
│ WXClipboardModule / WXLocationModule / WXScanModule │
│ WXMediaModule / WXShareModule │
│ —— 类型安全、有参数提示、覆盖 80% 常用场景 │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ ② 兜底通用桥(WXRawApiModule) │
│ wxRaw.call("scanCode", params) { res -> ... } │
│ —— 无需新建 Module,一行代码调任意 wx.xxx │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ ③ 业务自行封装(可选,用于长期沉淀) │
│ MyOrderModule / MyIMModule ... 业务语义的强类型 DSL │
│ —— 仿照 WXxxxModule,四步完成(见 §4) │
└──────────────────────────────────────────────────────────────────┘
- 建议优先使用 ①;
- 如果 Kuikly 还没封装,直接用 ② 兜底;
- 当业务反复在 ② 上调用某 API,建议把它抽成 ③ 专属 Module,沉淀到业务工程。
所有
WXxxxModule仅在微信小程序平台生效;非小程序平台调用会通过onFail回包一条errMsg = "not supported: not in mini-app"。业务若需跨端运行,请通过pageData.params.optString("is_wx_mp") == "1"分流。
3. Kuikly 已经封装了哪些 API?
所有强类型 Module 位于 core/src/commonMain/kotlin/com/tencent/kuikly/core/module/wx/,对应的小程序渲染层实现在 core-render-web/miniapp/src/jsMain/kotlin/com/tencent/kuikly/core/render/web/runtime/miniapp/expand/module/wx/。
3.1 模块清单
| Module | 覆盖 wx API | 典型场景 |
|---|---|---|
WXApiModule | wx.login / checkSession / getUserProfile / getUserInfo / getAccountInfoSync | 登录、获取用户信息 |
WXStorageModule | setStorage / getStorage / removeStorage / clearStorage(含 Sync) / getStorageInfoSync | 本地持久化 |
WXUIModule | showToast / hideToast / showLoading / hideLoading / showModal / showActionSheet | 原生交互弹窗 |
WXSystemModule | getWindowInfo / getDeviceInfo / getAppBaseInfo / getSystemInfo(Sync) / getLaunchOptionsSync / getEnterOptionsSync | 系统信息 / 启动参数 |
WXClipboardModule | setClipboardData / getClipboardData | 剪贴板 |
WXLocationModule | getLocation / chooseLocation / openLocation | 定位 |
WXScanModule | scanCode | 扫码 |
WXMediaModule | chooseImage / chooseMedia / previewImage / saveImageToPhotosAlbum | 图片 / 多媒体 |
WXShareModule | showShareMenu / hideShareMenu / updateShareMenu | 分享菜单 |
WXRawApiModule | 任意 wx.xxx / wx.xxxSync | 兜底桥 |
3.2 前置:引入 core-wx 依赖并在使用页面注册 Module
从 1.9.22 / 2.0.21 / 2.1.21 版本起,所有 WX Module 封装放在独立模块 core-wx 里。core-wx 的 target 矩阵与 :core 对齐(android / iOS / macOS / js(IR) 全平台产物),所以业务可以在 commonMain 里条件使用——无论 WXButton {} 组件 还是 registerWXModules() 都可以直接从 commonMain import 进来;在非小程序平台运行时,WX 组件会自动降级为普通 view,registerWXModules() 则是 no-op,不会 crash。
🎯 不需要 WX 能力的业务无需任何改动——
:core本身完全不依赖:core-wx,只有业务主动implementation(project(":core-wx"))/implementation("com.tencent.kuikly-open:core-wx:...")才会把这些类拉进来,因此对不用 WX 的项目来说产物零膨胀。
接入步骤:
步骤 1:gradle 依赖(放到 commonMain 即可跨端使用)
// 业务工程的 commonMain(推荐)——跨平台页面里也能用 WXButton / registerWXModules
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.tencent.kuikly-open:core-wx:<your-core-version>")
}
}
}
}
如果业务只在 jsMain 里用到 WX(例如页面只针对小程序平台专门编写),也完全可以只挂到 jsMain,进一步降低产物体积。
ℹ️ 只有
1.9.22/2.0.21/2.1.21这三个 Kotlin 版本会发布core-wx。老版本(1.3.10 ~ 1.8.21)不发布core-wx,业务若需要在老 Kotlin 版本下使用 WX 封装,请将 Kotlin 升级到 1.9.22 及以上。
步骤 2:在需要用到 WX Module 的页面里一行代码注册
core-wx 提供一个 Pager 扩展方法,在实际用到 WX Module 的页面的 createExternalModules() 里调用一次,即可把 10 个 WX Module 全部注册到当前 Pager:
import com.tencent.kuikly.core.module.Module
import com.tencent.kuikly.core.wx.registerWXModules
@Page("WXApiExamplePage")
internal class WXApiExamplePage : BasePager() {
override fun createExternalModules(): Map<String, Module>? {
registerWXModules() // ← 一行完成 WX Module 注册
return super.createExternalModules()
}
// ...业务代码
}
该扩展方法内置了平台判定(pageData.params.optString("is_miniprogram") == "1"),仅在小程序平台注册,其它平台调用为空操作。所以即便页面是跨端共享的(放在 commonMain 里),在 Android/iOS 运行时 registerWXModules() 也不会注册任何东西。
为什么不放到
BasePager统一注册?只有部分 Pager 真正会用到 WX Module,按需在使用页面里注册更轻量、更直观;而且BasePager位于基础设施层,不应该强耦合可选的 WX 能力。如果你只用到其中几个 Module,也可以不调用
registerWXModules(),而是按照原始方式在createExternalModules中用put(MODULE_NAME, XxxModule())手动只注册需要的几个。
如果业务未注册 Module 就使用,会报错:
acquireModule 失败:KRWXxxxModule 未注册,请在重写Pager.createExternalModules方法时机中添加注册(调用Pager.registerModule方法注册)
⚠️
Pager.acquireModule本身是跨平台接口,但这些 WX Module 只在微信小程序平台有真正的实现;在其它平台上,调用会走 fail 兑底(回调参数里会带errMsg),业务侧可按需判断pageData.params.optString("is_miniprogram") == "1"再决定是否调用。
3.3 如何使用:强类型 Module
所有 Module 都通过 acquireModule<T>(T.MODULE_NAME) 拿到单例实例,然后直接调用方法。统一的回调约定:
onSuccess: (JSONObject?) -> Unit—— 调用成功,参数是 wx API 回调的res;onFail: (JSONObject?) -> Unit—— 调用失败,参数是 wx API 回调的err(含errMsg)。
示例 1:登录 + 缓存
import com.tencent.kuikly.core.wx.module.WXApiModule
import com.tencent.kuikly.core.wx.module.WXStorageModule
import com.tencent.kuikly.core.wx.module.WXUIModule
// 登录
acquireModule<WXApiModule>(WXApiModule.MODULE_NAME).login(
onSuccess = { res ->
val code = res?.optString("code")
// 用 code 去业务后端换 openid + session_key
// ...
// 存本地
acquireModule<WXStorageModule>(WXStorageModule.MODULE_NAME)
.setStorageSync("login_code", code ?: "")
},
onFail = { err ->
acquireModule<WXUIModule>(WXUIModule.MODULE_NAME)
.showToast(title = "登录失败", icon = "error")
}
)
示例 2:原生 Toast / Modal
val ui = acquireModule<WXUIModule>(WXUIModule.MODULE_NAME)
ui.showToast(title = "操作成功", icon = "success", duration = 1500)
ui.showModal(
title = "提示",
content = "确认删除这条数据吗?",
confirmText = "删除",
cancelText = "取消",
onSuccess = { res ->
if (res?.optBoolean("confirm") == true) {
// 用户点了确定
}
}
)
示例 3:扫码 / 定位
acquireModule<WXScanModule>(WXScanModule.MODULE_NAME).scanCode(
onSuccess = { res -> KLog.i(TAG, "scan=${res?.optString("result")}") },
onFail = { err -> KLog.e(TAG, "scan fail: $err") }
)
acquireModule<WXLocationModule>(WXLocationModule.MODULE_NAME).getLocation(
type = "gcj02",
isHighAccuracy = true,
onSuccess = { res ->
val lat = res?.optString("latitude")
val lng = res?.optString("longitude")
}
)
3.4 如何使用:兜底桥 WXRawApiModule
当 Kuikly 尚未封装某个 wx API 时,不要急着新增 Module,先用兜底桥 WXRawApiModule 顶住。
import com.tencent.kuikly.core.wx.module.WXRawApiModule
import com.tencent.kuikly.core.nvi.serialization.json.JSONObject
val raw = acquireModule<WXRawApiModule>(WXRawApiModule.MODULE_NAME)
// 异步调用:raw.call(apiName, args, onSuccess, onFail)
raw.call(
apiName = "vibrateShort",
args = JSONObject().apply { put("type", "heavy") },
onSuccess = { /* 震动成功 */ },
onFail = { err -> KLog.e(TAG, "vibrate fail: $err") }
)
// 同步调用:raw.callSync(apiName, args) -> JSONObject?
val battery = raw.callSync("getBatteryInfoSync")
val level = battery?.optString("level")
约束:
- 参数对象里不要手动写
success / fail / complete——兜底桥会自动注入回调; - 参数仅支持 JSON 可序列化类型(string / number / boolean / array / JSONObject);
- 没有编译期类型校验,请自查 微信官方文档 填写
apiName和args。
3.5 完整 Demo 在哪里?
仓库已提供可运行的示例:
- 强类型 Module Demo:
demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/demo/wx/WXApiExamplePage.kt- 按 Module 分 section,覆盖 9 个 WX*Module 的典型方法;
- 兜底桥 Demo:
demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/demo/wx/WXRawApiExamplePage.kt- 展示
call/callSync的典型用法;
- 展示
- 入口:
ExampleIndexPage的 WX API Demo 与 WX Raw API Demo,仅在小程序平台显示。
在 微信开发者工具 打开 miniApp/dist,即可在入口列表中看到上述两个 Demo。
4. 如果 Kuikly 没有封装我需要的 API,怎么办?
4.1 优先方案:直接用兜底桥
先评估是否能用 WXRawApiModule.call(...) / .callSync(...) 满足业务。大多数一次性 / 低频 API 不需要自己封装。
4.2 沉淀成业务 Module(四步)
只有当业务反复在兜底桥上调用某个(或某组)API 时,才建议抽成业务自有的 MyXxxModule。
标准做法与 WX*Module 完全对齐:
┌─────────────────────────────────────────────────────────────────────┐
│ ① 业务 DSL 层(core/commonMain 或业务工程 commonMain) │
│ class MyXxxModule : Module() { │
│ fun doSomething(onSuccess: CallbackFn?, onFail: CallbackFn?) │
│ override fun moduleName() = "KRMyXxxModule" │
│ } │
└─────────────────────────────────┬───────────────────────────────────┘
│ toNative(method, {args:...}, cb)
┌─────────────────────────────────▼───────────────────────────────────┐
│ ② 渲染层(core-render-web/miniapp/jsMain 或业务工程 jsMain) │
│ class KRMyXxxModule : KuiklyRenderBaseModule() { │
│ override fun call(method, params, callback): Any? { │
│ val args = params.toJSONObjectSafely().optJSONObject(...) │
│ // 调 wx.xxx 或本地逻辑 │
│ } │
│ } │
└─────────────────────────────────┬───────────────────────────────────┘
│ moduleExport(name) { KRMyXxxModule() }
┌─────────────────────────────────▼───────────────────────────────────┐
│ ③ Delegator 注册 │
│ KuiklyRenderViewDelegator.registerModule() 增加一行 │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ ④(可选)业务 Demo + 小程序 pages/xxx 目录 + app.json pages 列表 │
└──────────────────────────────────────────────────────────────────────┘
其中 ②③ 可以直接复用 Kuikly 提供的 KRWXApiBridge 工具,大多数异步 API 只需要 1~2 行:
class KRMyXxxModule : KuiklyRenderBaseModule() {
override fun call(method: String, params: String?, callback: KuiklyRenderCallback?): Any? {
val args = params.toJSONObjectSafely().optJSONObject("args")
return when (method) {
// 直接透传到 wx.login / wx.scanCode 等微信 API
"xxx", "yyy" -> {
KRWXApiBridge.invokeAsync(method, args, callback)
null
}
else -> super.call(method, params, callback)
}
}
companion object { const val MODULE_NAME = "KRMyXxxModule" }
}
KRWXApiBridge.invokeAsync/.invokeSync会自动处理:
- 把
rawArgs注入success / fail;- 非小程序平台降级(
onFail回包errMsg = "not supported");- 异常捕获与 JSON 序列化。
4.3 参考源码
- 参考实现:
WXApiModule+KRWXApiModule是最标准的示例。- DSL:
core/src/commonMain/kotlin/com/tencent/kuikly/core/module/wx/WXApiModule.kt - 渲染:
core-render-web/miniapp/src/jsMain/kotlin/com/tencent/kuikly/core/render/web/runtime/miniapp/expand/module/wx/KRWXApiModule.kt - 注册:
core-render-web/miniapp/src/jsMain/kotlin/com/tencent/kuikly/core/render/web/runtime/miniapp/expand/KuiklyRenderViewDelegator.kt中的registerModule
- DSL:
- 同步方法样例:
WXSystemModule.getWindowInfoSync/KRWXSystemModule—— 演示直接返回值的调用; - 存储类样例:
WXStorageModule/KRWXStorageModule—— 演示同步 / 异步混合; - 兜底桥内部机制:
core-render-web/miniapp/src/jsMain/kotlin/com/tencent/kuikly/core/render/web/runtime/miniapp/expand/module/wx/KRWXApiBridge.kt是所有 Module 的共享工具,直接读源码就能完全理解原理。
4.4 常见坑
| 现象 | 原因 | 解决 |
|---|---|---|
| 调用直接崩溃 / 报错 "wx.xxx is not a function" | apiName 拼错 / 当前基础库版本不支持 | 对照微信开发者工具基础库版本 / 查官方文档 |
永远走 fail 分支,errMsg = "not supported" | 不是小程序平台(H5、原生)调到了 WX*Module | 用 pageData.params.optString("is_wx_mp") == "1" 分流 |
| 参数传进去不生效 | 对象参数没有完整给定字段(wx 有默认值要求) | 查微信官方文档,补齐必填字段 |
| success / fail 被你自己覆盖 | 在 args 中手写了 success / fail | 不要写;兜底桥会自动注入 |
| 二进制 / 复杂数据调不通 | 兜底桥只支持 JSON 可序列化数据 | 新封装一个 KRMyXxxModule,自行处理 ArrayBuffer |
5. 使用 AI 辅助生成新 API 封装
封装一个新的 wx API Module,绝大部分情况就是按模板填空,非常适合交给大模型。推荐 prompt:
5.1 Prompt 举例(生成业务专属 Module)
在 Kuikly 的 core/src/commonMain/kotlin/com/tencent/kuikly/core/module/wx/ 目录, 已经封装了 WXApiModule / WXStorageModule / WXUIModule / WXSystemModule / WXScanModule 等一批微信小程序 API Module。 它们的实现模式:业务层 WXxxxModule : Module() + 渲染层 KRWXxxxModule : KuiklyRenderBaseModule(), 渲染层统一通过 KRWXApiBridge.invokeAsync(apiName, args, callback) 透传 wx.xxx 调用。
现在请你参照上述结构,新增对微信小程序蓝牙系列 API(wx.openBluetoothAdapter / wx.startBluetoothDevicesDiscovery / wx.onBluetoothDeviceFound / wx.createBLEConnection 等)的封装,要求:
- 业务 DSL 类名
WXBluetoothModule,放到同目录; - 渲染层类名
KRWXBluetoothModule,放到core-render-web/miniapp/.../expand/module/wx/下; MODULE_NAME使用常量"KRWXBluetoothModule",并在WXModuleConst中补一个BLUETOOTH常量;- 在
KuiklyRenderViewDelegator.registerModule()中加一行注册; - 对
onXxx事件(例如onBluetoothDeviceFound)需要保持回调 keepAlive,请在业务 DSL 层用toNative(keepCallbackAlive = true, ...)调用; - 参考
WXApiModule.kt的注释风格,中文注释说明入参、回调字段; - 最后在
WXApiExamplePage.kt添加一个 section 演示蓝牙扫描的调用。
5.2 Prompt 举例(无需新建 Module,教 AI 直接用兜底桥)
我需要在 Kuikly 的 commonMain 调用微信小程序 wx.makePhoneCall,它不是很常用,不需要新增 Module。 请直接使用 Kuikly 已提供的 WXRawApiModule.call(...),写一段示例代码,支持电话号码校验失败走 onFail。
通过这种"方案优先级 + 四步模板 + 兜底桥 + 参考源码"的组合,业务无论"增量扩展"还是"快速接入"都有明确的路径。
6. 小结
- 默认优先用 Kuikly 已封装的
WXxxxModule(9 个常用 Module); - 没封装的 直接用
WXRawApiModule兜底桥,一行代码调任意wx.xxx; - 反复用到的 再沉淀为业务专属
MyXxxModule,配合KRWXApiBridge工具,四步落地; - 仅在微信小程序平台生效,跨端业务请用
pageData.params.optString("is_wx_mp")分流; - 有 AI 加持:按 §5 的 prompt 模板,可以把"封装新 API"变成一句话的活儿。