迁移兼容问题
迁移兼容问题
实习过程中面对过一个复杂需求:H5仓库进行新版本迁移,还要兼容小程序。此篇文章特此记录H5和小程序的差异情况,相当于踩坑笔记吧。
一、wxSDK替代
程序会遇到三种情况:H5、小程序、小程序内嵌H5。
为此针对上述情况,需要进行判别函数的撰写。
1 | export async function isMiniProgram(): Promise<boolean> { |
这里会涉及到wxSDK使用uniapp的能力进行替代。
miniProgram.reLaunch
reLaunch的功能主要有两个:关闭所有页面、打开指定页面。
使用场景:用户登录/注销、应用重置、错误处理。
我这里遇到的场景是用户删卡后回首页,类似于用户注销。1
2
3
4
5
6
7
8
9
10
11
12
13
14const goHome = async () => {
// 小程序内嵌时,删卡直接回小程序首页(为了防止回退,直接reLaunch)
const isMini = await isMiniProgram();
if (isMini) {
weixinModule.invoke('miniProgram.reLaunch', { url: '/pages/index/index' });
return;
}
if (!IS_H5) {
return uni.reLaunch({
url: '/pages/index/index',
});
}
navigateTo({ path: P2_INDEX });
};miniProgram.getEnv
用于判断当前的运行环境, 通常在网页被调用,以确定该网页是否在微信小程序的环境中运行。此方法在上述的判断环境中进行使用。miniProgram.navigateTo
要进行页面跳转进行对应性兼容。1
2
3
4
5
6const isMini = await isMiniProgram();
if (isMini) {
...
weixinModule.invoke('miniProgram.navigateTo', { url: `${'/pages/card-remove/index'}?${stringify(query)}` });
return;
}
二、页面适配
小程序不能使用DOM、BOM用法,同时获取url query的实际时序问题,这些其实需要视情况而定,我选取几个特殊场景进行举例吧。
2.1 页面渲染
render和template都是用于定义视图层的内容和结构的方式。
但小程序不支持使用render函数来定义页面的视图层,为此需要将render函数的方式转变为template。
1 | const svgMap = { |
解决思路:H5 方面通过组件的引入来直接使用,通过组件名字的对应关系来实现。小程序无法直接使用 @Component 这种方式来定义和注册组件的。可以通过父子组件传输组件名,去加载相关组件名和对应样式(这里发现样式名与组件名有关联,直接采用写死的方式减少数据传输)。
1 | <template> |
小程序子组件
1 | <template> |
2.2 DOM操作
1 | import ModICBCOpenBill from '../mod-icbc-openbill/index.vue'; |
这段代码实现了一个Vue插件,动态创建并挂载组件实例,接着将实例的根元素(icbc.$el)插入到DOM中,这样组件就可被动态添加到页面中,最后将组件实例挂载到Vue原型上。
但遇到一个问题:但小程序无document.createElement的功能。
解决思路:直接引入并使用ModICBCOpenBill,通过在组件实例赋值方式实现。
1 | <template> |
在 setup 中将组件的实例赋值给一个名为 $icbc 的引用,接着在组件挂载到 DOM 之前,将实例赋值给 Vue 的原型属性 icbc。
1 | import { ref, Ref, computed, SetupContext, nextTick, onBeforeMount } from '@vue/composition-api'; |
2.3 时序问题
在小程序中,getCurrentPages方法用于获取当前页面栈(页面栈是一个数组,包含当前小程序中所有已打开的页面实例),通常情况下,我们可以通过getCurrentPages获取页面栈并进行相应的操作。然而,通过全局函数(如navigateTo、navigateBack)进行页面跳转,页面栈更新不会立即完成(异步)。
为解决上述问题,常见的解决方案有两种:一种是使用async/await;另一种是使用回调函数。我选择的是第一种,在页面跳转后,使用await nextTick()等待页面栈更新完成,再调用getCurrentPages获取最新的页面栈信息。
1 | // 页面初始化 |
但遇到了新的问题,页面初始化原本再setup中调用,而实际DOM操作在beforeMount生命周期钩子中,对此需要进行调整。
1 | onBeforeMount(() => { |
三、组件适配
这里以日期选择器为例,迁移到小程序中,会遇到日期选择器样式失效、选择日期有误(日期天数有误)等各种问题。
1 | <template> |
这里主要涉及到日期选择器逻辑的适配,主要通过触摸事件来控制选择器的滚动和选择。
$$start方法初始化触摸的开始位置和时间;
$$move方法处理触摸移动事件,计算移动的距离diff,并更新平移值。
- 更新触摸终点,计算滑动距离;
- 设置过渡时间为0,记录平移值(移动距离+当前平移值);
- 更新开始时间;
- 记录触摸点(当前时间、触摸点Y坐标),同时限制记录点数量;
$$end方法,处理触摸结束事件,计算最终的平移值并选择触发事件。
- 若无触摸开始事件,直接返回;
- 获取结束时间;
- 通过uni.createSelectorQuery()获取选择器位置信息,计算出选择器中心的Y坐标;
- 计算触摸时间;
- 松开手距离上一次移动的时间超过100ms,认为停止,不执行惯性滑动;
- 触摸时间小于100ms,根据触摸点的起始和结束两个位置,通过时间差和距离差计算出速度v;
- v乘以惯性滑动事件(如150ms),计算出应该滑动的距离;
- 重置触摸开始位置。
通过这些步骤,处理触摸结束事件,计算最终的平移值,确保选择器能正确对齐到最近的选项,同时处理惯性滑动的效果。
$$stop方法,和end方法结合,确保选择器能正确对齐到最近的选项。
更新平移值;
移动到最近的一行,将平移值除以行高,四舍五入再乘以行高;
1
this.translate = Math.round(this.translate / this.rowHeight) * this.rowHeight;
获取平移的最大值和最小值,限制平移值不会超出边界;
设置过渡时间,更新平移值;
四、api与属性
H5的api与属性在小程序中存在不适配情况,以常见的例子进行举例:
4.1 storage
获取本地存储中的数据:
1 | //H5 |
将数据存储到本地存储中:
1 | //H5 |
还包括清空等各种方法,大同小异。
4.2 window
window对象提供了诸多属性和方法,但小程序没有,需要进行处理。
requestAnimationFrame
浏览器的requestAnimationFrame在小程序是没有的,通过在MDN上阅读(https://developer.mozilla.org/zh-CN/docs/Web/API/window/requestAnimationFrame),用setTimeout实现。
1
2
3
4
5requestAnimationFrame(callback) {
return setTimeout(() => {
callback(Date.now());
}, 1000 / 60);
}视图属性
视图窗口的大小、位置、导航等,小程序没有,参考 uniapp 官网,可以实现对应的需求。
以 window.innerHeight 为例,参考(https://doc.dcloud.net.cn/uni-app-x/api/get-system-info.html#getsysteminfo)。1
curHeight= IS_H5 ? window.innerHeight : uni.getSystemInfoSync().windowHeight;
4.3 appid
在进行跳转的过程中,H5只需要path跳转路径即可,但小程序还需传入appid,方可进行正常跳转。
五、样式问题
样式问题一般都是一些小问题,遇到问题,大家也可以在网上进行搜索解决,我随便举两个问题吧。
a标签
a标签在小程序上会变为navigator,有以下几种处理方法:
- 使用条件编译;
- 自定义组件;
- 使用uni.navigateTo方法;
div标签
这边建议div标签全部换为view标签。
标签设置宽高等属性
直接在标签上(如img)设置宽高,会失效,推荐使用css样式表来进行样式管理。
图标
H5图标使用svg标签内嵌font-size来控制大小,但小程序不支持svg标签渲染,可以使用ui组件库或者将图标转为base64,然后人工调整大小。举个例子吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<template>
// 原先H5
<svg width="87px" height="63px" class="weui-inline-block weui-icon-svg"
// 兼容处理
<view>
<svg v-if="isH5" width="87px" height="63px" class="weui-inline-block weui-icon-svg" :style="{'font-size':pxToRem(size) }"viewBox="0 0 87 63" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>路径</title>
<img v-if="!isH5" class="weui-inline-block weui-icon-svg tick-mp" src="data:image/png;base64,iVBORw0KGgoAAAANSUh..."/>
</view>
</template>
<style lang="less" scoped>
.tick-mp {
height: xx rem;
width: xx rem;
}
</style>伪元素
小程序对伪元素的支持度不够友好,对常见的伪元素(如::before)提供支持,但较新或较复杂的css特性(如::first-line)可能不支持,使用时需查阅最新文档/实际试验。

