欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

復(fù)雜場景下的h5與小程序通信 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們在追求其視覺表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

復(fù)雜場景下的h5與小程序通信

發(fā)表時(shí)間:2021-1-6

發(fā)布人:葵宇科技

瀏覽次數(shù):66

一、背景

在套殼小程序盛行的當(dāng)下, h5調(diào)用小程序能力來打破業(yè)務(wù)邊界已成為家常便飯,h5與小程序的結(jié)合,極大地拓展了h5的能力邊界,豐富了h5的功能。使許多以往純h5只能想想或者實(shí)現(xiàn)難度極大的功能變得輕松簡單。

但在套殼小程序中,h5與小程序通信存在以下幾個(gè)問題:
  • 注入小程序全局變量的時(shí)機(jī)不確定,可能調(diào)用的時(shí)候不存在小程序變量。和全局變量my相關(guān)的判斷滿天飛,每個(gè)使用的地方都需要判斷是否已注入變量,否則就要創(chuàng)建監(jiān)聽。
  • 小程序處理后的返回結(jié)果可能有多種,h5需要在具體使用時(shí)監(jiān)聽多個(gè)結(jié)果進(jìn)行處理。
  • 一旦監(jiān)聽建立,就無法取消,在組件銷毀時(shí)如果沒有判斷組件狀態(tài)容易導(dǎo)致內(nèi)存泄漏。

二、在業(yè)務(wù)內(nèi)的實(shí)踐

  • 因業(yè)務(wù)的特殊性,需要投放多端,小程序sdk的加載沒有放到head里面,而是在應(yīng)用啟動時(shí)動態(tài)判斷是小程序環(huán)境時(shí)自動注入的方式:

    export function injectMiniAppScript() {
        if (isAlipayMiniApp() || isAlipayMiniAppWebIDE()) {
            const s = document.createElement('script'); 
    
            s.src = http://www.wxapp-union.com/'https://appx/web-view.min.js';
            s.onload = () => {
                // 加載完成時(shí)觸發(fā)自定義事件
                const customEvent = new CustomEvent('myLoad', { detail:'' });
                document.dispatchEvent(customEvent);
            };
    
            s.onerror = (e) => {
                // 加載失敗時(shí)上傳日志
                uploadLog({
                    tip: `INJECT_MINIAPP_SCRIPT_ERROR`,
                });
            };
    
            document.body.insertBefore(s, document.body.firstChild);
        }
    }
    

    加載腳本完成后,我們就可以調(diào)用my.postMessagemy.onMessage進(jìn)行通信(統(tǒng)一約定h5發(fā)送消息給小程序時(shí),必須帶action,小程序根據(jù)action處理業(yè)務(wù)邏輯,同時(shí)小程序處理完成的結(jié)果必須帶type,h5在不同的業(yè)務(wù)場景下通過my.onMessage處理不同type的響應(yīng)),比如典型的,h5調(diào)用小程序簽到:
    h5部分代碼如下:

    // 處理掃臉簽到邏輯
        const faceVerify = (): Promise<AlipaySignResult> => {
    
            return new Promise((resolve) => {
                const handle = () => {
                    window.my.onMessage = (result: AlipaySignResult) => {
                        if (result.type === 'FACE_VERIFY_TIMEOUT' ||
                            result.type === 'DO_SIGN' ||
                            result.type === 'FACE_VERIFY' ||
                            result.type === 'LOCATION' ||
                            result.type === 'LOCATION_UNBELIEVABLE' ||
                            result.type === 'NOT_IN_ALIPAY') {
                            resolve(result);
                        }
                    };
    
                    window.my.postMessage({ action: SIGN_CONSTANT.FACE_VERIFY, activityId: id, userId: user.userId });
                };
    
                if (window.my) {
                    handle();
                } else {
                    // 先記錄錯(cuò)誤日志
                    sendErrors('/threehours.3hours-errors.NO_MY_VARIABLE', { msg: '變量不存在' });
                    // 監(jiān)聽load事件
                    document.addEventListener('myLoad', handle);
                }
            });
        };

    實(shí)際上還是相當(dāng)繁瑣的,使用時(shí)都要先判斷my是否存在,進(jìn)行不同的處理,一兩處還好,多了就受不了了,而且這種散亂的代碼遍布各處,甚至是不同的應(yīng)用,于是,我封裝了下面這個(gè)sdkminiAppBus,先來看看怎么用,還是上面的場景

    // 處理掃臉簽到邏輯
        const faceVerify = (): Promise<AlipaySignResult> => {
            miniAppBus.postMessage({ action: SIGN_CONSTANT.FACE_VERIFY, activityId: id, userId: user.userId });
            return miniAppBus.subscribeAsync<AlipaySignResult>([
                'FACE_VERIFY_TIMEOUT',
                'DO_SIGN',
                'FACE_VERIFY',
                'LOCATION',
                'LOCATION_UNBELIEVABLE',
                'NOT_IN_ALIPAY',
            ])
        };

    可以看到,無論是postMessage還是監(jiān)聽message,都不需要再關(guān)注環(huán)境,直接使用即可。在業(yè)務(wù)場景復(fù)雜的情況下,提效尤為明顯。

