前言
接到一个需求,寄生在APP中的h5开发的IM模块,需要实现微信小程序版本的同等功能。首先想到的是借用跨端框架,以最低成本实现现有代码往跨端框架中迁移,借用跨端框架的多端编译能力,实现一套代码同时编译到小程序和H5。
技术方案选型
目前,各大框架趋于稳定,RN、flutter、Taro、uni-app 也是目前最活跃的跨端工具,这里选择了uni-app,原因有以下几点:1)uni-app站队vue,需要迁移的代码也是vue技术栈;2)uni-app至2018开源至今,已拥900万开发者(官方文档数据),且归属在国内的移动开发领域有着广泛的影响力的于DCloud团队,生态丰富,开发文档友好;3)uni-app提供的条件编译能力能较好的解决不同端的环境差异问题,实现了APP、各类小程序、快应用的跨端效果,一套代码编到十几个平台,为以后业务扩展到其他平台也提供了支持。
技术栈:UniApp + Vite5 + Vue3 + Typescript + pinia
gitHub:github.com/simpleKeepe…
效果展示
以下视频非实际项目效果图,仅是调用期间对聊天主要功能的实践,但其主要能力(收发消息、下拉查看更多、消息回滚定位、发送图片、视频以及播放等等)都已正常实现,且无性能问题。
踩过的几个坑
- 下拉列表应用内置组件scroll-view,代码如下:
<scroll-view
:scroll-into-view="uniItemId"
scroll-y="true"
refresher-enabled
class="scroll-Y"
ref="myScrollView"
:refresher-threshold="100"
:scroll-with-animation="true"
:refresher-triggered="triggered"
@refresherrefresh="refresherpulling"
@refresherpulling="onPulling"
@refresherabort="onAbort"
>
// 中间为会话区各种格式的消息
</scroll-view>
// 自定义下拉刷新控件被下拉
function onPulling(e) {
console.log('onpulling', e);
if (e.detail.deltaY < 0) return; // 防止上滑页面也触发下拉
triggered.value = true;
}
// 自定义下拉刷新被复位
function onRestore() {
triggered.value = 'restore'; // 需要重置
console.log('onRestore');
}
// 自定义下拉刷新被中止
function onAbort() {
console.error('onAbort');
}
function refresherpulling() {
if (_freshing) return;
_freshing = true;
setTimeout(() => {
triggered.value = false;
_freshing = false;
}, 500);
const Len = 5;
for (let i = Len; i > 0; i--) {
chatList.value.unshift({
text: '第' + (chatList.value.length + Len - i) + '条下拉回复数据',
avatarImg: '../../static/imgs/me-default.png',
msgType: 1,
msgSender: 1,
});
chatList.value.unshift({
text: '第' + (chatList.value.length + Len - i) + '条下拉发送数据',
avatarImg: '../../static/imgs/me-visitor.png',
msgType: 1,
msgSender: 0,
});
}
// 定位前,先把原锚点清除,再重新设置锚点,否则第一次定位后,滚动滚动条,无法定位到锚点位置
uniItemId.value = null;
setTimeout(() => {
uniItemId.value = `chatItem_${2 * Len - 2}`;
}, 500);
}
- 应用scroll-into-view属性进行描点定位时,绑定的响应式数据在变更前先把原锚点清除,再重新设置锚点,否则第一次定位后,滚动滚动条,无法定位到锚点位置;
- :refresher-triggered="triggered"中,这个属性要随着下拉树下的过程变更状态,否则会导致下拉的loading状态一直不消失;
- :refresher-triggered="triggered"中triggered的初始值要设置为false,const triggered = ref<Boolean | string>(false);否则在小程序中初始化页面就会触发下拉刷新。
- 发送图片消息时,小程序和H5中存在差异,需要使用条件编译
<view class="button-con" @click="chooseImage">
<uni-icons type="image" size="30"></uni-icons>
<text class="text">相片</text></view
>
// 发送图片、视频
chooseImage() {
const f = this;
// #ifdef H5
uni.chooseImage({
count: 1, //默认9
sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: function (res) {
console.log(JSON.stringify(res.tempFilePaths));
f.$emit('sendMeidaMsg', res.tempFilePaths[0], 2);
},
});
// #endif
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
maxDuration: 30,
camera: 'back',
success(res) {
console.log(res.tempFiles);
f.$emit('sendMeidaMsg', res.tempFiles[0].tempFilePath, 2);
},
});
// #endif
},
- 样式在不同平台的差异性 例如当设置了box-sizing: border-box时,在小程序中设置宽度依旧要考虑padding的距离,h5中某些父元素可以根据子元素自动把高度撑开,但是在小程序中需要设置具体高度等等。毕竟小程序独立的运行时环境和浏览器的运行时环境有差异,这些不兼容也在所难免。
/* #ifdef MP-ALIPAY || MP-WEIXIN */
.popup-content {
width: 360px;
height: 106px;
}
/* #endif */
::v-deep .uni-row {
position: absolute !important;
bottom: 10px;
width: 100%;
/* #ifdef MP-WEIXIN */
width: calc(100% - 28rpx);
/* #endif */
background: #f6f6f6;
padding: 8rpx 16rpx 8rpx 12rpx;
display: flex;
align-items: center;
z-index: 9;
.img-con {
.img {
width: 26rpx;
height: 26rpx;
}
}
}
结束语
在迁移IM模块原始代码到uni-app框架的过程中,确实遇到了一些问题,但总体来说,解决的并不算艰难。编译生成的小程序包在微信开发者工具中的调试也能正常定位,加上得益于官方文档的友好,广大开发者经验输出,原来的H5代码迁移到uni-app,并编译成H5和小程序2套代理,总体算是顺利,相比于重新开发一套小程序代码,大大的节省了开发和维护成本。