背景
公司的广告位功能,目前有两种广告位方式
- 点击广告位,弹出微信小程序弹窗
- 点击广告位,直接跳转到接口返回的url地址
在封装过程中,wx-open-launch-weapp 这个标签的样式不好调整,所以需要封装一个公共的广告位组件,满足上面两种情况,并且保证 wx-open-launch-weapp 的样式能盖住子元素来触发小程序弹窗
思路
采用子绝父相,即:
container.div 为点击唤起小程序的区域(相对定位),而 content 则是该区域的展示内容,wx-open-launch-weapp 则是占满唤起区域(绝对定位)
首先是唤起微信小程序跳转弹窗的组件 ToWxMpV2
// ToWxMpV2.tsx
import React, { FC, useEffect, useState } from 'react';
const _ = require('loadsh');
export type WxMpConfig = {
appid: string;
path: string;
username?: string;
envVersion?: string;
};
type Props = {
config?: WxMpConfig;
onLaunch?: () => void;
};
// 跳转小程序配置,默认跳转为书链作业小程序首页
// 使用跳转组件时,请先配置configWxData(),获取openTagList
const defaultConfig = {
appid: 'wx8a969537f52cd9fa',
path: 'pages/home/home',
username: 'gh_2fd9ea16574d',
envVersion: 'release',
};
export const ToWxMpV2: FC<Props> = (props) => {
const [id] = useState(_.uniqueId('launch-btn'));
const { children, config = defaultConfig, onLaunch } = props;
useEffect(() => {
const btn = document.getElementById(id);
if (btn) {
btn.addEventListener('launch', () => {
onLaunch?.();
});
}
}, []);
return (
<div className="relative">
{/* 内容区域 */}
{children}
{/* wx标签子绝父相覆盖children区域 */}
<wx-open-launch-weapp
id={id}
path={config.path}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
}}
appid={config.appid}
username={config.username}
env-version={config.envVersion}
>
<script type="text/wxtag-template">
{/* 点击唤醒小程序区域 */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
opacity: 0,
}}
/>
</script>
</wx-open-launch-weapp>
</div>
);
};
wx-open-launch-weapp里面都给整上绝对定位,然后宽高100%,保证能盖住 container
然后,再根据业务去封装一个 AdvertiseSpace 广告位组件,该组件逻辑是
- 调接口,记录数据
- 根据数据中的 actionType 字段,判断是跳转微信小程序,还是直接跳转href
- 条件渲染不同的子组件
import {
ActionType,
BannerDTO,
IBannerListParamsDTO,
useBannerList,
} from '@common/hooks/useBannerList';
import {
isInBookLnApp,
isMiniProgram,
isMobile,
ytLogger,
} from '@common/utils/util';
import { useMount } from 'ahooks';
import React, { ReactNode, memo, useCallback, useMemo, useState } from 'react';
import { WxMpConfig } from '../toWxMp';
import { ToWxMpV2 } from '../toWxMpV2';
export enum AdvertiseSpaceType {
/**
* 图片广告位
*/
Image = 'image',
/**
* 按钮广告位
*/
Button = 'button',
}
export type RenderChildrenParams = {
bannerData: BannerDTO;
cb?: () => void;
};
export type AdvertiseSpaceProps = {
/**
* 广告位接口参数
*/
fetchBannerParams: IBannerListParamsDTO;
/**
* 渲染子元素
* @bannerData 广告位数据
*/
renderChildren: (bannerData: BannerDTO) => ReactNode;
};
const AdvertiseSpace = memo(
({ fetchBannerParams, renderChildren }: AdvertiseSpaceProps) => {
const { fetchBannerList } = useBannerList();
//广告位数据
const [bannerData, setBannerData] = useState<BannerDTO>({
banners: [],
hide: [],
});
//微信配置项
const [wxMpConfig, setWxMpConfig] = useState<WxMpConfig | undefined>(
undefined,
);
// 跳转方式
const actionType: ActionType = useMemo(() => {
return bannerData?.banners?.[0]?.actionType;
}, [bannerData]);
// 点击href跳转
const handleClickBanner = useCallback(() => {
//...业务逻辑
}, [bannerData]);
// 渲染dom
const renderContent = useMemo(() => {
switch (actionType) {
// 跳转微信小程序时,渲染 toWxMpV2
case ActionType.ToWx:
return (
<ToWxMpV2
config={wxMpConfig}
>
{renderChildren(bannerData)}
</ToWxMpV2>
);
// 直接跳转 href 时,直接渲染即可
case ActionType.Href:
return (
<div onClick={handleClickBanner}>{renderChildren(bannerData)}</div>
);
default:
return null;
}
}, [
actionType,
bannerData,
handleClickBanner,
hideAdvertiseSpace,
renderChildren,
wxMpConfig,
]);
// 获取 banner 广告位数据
const queryBanner = async () => {
const res = await fetchBannerList(fetchBannerParams);
const { success, data } = res;
if (success && !!data) {
// 业务逻辑,记录数据
setWxMpConfig({
appid,
path,
});
setBannerData(data);
} else {
setWxMpConfig(undefined);
}
};
useMount(() => {
queryBanner();
});
return renderContent;
},
);
export default AdvertiseSpace;
然后去使用该组件:
// 比如,这里是渲染一个图片形式的广告位
const renderChildren = (bannerData: BannerDTO) => {
const imgUrl = bannerData.banners[0].imgUrl;
return (
<div className="my-[12px]">
<Img
src={imgUrl}
style={{
width: '100%',
height: '44px',
objectFit: 'contain',
borderRadius: '40px',
}}
alt=""
/>
</div>
);
};
<AdvertiseSpace
fetchBannerParams={params}
renderChildren={renderChildren}
/>
效果
可以看出,wx-open-launch-weapp 已经盖住了 img,并且大小其实和 img 一样的