Taro小程序兼容H5 API、组件兼容问题
总结在开发过程中的实际情况中所遇到的Taro小程序兼容H5的api、组件以及相关问题
文章目录
- 一、H5Taro.getCurrentPages 返回空值。
- 二、Taro.getCurrentPages() 提取并获取当前页面的路由参数信息。
- 三、以 key 表示组件传递参数的入参属性名。
- 四、在处理网络请求时出现异常情况:分别进行相应的错误处理。
- 五、支持多终端自定义的 custom-tab-bar 组件。
- 六、“ Swipe 动作组件 AtSwipeAction 不支持与h5框架兼容。
- 七、“ 输入封装为文件时 ref 获取节点会导致 click 操作失效。
- 八、“ 在H5开发环境中无法正常使用的 AtModal 组件样式。
- 九、“ 使用 AtIcon 组件配置组件大小。
总结:
一、H5Taro.getCurrentPages 获取为空
当正常添加页面栈时,在未进行手动刷新的情况下,“h5能够正确地取得”对应的页码列表是有效的。然而,在用户主动触发刷新操作时,“Taro.getCurrentPages”则会返回空数组。通过调用“Taro.nextTick”可以获得当前处于显示状态的网页列表信息;而用于查看历史页码的功能则无法实现
componentDidMount(){
Taro.nextTick(() => {
const pages = Taro.getCurrentPages()
})
}
二、Taro.getCurrentPages() 获取当前页面路由参数
当在 weapp 和 H5 中通过调用 Taro.getCurrentPages() 获取页面路由参数时会出现不一致的情况。具体来说,在不同端使用 Taro.getCurrentPages() 可以获取到一些特定属性。
| path | route | $taroPath | |
|---|---|---|---|
| weapp | × | √ | √ |
| H5 | √ | × | √ |
获取路由参数
Taro.getCurrentPages()[-1]?.$taroPath?.split("?")[0]
三、用 key 作为组件传参的入参属性名
在开发过程中发现了这样一个情况:一些开发人员为了方便起见,在遍历操作中采用了某个组件,并将 key 参数传递给该组件作为 key 参数使用。然而,在小程序环境下这种做法不成问题(不成问题),但在 H5 环境中却出现了错误(错误)。具体原因在于 key 是一个关键字(关键),不能作为 props 参数(参数)接收(接收),因此在组件内部根本无法获取到 key 的实际值(实际值),其值被设为 undefined(无效)。建议大家可以在组件外部设置 key 作为标识符即可(即可),无需对内部进行任何处理(处理)。
{{
list.map(item=><my-item key={item.id} />)
}}
四、Taro.request 网络请求异常处理中,weapp 和 h5 异常处理
我们在项目中规定了 接口的状态码 Status Code 401 作为 token 过期时使用的编码,并且 Taro.request 包裹的响应拦截也需根据修改情况进行相应调整,在不同的端点差异如下:
| weapp | h5 | |
|---|---|---|
| 接口状态码 | 401 | 401 |
| 触发的回调函数 | success | fail |
| 响应数据状态码字段 | statusCode | status |
五、Taro 多端自定义 custom-tab-bar 组件
在Taro中,默认版本无法自定义custom-tab-bar功能,请参考官方文档详细了解相关设置。
- 将原有代码中的模块重新组织为独立文件:将原有的
custom-tab-bar.py$ - 修改模块内的默认行为:令该组件返回值为无。
- 在页面中引入新组件并作为组件进行管理:在该页面中引入新的模块文件命名为 components/customTabBar.py 并将其作为标准组件导入使用。
- 删除官方提供的自定义 tab-bar.py 文件。
.taro-tabbar__tabbar,
tar-bar {
display: none !important;
}
hint:在 CustomTabBar 组件中, 可以通过 Taro.getCurrentInstance() 获取当前页面的路由信息, 并将其与 app.config.js 中定义好的 tabBar.list 对应起来分析, 从而判断并确定哪些 tab 页需要进行高亮显示。
一些机智的同学可能会思考:一旦改为自定义组件后将 custom-tab-bar 设为空组件,则无需配置 tabBar.list 就行了;为什么要手动隐藏默认的 custom-tab-bar 呢?
确实能够实现自定义 tab-bar 的作用。但是存在一个问题,在切换页面的过程中,上一次的页面无法保持其状态而无法保持上一次的状态,并且 tab-bar 页都需要重新加载并重新渲染内容。如果操作速度能够得到提升的话,在这种情况下就不会出现闪烁现象
六、AtSwipeAction 组件不兼容 h5
AtSwipeAction 组件借助 MovableArea 组件来实现相应功能。然而在 h5 环境下,MovableArea 组件并不被支持。如此一来,就只能手动编写一个具备滑动操作功能的组件了。相关代码如下:
index.jsx
import { useRef, useEffect, useState, useCallback, useMemo } from "react";
import Taro from "@tarojs/taro";
import { View } from "@tarojs/components";
import "./index.less";
/*** * 参数参考 taro ui SwipeAction
*/
const SwipeAction = (props) => {
const { onOpened = () => { }, onClosed = () => { }, onClick = () => { }, options = [], autoClose = false, disabled, isOpened } = props;
const [translateX, setTranslateX] = useState(0); // 偏移的 X 距离
const [startX, setStartX] = useState(0); // 鼠标移入的 X 坐标
const [isSwiping, setIsSwiping] = useState(false); // 是否触摸滑动
const actionWidthRef = useRef(0); // 操作节点的 width
const alreadyXRef = useRef(0); // 已经滑动的距离
useEffect(() => {
Taro.nextTick(() => {
Taro.createSelectorQuery()
.select(".swipe-action__actions")
.boundingClientRect((rect) => {
actionWidthRef.current = rect.width;
})
.exec();
});
}, []);
useEffect(() => {
reset(isOpened);
}, [isOpened]);
// 操作后重置关闭
const reset = useCallback((is_opened) => {
if (is_opened) {
setTranslateX(-actionWidthRef.current);
alreadyXRef.current = -actionWidthRef.current;
_onOpened()
} else {
setTranslateX(0);
alreadyXRef.current = 0;
_onClosed()
}
}, [props]);
function _onOpened() {
Taro.nextTick(() => {
onOpened();
});
}
function _onClosed() {
Taro.nextTick(() => {
onClosed();
});
}
// 滑动开始
const handleTouchStart = useCallback((e) => {
if (disabled) return false; // 禁止滑动
setStartX(e.touches[0].clientX);
setIsSwiping(true);
}, [disabled]);
// 滑动结束
const handleTouchEnd = useCallback((e) => {
setIsSwiping(false);
if (translateX < -(actionWidthRef.current / 2)) {
setTranslateX(-actionWidthRef.current);
alreadyXRef.current = -actionWidthRef.current;
_onOpened()
} else {
reset(false);
_onClosed()
}
}, [translateX, actionWidthRef.current, reset]);
// 滑动中
const handleTouchMove = useCallback((e) => {
if (!isSwiping) return;
const cX = e.touches[0].clientX;
const diffX = cX - startX + alreadyXRef.current;
if (diffX <= 0.00001) {
setTranslateX(diffX);
}
}, [isSwiping, startX, alreadyXRef.current]);
const handleClick = useCallback((item, inx, e) => {
// 添加 options 可配置 click
const optionsClick = typeof item.onClick == "function" ? item.onClick : ()=>{}
onClick(item, inx, e);
optionsClick(item, inx, e);
if (autoClose) reset(false);
}, [props, reset]);
const actions = useMemo(() => {
return options && options.length ? (
<View className='swipe-action__actions'>
{options.map(({ text, className, ...residue }, inx) => (
<View
{...residue}
key={`swipe-action__actions_item_${inx}`}
onClick={(e) => handleClick({ text, className, ...residue }, inx, e)}
className={`swipe-action__actions_btn ${className}`}
>
{text}
</View>
))}
</View>
) : null;
}, [options, handleClick]);
return (
<View className={`SwipeActionBx ${props.className}`}>
<View
className='swipe-action'
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
style={{ transform: `translateX(${translateX}px)` }}
>
<View className='swipe-action__content'>{props.children}</View>
</View>
{actions}
</View>
);
};
export default SwipeAction;
index.less
.SwipeActionBx {
width: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
.swipe-action {
display: flex;
overflow: hidden;
position: relative;
z-index: 10;
transition: transform 0.3s ease;
}
.swipe-action__content {
flex: 1;
width: 100%;
background-color: #fff;
}
.swipe-action__actions {
display: flex;
align-items: center;
color: #fff;
position: absolute;
right: 0;
top: 0;
z-index: 5;
height: 100%;
flex: 1;
.swipe-action__actions_btn {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 16px;
box-sizing: border-box;
}
}
}
在开发过程中发现了一项关键点:taro ui swipeAction 组件中的所有属性都被系统自动生成,并且将其打包成为组件参数供其他部分调用。
具体而言,在微信开发者工具中遇到了一个问题:当为底部操作栏按钮设置颜色时,在父级元素上会出现按钮宽度超出预期的情况,并能观察到明显的边框效果;然而,在实际应用中发现这一现象并不明显存在。
值得注意的是,在实际应用中发现这一现象并不明显存在。
七、Input作为原生上传文件封装,ref 获取节点 click 无效
场景:默认情况下,在页面中点击其他元素节点时,默认情况下无法直接触发 Input 元素的 click 事件以实现上传功能。
问题分析:在 H5 应用场景中,默认情况下采用 Input 包裹组件的方式时,在某些情况下若需手动操作点击其他非 Input 元素节点并触发其 click 事件来调用上传功能,则可能会遇到无法正常触发的情况。
具体来说,在 CSS 问题兼容处理 所述,在 H5 场景下,默认情况下 Input 标签外层会额外包裹一层 taro-input-core 标签导致相关操作失效,请确保正确获取目标输入标签才能正常调用相关功能。
解决方案:请确保所有输入字段均正确地嵌套或绑定到对应的 HTML 输入标签上,并避免因嵌套或绑定错误导致的操作异常现象发生。
import { Component, createRef } from 'react';
import Taro from '@tarojs/taro'
export default class FileUpload extends Component {
this.fileInput = createRef()
onUploadFiles(){
if (process.env.TARO_ENV === 'h5') { //H5上传附件
const inputNode = this.fileInput.current;
inputNode.querySelector("input")?.click()
} else { //weapp 上传附件
const options = {...}
Taro.chooseMessageFile(options)
}
}
//上传文件
onInputChange(){
let { files = [] } = this.fileInput.current?.querySelector("input") //获取上传的文件
/***
数据处理...
**/
files = [] //将 input 附件清空
}
render(){
const { filePorps } = this.props
return <>
<Button onClick={()=>this.onUploadFiles()}>上传</Button>
<Input { ...filePorps } ref={this.fileInput} type='file' style={{ display: 'none' }} onChange={e => this.onInputChange(e)} > </Input>
</>
}
}
八、AtModal 组件样式不兼容 H5
遵循H5规范的AtModal组件采用多个子组件实现功能。其中每个子组件均配备默认样式文件。
- 建议使用
Taro.showModal - 自己手写一个
Modal - 修改
AtModal全局样式
.at-modal__action {
taro-button-core {
margin-top: 0;
border-radius: 0;
background-color: #fff;
border: none;
color: #333;
}
taro-button-core::after {
border: none;
}
taro-button-core:nth-last-child(1) {
color: #6190E8;
}
taro-button-core:nth-last-child(1)::after {
z-index: 30;
border-radius: 0;
border-left-width: 1px;
border-left-style: solid;
border-left-color: rgb(229, 229, 229);
}
}
.at-modal__footer::before {
z-index: 30;
border-top-width: 1px;
border-top-style: solid;
border-top-color: rgb(229, 229, 229);
}
九、使用 AtIcon 组件 size 设置
在 AtIcon 组件的 props 属性中定义了一个名为 size 的参数用于指定图标大小,在向 H5 端迁移过程中 size 的计算会出现异常导致 font size 字段为空值 此时应将 font size 值手动设置在 className 属性中
tips : 同一个图标的 value 在微信的图标和H5的图标可能会存在差异。
总结
在实际开发中遇到了一些API与组件兼容的问题。项目进展较为复杂,并且还有很多问题尚未被完全记录下来。这篇文档主要用于记录这些问题,并希望你能从中获得帮助。实在抱歉有些问题未能及时解决,请argsyous再次详细分析一下吧。