三、實(shí)現(xiàn)及背后的思考

  • 為了滿足不同場景和使用的方便,公開暴露的interface如下:

      
           interface MiniAppEventBus {
               /**
               * @description 回調(diào)函數(shù)訂閱單個(gè)、或多個(gè)type
               * @template T
               * @param {(string | string[])} type
               * @param {MiniAppMessageSubscriber<T>} callback
               * @memberof MiniAppEventBus
               */
               subscribe<T extends unknown = {}>(type: string | string[], callback: MiniAppMessageSubscriber<T>): void;
               /**
               * @description Promise 訂閱單個(gè)、或多個(gè)type
               * @template T
               * @param {(string | string[])} type
               * @returns {Promise<MiniAppMessage<T>>}
               * @memberof MiniAppEventBus
               */
               subscribeAsync<T extends {} = MiniAppMessageBase>(type: string | string[]): Promise<MiniAppMessage<T>>;
               /**
               * @description 取消訂閱單個(gè)、或多個(gè)type
               * @param {(string | string[])} type
               * @returns {Promise<void>}
               * @memberof MiniAppEventBus
               */
               unSubscribe(type: string | string[]): Promise<void>;
               /**
               * @description postMessage替代,無需關(guān)注環(huán)境變量
               * @param {MessageToMiniApp} msg
               * @returns {Promise<unknown>}
               * @memberof MiniAppEventBus
               */
               postMessage(msg: MessageToMiniApp): Promise<unknown>;
           }

    subscribe:函數(shù)接收兩個(gè)參數(shù),
    type:需要訂閱的type,可以是字符串,也可以是數(shù)組。
    callback:回調(diào)函數(shù)。
    subscribeAsync:接收type(同上),返回Promise對象,值得注意的是,目前只要監(jiān)聽到其中一個(gè)type返回,promise就resolved,未來對同一個(gè)action對應(yīng)多個(gè)結(jié)果type時(shí)存在問題,需要拓展,不過目前還未遇到此類場景。
    unsubscribe:取消訂閱。
    postMessage:postMessage替代,無需關(guān)注環(huán)境變量。

    完整代碼:

       import { injectMiniAppScript } from './tools';
    
       /**
       * @description 小程序返回結(jié)果
       * @export
       * @interface MiniAppMessage
       */
    
       interface MiniAppMessageBase {
           type: string;
       }
    
       type MiniAppMessage<T extends unknown = {}> = MiniAppMessageBase & {
           [P in keyof T]: T[P]
       }
       /**
       * @description 小程序接收消息
       * @export
       * @interface MessageToMiniApp
       */
       export interface MessageToMiniApp {
           action: string;
           [x: string]: unknown
       }
    
       interface MiniAppMessageSubscriber<T extends unknown = {}> {
           (params: MiniAppMessage<T>): void
       }
       interface MiniAppEventBus {
           /**
           * @description 回調(diào)函數(shù)訂閱單個(gè)、或多個(gè)type
           * @template T
           * @param {(string | string[])} type
           * @param {MiniAppMessageSubscriber<T>} callback
           * @memberof MiniAppEventBus
           */
           subscribe<T extends unknown = {}>(type: string | string[], callback: MiniAppMessageSubscriber<T>): void;
           /**
           * @description Promise 訂閱單個(gè)、或多個(gè)type
           * @template T
           * @param {(string | string[])} type
           * @returns {Promise<MiniAppMessage<T>>}
           * @memberof MiniAppEventBus
           */
           subscribeAsync<T extends {} = MiniAppMessageBase>(type: string | string[]): Promise<MiniAppMessage<T>>;
           /**
           * @description 取消訂閱單個(gè)、或多個(gè)type
           * @param {(string | string[])} type
           * @returns {Promise<void>}
           * @memberof MiniAppEventBus
           */
           unSubscribe(type: string | string[]): Promise<void>;
           /**
           * @description postMessage替代,無需關(guān)注環(huán)境變量
           * @param {MessageToMiniApp} msg
           * @returns {Promise<unknown>}
           * @memberof MiniAppEventBus
           */
           postMessage(msg: MessageToMiniApp): Promise<unknown>;
       }
       class MiniAppEventBus implements MiniAppEventBus{
    
           /**
           * @description: 監(jiān)聽函數(shù)
           * @type {Map<string, MiniAppMessageSubscriber[]>}
           * @memberof MiniAppEventBus
           */
           listeners: Map<string, MiniAppMessageSubscriber[]>;
           constructor() {
               this.listeners = new Map<string, Array<MiniAppMessageSubscriber<unknown>>>();
               this.init();
           }
    
           /**
           * @description 初始化
           * @private
           * @memberof MiniAppEventBus
           */
           private init() {
               if (!window.my) {
                   // 引入腳本
                   injectMiniAppScript();
               }
    
               this.startListen();
           }
    
           /**
           * @description 保證my變量存在的時(shí)候執(zhí)行函數(shù)func
           * @private
           * @param {Function} func
           * @returns 
           * @memberof MiniAppEventBus
           */
           private async ensureEnv(func: Function) {
               return new Promise((resolve) => {
                   const promiseResolve = () => {
                       resolve(func.call(this));
                   };
    
                   // 全局變量
                   if (window.my) {
                       promiseResolve();
                   }
    
                   document.addEventListener('myLoad', promiseResolve);
               });
           }
    
           /**
           * @description 監(jiān)聽小程序消息
           * @private
           * @memberof MiniAppEventBus
           */
           private listen() {
               window.my.onMessage = (msg: MiniAppMessage<unknown>) => {
                   this.dispatch<unknown>(msg.type, msg);
               };
           }
    
           private async startListen() {
               return this.ensureEnv(this.listen);
           }
    
           /**
           * @description 發(fā)送消息,必須包含action
           * @param {MessageToMiniApp} msg
           * @returns 
           * @memberof MiniAppEventBus
           */
           public postMessage(msg: MessageToMiniApp) {
               return new Promise((resolve) => {
                   const realPost = () => {
                       resolve(window.my.postMessage(msg));
                   };
    
                   resolve(this.ensureEnv(realPost));
               });
           }
    
           /**
           * @description 訂閱消息,支持單個(gè)或多個(gè)
           * @template T
           * @param {(string|string[])} type
           * @param {MiniAppMessageSubscriber<T>} callback
           * @returns 
           * @memberof MiniAppEventBus
           */
           public subscribe<T extends unknown = {}>(type: string | string[], callback: MiniAppMessageSubscriber<T>) {
               const subscribeSingleAction = (type: string, cb: MiniAppMessageSubscriber<T>) => {
                   let listeners = this.listeners.get(type) || [];
    
                   listeners.push(cb);
                   this.listeners.set(type, listeners);
               };
    
               this.forEach(type,(type:string)=>subscribeSingleAction(type,callback));
           }
    
           private forEach(type:string | string[],cb:(type:string)=>void){
               if (typeof type === 'string') {
                   return cb(type);
               }
    
               for (const key in type) {
                   if (Object.prototype.hasOwnProperty.call(type, key)) {
                       const element = type[key];
    
                       cb(element);
                   }
               }
           }
    
           /**
           * @description 異步訂閱
           * @template T
           * @param {(string|string[])} type
           * @returns {Promise<MiniAppMessage<T>>}
           * @memberof MiniAppEventBus
           */
           public async subscribeAsync<T extends {} = MiniAppMessageBase>(type: string | string[]): Promise<MiniAppMessage<T>> {
               return new Promise((resolve, _reject) => {
                   this.subscribe<T>(type, resolve);
               });
           }
    
           /**
           * @description 觸發(fā)事件
           * @param {string} type
           * @param {MiniAppMessage} msg
           * @memberof MiniAppEventBus
           */
           public async dispatch<T = {}>(type: string, msg: MiniAppMessage<T>) {
               let listeners = this.listeners.get(type) || [];
    
               listeners.map(i => {
                   if (typeof i === 'function') {
                       i(msg);
                   }
               });
           }
    
           public async unSubscribe(type:string | string[]){
               const unsubscribeSingle = (type: string) => {
                   this.listeners.set(type, []);
               };
    
               this.forEach(type,(type:string)=>unsubscribeSingle(type));
           }
       }
    
       export default new MiniAppEventBus();

    class內(nèi)部處理了腳本加載,變量判斷,消息訂閱一系列邏輯,使用時(shí)不再關(guān)注。

四、小程序內(nèi)部的處理

  • 定義action handle,通過策略模式解耦:

    const actionHandles = {
        async FACE_VERIFY(){},
        async GET_STEP(){},
        async UPLOAD_HASH(){},
        async GET_AUTH_CODE(){},
        ...// 其他action
    }
    .... 
    // 在webview的消息監(jiān)聽函數(shù)中
    async startProcess(e) {
        const data = http://www.wxapp-union.com/e.detail;
        // 根據(jù)不同的action調(diào)用不同的handle處理
        const handle = actionHandles[data.action];
        if (handle) {
            
            return actionHandles[data.action](this, data)
        }
        return uploadLogsExtend({
            tip: STRING_CONTANT.UNKNOWN_ACTIONS,
            data
        })
    }

    使用起來也是得心順暢,舒服。

其他

類型完備,使用時(shí)智能提示,方便快捷。

相關(guān)案例查看更多