前提须知

笔者公司的项目在微信端的功能定位为基础功能交易及服务,通知用户交易提醒、交易流水等,而 APP 为主要的交易功能。之前是在多个页面有引流按钮跳转至 App,功能点比较粗暴,直接 location.href = 应用宝链接。现在产品有需求,说要用微信提供的标签来唤起 App

需求点:

所有跳转至 App 下载页面的部分,改成

需求点

Demo 先行

遇事不决,官网文档。查看后与微信 JS-SDK 功能点很像,这里我不废话,直接跳过。按照官网 demo,把示例写进业务代码中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React, { useEffect, useRef } from 'react';
import { toDownloadApp, isWechat, getWeixinVersion } from 'utils';

const Download = () => {

const wxRef = useRef(null)

useEffect(() => {
if (wxRef.current) {
// @ts-ignore
wxRef.current?.addEventListener('launch', function (e: any) {
console.log('success');
});
// @ts-ignore
wxRef.current.addEventListener('error', function (e) {
console.log('fail', e.detail);
toDownloadApp()
});
}
}, [])

const onHandleClick = () => {
toDownloadApp()
}

return (
<div className="Download" onClick={onHandleClick}>
{/* @ts-ignore */}
<wx-open-launch-app
ref={wxRef}
appid="XXXX"
>
<script type='text/wxtag-template'>
<button>App内查看</button>
</script>
{/* @ts-ignore */}
</wx-open-launch-app>
</div>
)
}

export default React.memo(Download);

测试成功,demo 能跑通

组件试点

现在搞业务,以这个组件(Download)为试点展开,我要点击页面顶部的卡片(多个地方使用,抽离成 Download 组件),让其唤起 App,但是要判断其版本,如果版本过低,让其跳转至应用宝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React, { useState, useEffect, useRef } from 'react';
import LogoImg from '@/assets/images/logo.png';
import { toDownloadApp, isWechat, getWeixinVersion } from 'utils';

const Download = () => {

const wxRef = useRef(null)
const [enableLaunchWeapp, setEnableLaunchWeapp] = useState(false);

useEffect(() => {
const wxVersion = isWechat() && getWeixinVersion() || ''
if (wxVersion) {
let v = wxVersion.split('.')
if (Number(v[0]) >= 7) {
if (Number(v[1]) >= 0) {
if (Number(v[2]) >= 12) {
setEnableLaunchWeapp(true)
}
}
}
}
if (wxRef.current) {
// @ts-ignore
wxRef.current?.addEventListener('launch', function (e: any) {
console.log('success');
});
// @ts-ignore
wxRef.current.addEventListener('error', function (e) {
console.log('fail', e.detail);
toDownloadApp()
});
}
}, [])

const onHandleClick = () => {
if (!enableLaunchWeapp) {
toDownloadApp()
}
}

return (
<div className="Download" onClick={onHandleClick}>
<div className="Download__logo">
<img src={LogoImg} alt="logo" />
</div>
<div className="Download__content">
<div className="Download__content-title">雅美App</div>
<div className="Download__content-desc">长泽雅美服务专区</div>
</div>
{/* <div>1</div> */}
<div className="Download__btn">立即打开</div>
{/* @ts-ignore */}
<wx-open-launch-app
ref={wxRef}
appid="XXXXX"
style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '60px', opacity: 0.3, background: 'blue' }}
>
<script type='text/wxtag-template'>
<div style={{ position: 'fixed', top: 0, left: 0, width: '90%', height: '100%', opacity: 0.3, background: 'red' }} />
</script>
{/* @ts-ignore */}
</wx-open-launch-app>
</div>
)
}

export default React.memo(Download);

效果如下所示:

点击范围

思路逻辑参考:wx-open-launch-weapp 样式问题,我也给它配上颜色,方便后续观察

测试同步,能点击卡片跳转,好,下一步,在所有需要点击跳转页面的地方加入类似这样的代码

1
2
3
4
5
6
7
8
9
10
<wx-open-launch-app
ref={wxRef}
appid="XXXX"
style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '60px', opacity: 0.3, background: 'blue' }}
>
<script type='text/wxtag-template'>
<div style={{ position: 'fixed', top: 0, left: 0, width: '90%', height: '100%', opacity: 0.3, background: 'red' }} />
</script>
{/* @ts-ignore */}
</wx-open-launch-app>

封装组件 WxOpenLaunchApp

如果是这样,就可以将其封装成一个组件了,起个名吧: WxOpenLaunchApp

将唤起 App 的内容包装成一个组件,暴雷 children 和 style 两个 props,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React, { useEffect, useRef, forwardRef } from 'react';
import { toDownloadApp } from 'utils';

export interface WxOpenLaunchAppProps {
children: React.ReactNode;
style?: React.CSSProperties;
}

const WxOpenLaunchApp: React.FC<WxOpenLaunchAppProps> = props => {
const { style, children } = props;

const wxRef = useRef(null)

useEffect(() => {
if (wxRef.current) {
// @ts-ignore
wxRef.current?.addEventListener('launch', function (e: any) {
console.log('success');
});
// @ts-ignore
wxRef.current.addEventListener('error', function (e) {
console.log('fail', e.detail);
toDownloadApp()
});
}
}, [])

return (
<div className="wx-open-launch-app">
{/* @ts-ignore */}
<wx-open-launch-app
ref={wxRef}
appid="XXXX"
style={style}
>
<script type='text/wxtag-template'>
{children}
</script>
{/* @ts-ignore */}
</wx-open-launch-app>
</div>
)
}

export default React.memo(WxOpenLaunchApp);

