跨平台模块复用需按平台能力切片并显式暴露差异点,通过条件编译+统一适配层解耦,禁止硬编码平台字段,组件UI与逻辑分离,且必须添加运行时平台能力校验。

业务模块必须按平台能力切片,不能直接搬代码
跨平台模块复用失败最常见的原因是把“能跑通”当成“可复用”。比如一个封装了 uni.chooseImage 的图片上传组件,在微信小程序里返回 tempFilePaths,在 App 端却可能返回 localFilePath 或完整 file:// 路径;如果组件内部直接对 res.tempFilePaths[0] 做字符串截取或拼接,App 端运行时就会报 Cannot read property '0' of undefined。这不是 bug,是平台 API 返回结构不一致导致的必然结果。
真正可沉淀的模块,得把平台差异点显式暴露出来:
- 所有涉及文件路径、系统信息、支付回调、蓝牙连接、地理位置精度等 API 的调用,必须拆成独立函数,并用
// #ifdef MP-WEIXIN或// #ifdef APP-PLUS包裹最小执行单元,而不是整个方法 - 避免在组件
data或computed中硬编码平台特有字段(如SDKVersion、model),改用统一适配层返回标准化对象 - 组件 props 设计要屏蔽底层差异:比如传入
imageMode="crop",而不是让调用方传sizeType: ['compressed']这类原生参数
用条件编译 + 统一适配层解耦平台逻辑
直接在业务组件里写一堆 #ifdef 是短期方案,长期维护会失控。推荐建一个 platform/ 目录,集中管理各端能力封装:
例如 platform/image.js:
// #ifdef MP-WEIXIN
export function chooseImage(options = {}) {
return uni.chooseImage({
count: options.count || 1,
sizeType: options.compressed ? ['compressed'] : ['original'],
sourceType: options.sourceType || ['album', 'camera']
}).then(res => ({
paths: res.tempFilePaths,
type: 'temp'
}))
}
// #endif
// #ifdef APP-PLUS
export function chooseImage(options = {}) {
return uni.chooseImage({
count: options.count || 1,
// iOS 需要加 file:// 前缀,Android 可能不需要
sourceType: options.sourceType || ['album', 'camera']
}).then(res => ({
paths: res.tempFiles.map(f => f.path),
type: 'local'
}))
}
// #endif
业务组件只 import chooseImage,不关心它内部怎么实现。后续新增抖音小程序支持,只需在 platform/image.js 里补一段 // #ifdef MP-TOUTIAO,业务层完全无感。
组件复用要靠“能力抽象”,不是“UI复制”
看到别人家的 <uni-upload></uni-upload> 好用,就直接拷进自己项目?大概率踩坑。uni-ui 的组件本身做了大量条件编译和平台判断,但它的抽象粒度是通用场景,你的业务往往有定制逻辑(比如上传前强制压缩、失败后自动重试三次、图片需带水印)。
更可持续的做法是:
- 把 UI 和逻辑分离:用
<uni-upload></uni-upload>渲染界面,但上传逻辑由你自己封装的uploadImage()处理 - 自定义组件命名带平台标识,如
<my-upload-mp></my-upload-mp>(仅微信)、<my-upload-app></my-upload-app>(仅 App),再用easycom自动注册 - 共用样式抽成
@/styles/common.scss,但平台专属样式(如 cover-view 与 view 的层级兼容)单独放@/styles/mp-weixin.scss
这样既复用了视觉层,又保住了业务逻辑的可控性。
复用模块必须带平台运行时校验
很多团队把模块打包成 npm 包共享,结果上线后某端白屏,查半天发现是某个 API 在目标平台根本不存在(比如 uni.getConnectedWifi 在 H5 不支持)。光靠编译期条件编译不够,还得加运行时兜底。
在模块入口加一层检测:
export function initUploadModule() {
if (!uni.chooseImage) {
console.warn('[upload] uni.chooseImage not available on current platform')
return Promise.reject(new Error('Platform not supported'))
}
// 其他初始化逻辑...
}
调用方在 onLoad 里先 initUploadModule().catch(...),比等到点击按钮才报错更友好。这种校验成本极低,但能提前暴露平台能力缺失问题,避免用户操作到一半崩溃。
跨平台模块真正的复杂点不在写法,而在“什么时候该拆、什么时候该合”——API 差异必须切到语句级,UI 层可以合并,但业务逻辑一旦混着写,下次加个新平台就得重写一遍。


















