目的

在 Taro Js 项目中使用了 Swiper 滑块容器组件,在微信小程序上查看时,发现高度总是固定的 150px。这很难受,我希望它能够自动适应内容的高度。

固定高度 自动适应
1729162665998 399d06fe 3ebe 4662 91ad 9f9265f658d9 1729162671938 3dd594a4 5719 4683 bbfb 73579b45ecc5

失败的尝试

尝试用了各种 CSS 最终可以在网页端成功自适应,但是在微信小程序中就是不行。然后又按照文档 https://taro-docs.jd.com/docs/components/viewContainer/swiper 中提到的 adjustHeight进行了设置:

1729162785343 6b3c37f2 a8cd 4251 b3e4 4306e0125ebc

发现在微信小程序上仍然无效。继续看了一下文档,发现原来这个属性只有支付宝小程序支持!

谁的锅?

微信小程序。直觉上是微信团队在实现小程序时,做得比较粗糙,要求使用者传入一个固定的高度值,简单直接。因为支付宝在实现小程序时可以做到高度自适应,所以技术上应该是可行的。

尽管如此,Taro Js 难道不应该包一层吗?不仅仅可以打包到不同的平台,而是真正的统一开发体验。对于同样的需求,只需要一个标准的写法就好,而不是为不同的平台写不同的代码。

自己动手,虽丑但能用

目前自己封装了一个丑的实现,暂时比较满意,效果可以查看 https://taro.jefftian.dev/ 或者本文开头的效果对比图。

首先,因为支付宝小程序内置自适应,所以会在支付宝环境中直接返回原生的 Swiper 组件。然后,在网页环境中,使用纯 CSS 实现自适应。最后,检测到是微信小程序环境的话,还需要增加一个 JS 代码动态计算子元素的高度,并以固定的像素值设置到 Swiper 上。

使用方法

命名为 HardwaySwiper,使用上和原来的 Swiper 使用方式一致。比如:

plain export const Banner = () => <HardwaySwiper indicatorColor=#999 indicatorActiveColor=#333 circular indicatorDots autoplay full displayMultipleItems={1} adjustHeight=highest style={{ height: 300px }}

<SwiperItem className=banner-item onClick={() => Taro.navigateTo({ url: /pages/subpages/react-view/webview?src=${encodeURIComponent(https://mp.weixin.qq.com/s/nwUTbYfi7SMh2X5Lcn-YpA)} })}

<Image
  src={https://uniheart.pa-ca.me/proxy?url=${encodeURIComponent(https://raw.githubusercontent.com/Jeff-Tian/wechat-oauth-ts/master/img_1.png)}}
  mode=widthFix
/>
Taro.navigateTo({ url: /pages/subpages/tictactoe/ai })}> 90ecaf80 b2eb 11e9 9c91 5cd6607772da ;

实现细节

组件代码

最复杂的就是在 useEffect 中计算和设置高度值了:

plain import { Swiper } from @tarojs/components; import Taro from @tarojs/taro; import React, { useEffect, useRef, useState } from react;

import ./hardway-swiper.styl;

const AutoAdjustHeightSwiper = ({ children, ...otherProps }) => { const [currentIndex, setCurrentIndex] = useState(0); const [swiperHeight, setSwiperHeight] = useState(auto); const swiperRef = useRef();

const adjustSwiperHeight = () => { const query = Taro.createSelectorQuery(); query.selectAll(.swiper-item.x).boundingClientRect(); query.exec((res) => { console.log(res = , res); const heights = res[0].map((item) => item.height); const maxHeight = Math.max(...heights); setSwiperHeight(${maxHeight}px); }); };

useEffect(() => { adjustSwiperHeight(); }, [currentIndex]);

const handleSwiperChange = (e) => { setCurrentIndex(e.detail.current); };

const itemsWithRef = React.Children.map(children, (child) => { return React.cloneElement(child, { className: swiper-item x }); });

return <Swiper className={hardway-swiper} style={{ height: swiperHeight }} onChange={handleSwiperChange} ref={swiperRef} {...otherProps}> {itemsWithRef} ; };

const HardwaySwiper = ({ children, ...otherProps }) => { if (Taro.getEnv() === Taro.ENV_TYPE.ALIPAY) { return <Swiper className={hardway-swiper} {...otherProps}> {children} ; }

if (Taro.getEnv() === Taro.ENV_TYPE.WEB) { return <Swiper style={{ height: 100% }} className={hardway-swiper} {...otherProps}> {children} ; }

return <AutoAdjustHeightSwiper className={hardway-swiper} {...otherProps}> {children} ; };

export default HardwaySwiper;

CSS 样式

plain .hardway-swiper { margin-top: 5px overflow visible

.swiper-item, swiper-item { overflow visible height auto !important text-align center display flex align-items center justify-content center } }