那么 Download 组件也就可以干净很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
const Download = () => {
...
return (
...
<div className="Download__btn">立即打开</div>
{/* @ts-ignore */}
<WxOpenLaunchApp style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '60px', opacity: 0.3, background: 'blue' }}>
<div style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', opacity: 0.3, background: 'red' }} />
</WxOpenLaunchApp>
...
)
}
...

业务组件 OpenAppPopup

回到需求点,每个点击的地方都要弹出弹出框,点击 打开 App ,再唤起 App,这样的话,弹出框 + WxOpenLaunchApp 就可以结合成一个组件,放出来供页面调用,名字就叫 OpenAppPopup ,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, { FC } from 'react';
import { Popup, WxOpenLaunchApp, Toast } from 'components'; // 此乃公司自研组件库

export interface OpenAppPopupProps {
show: boolean;
onCancel: () => void;
onSubmit: () => void;
}

const OpenAppPopup: FC<OpenAppPopupProps> = (props) => {
const { show, onCancel, onSubmit } = props;

return (
<Popup.Group show={show}>
<Popup.Confirm
title="抱歉,此功能需在雅美App中使用"
btnSubmitText={
<div style={{ position: 'relative' }}>
打开App
<WxOpenLaunchApp style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', opacity: 0.3, background: 'blue' }}>
<div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', opacity: 0.6, background: 'red' }} />
</WxOpenLaunchApp>
</div>
}
onCancel={onCancel}
onSubmit={onSubmit}
/>
</Popup.Group>
)
}

export default React.memo(OpenAppPopup);

示意图如下:

OpenAppPopup

接着在所有有跳转 App 的页面上用上这块逻辑即可

封装 HOOK

每个页面点击类似 下载App 按钮时,会弹出 OpenAppPopup,点击 打开App,需要判断你的微信版本,是否达到 7.0.12,如果每个页面都要加上这段

1
2
3
4
5
6
7
8
9
10
11
const wxVersion = (isWechat() && getWeixinVersion()) || ''
if (wxVersion) {
let v = wxVersion.split('.')
if (Number(v[0]) >= 7) {
if (Number(v[1]) >= 0) {
if (Number(v[2]) >= 12) {
setEnableLaunchWeapp(true)
}
}
}
}

真的太恶心了,果断抽离成 hook。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useState, useEffect } from 'react';
import { isWechat, getWeixinVersion } from 'utils';

const useEnableLaunchWeapp = () => {
const [enableLaunchWeapp, setEnableLaunchWeapp] = useState(false);
useEffect(() => {
const wxVersion = isWechat() && getWeixinVersion() || ''
if (wxVersion) {
let v = wxVersion.split('.')
if (Number(v[0]) >= 7) {
if (Number(v[1]) >= 0) {
if (Number(v[2]) >= 12) {
setEnableLaunchWeapp(true)
}
}
}
}
}, [])
return enableLaunchWeapp
}

export default useEnableLaunchWeapp;

逻辑也很简单,在刚加载时判断它是否可以点击,可以点击,就设置 enableLaunchWeapp 为 true。使用方法也很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React, { useState, useEffect } from 'react';
import { Dispatch, History } from 'umi';
import { OpenAppPopup } from 'components';
+import { useEnableLaunchWeapp } from 'hooks';
import { toDownloadApp } from 'utils';

interface KVProps {
history: History;
}

const KV: React.FC<KVProps> = (props) => {
const { history } = props;

const [isShow, setIsShow] = useState(false);

+const enableLaunchWeapp = useEnableLaunchWeapp();

const onHandleClickToBuy = () => {
setIsShow(true);
};

const onHandleClickToSubmit = () => {
+if (!enableLaunchWeapp) {
+ toDownloadApp()
+}
}

return (
<div className="KV" style={{ background: kvBgColor }}>
<div className="KV__content">
<img src={img} alt="" />
</div>
<OpenAppPopup
show={isShow}
onCancel={() => {
setIsShow(false);
}}
onSubmit={onHandleClickToSubmit}
/>
</div>
);
};

export default React.memo(KV);

与 App 交互

需求点里说:要在所在页面跳转至 App 相对页面,文档上写的很明显,可以传参数 extinfo="your-extinfo",随便写了个让客户端同事先测试先

未唤醒 App

我手机是 IOS 的,是可以唤起的,但是安卓同事调试的时候说,后台运行时,可以唤起 App,但是没有切换动作;如果杀掉进程,就无法唤起。而这问题,大概率是 SDK 配置的问题,同事看了半天没解决,扔给他 Android 接入指南 。我又看不懂 Android,只能看他了

如果测试成功,能跳过去,那么就把本页链接当作 extinfo 传过去,他那边接收到 extinfo 后,做个映射表,跳转至自身的页面即可,所以 WxOpenLaunchApp 需要改造,多一个 extinfo 参数。。。

后记

因为我们用的是 flutter,同事说,因为引入的第三方库不支持,所以跳不过去,所以这个功能要后置,等他搞定了我再做更新

错误处理

除了在 WxOpenLaunchApp 组件中加入监听 error,错误就让它跳转至 App 外,还要做当微信或者系统版本不支持微信标签时,需要监听并进行回退兼容,代码如下:

1
2
3
4
document.addEventListener('WeixinOpenTagsError', function (e: any) {
console.error(e.detail.errMsg) // 无法使用开放标签的错误原因,需回退兼容。仅无法使用开发标签,JS-SDK其他功能不受影响
toDownloadApp()
})

总结

又复用就抽离成组件

必须要上生产环境,所以最好是有个预生产环境

参考资料