使用微信wx-open-launch-app標籤實現微信網頁開啟App

山頭人漢波發表於2022-04-24

前提須知

筆者公司的專案在微信端的功能定位為基礎功能交易及服務,通知使用者交易提醒、交易流水等,而 APP 為主要的交易功能。之前是在多個頁面有引流按鈕跳轉至 App,功能點比較粗暴,直接 location.href = 應用寶連結。現在產品有需求,說要用微信提供的標籤來喚起 App

需求點:

所有跳轉至 App 下載頁面的部分,改成

需求點

Demo 先行

遇事不決,官網文件。檢視後與微信 JS-SDK 功能點很像,這裡我不廢話,直接跳過。按照官網 demo,把示例寫進業務程式碼中

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,但是要判斷其版本,如果版本過低,讓其跳轉至應用寶

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 樣式問題,我也給它配上顏色,方便後續觀察

測試同步,能點選卡片跳轉,好,下一步,在所有需要點選跳轉頁面的地方加入類似這樣的程式碼

<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,程式碼如下:

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 元件也就可以乾淨很多

...
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 ,程式碼如下:

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,如果每個頁面都要加上這段

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。程式碼如下:

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。使用方法也很簡單

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 外,還要做當微信或者系統版本不支援微信標籤時,需要監聽並進行回退相容,程式碼如下:

document.addEventListener('WeixinOpenTagsError', function (e: any) {
  console.error(e.detail.errMsg) // 無法使用開放標籤的錯誤原因,需回退相容。僅無法使用開發標籤,JS-SDK其他功能不受影響
  toDownloadApp()
})

總結

又複用就抽離成元件

必須要上生產環境,所以最好是有個預生產環境

參考資料

相關文章