This is the third day of my participation in the More text Challenge. For details, see more text Challenge

A, small program and H5 difference

1.1 Running environment

From the perspective of running environment, the host environment of H5 is a browser. As long as there is a browser, you can use it, including the Web-View component in the APP, and the Web-View component applet provided by the applet is different. It runs on specific mobile software platforms (Wechat/Alipay/Bytedance/Baidu/QQ, etc.).

Take wechat small program for example, it is a built-in parser based on the browser kernel refactoring, it is not a complete browser, the official document emphasizes that the script cannot use the commonly used window object and document object in the browser, that is, there is no DOM and BOM related API. This one kills JQ and some NPM packages that rely on BOM and DOM.

1.2 Operation mechanism

H5 run is a web page run, here is not more than narrative, small program or wechat small program for example.

1.2.1 start

If the user has opened a small program, in a certain period of time to open the small program again, at this time there is no need to restart, just the background state of the small program to switch to the foreground, the whole process is called hot start. If the user opens it for the first time or the small program is actively destroyed by wechat and then opens it again, the small program needs to be reloaded and started, which is cold start.

1.2.2 destroyed

When the small program into the background for a certain time, or system resources are too high, or you manually destroy, only the real destruction

1.3 System Rights

Where is H5 most criticized? System permissions, such as the state of network communication, data cache capacity, contacts, or call the hardware, such as bluetooth functions, and so on some APP, H5, no permissions of these systems, because of its heavy dependence on browser capabilities, is still WeChat small program, for example, WeChat client these system-level access can be seamlessly and WeChat small applications, Official claims to have the smooth performance of Native App.

1.4 Development coding level

H5 development as we all know, standard HTML, CSS, JavaScript, the three swordsman small program is different, (Wechat/Alipay/bytedance/Baidu/QQ, etc.) different small programs have their own definition of a unique language the most commonly used micro channel small program, Custom WXML, WXSS, WXML are all wechat custom tags, WXSS, JSON and JS files in the writing method are slightly limited, official documents have a clear introduction to use, although easy to get started, but there are still differences.

1.5 Update mechanism

H5 if you want to update how to update, update aside CDN/ browser cache what, basically update the end of the refresh can see the effect of small program is different, or wechat for example, hey hey, micro channel small program update what is required to pass the audit. After a new release, and developers can’t immediately affect all web users, should be in 24 hours after the release issued by the new version information to the user small program every time a cold start, will check for updated version, if found to have the new version, asynchronous download the new version code package, and at the same time with the client to start the local package, So the new version of the small program need to wait for the next cold start will be applied, of course, wechat also has WX. getUpdateManager can do check update

1.6 Rendering mechanism

H5 is web rendering, browser rendering. Micro channel small program host environment is micro channel, host environment in order to execute a variety of small program files: WXML file, WXSS file, JS file, provides a two-line model.

Second, small program environment analysis

The rendering layer and logical layer of the applet are managed by two threads:

  • The rendering layer’s interface is rendered using WebView. There are multiple interfaces in a small program, so there are multiple WebViews in the rendering layer.
  • The logical layer uses JSCore threads to run JavaScript scripts.

The communication between the two threads is forwarded by the small program Native side, and the network request sent by the logical layer is forwarded by the Native side. Designed for the purpose of control and security, wechat mini program prevents developers from using some open interfaces provided by browsers, such as jump page, DOM operation, and dynamic script execution. The logical layer is separated from the view layer. There is only data communication between the view layer and the logical layer, which can prevent developers from operating the interface at will and better ensure the security of user data.

The script execution environment on the three ends and the environment used to render non-native components are different:

Runtime environment Logic layer Rendering layer
Android V8 Chromium custom kernel
IOS JavaScriptCore WKWebView
Small program developer tools NWJS Chrome WebView

Runtime environment logic layer rendering layer AndroidV8Chromium custom kernel IOS JavaScriptCoreWKWebView applet developer tool NWJSChrome WebView applet view is rendered in WebView, So the way to do that is to use HTML. But the HTML language has many tags, increasing the cost of understanding, and using THE HTML language directly, developers can use tags to jump to other online web pages, and can also execute JAVAScript animation, the aforementioned two-thread model to solve the problem of control and security.

Therefore, small programs design a component framework called Exparser. Based on this framework, a set of components is built in to cover the basic functions of small programs, making it easy for developers to quickly build any interface. It also provides the ability to customize components, allowing developers to extend more components to achieve code reuse. It is worth mentioning that some of the more complex components of the built-in components are rendered natively on the client side to provide better performance.

Iii. H5 browser environment analysis

You know browser caching is a very useful feature that can improve performance, reduce latency, and reduce bandwidth and network load. The browser’s caching mechanism can be summed up in two sentences:

  • Each time the browser initiates a request, it first looks in the browser cache for the result of the request and the cache identifier
  • Each time the browser gets a returned request result, it stores the result and the cache identifier in the browser cache

Further, we can take a look at how forced caching and negotiated caching work. If mandatory caching (Expires and cache-control) is in effect, use the Cache directly. If not, use negotiated caching (last-modified/if-modified-since and Etag/ if-none-match). The server decides whether to use the negotiation cache. If the negotiation cache is invalid, the request cache is invalid and 200 is returned. Resources and cache identifier are returned and stored in the browser cache. If effective, return 304 and continue using cache. If you want to know more, you can use the keywords (strong Cache, negotiated Cache, Expire, cache-control, etc.) to find more detailed information. Wechat’s Web-View component is a browser embedded in a small program. It does not completely comply with the above rules in the cache, that is, its cache can not be cleaned in time. Presumably the following operation we have tried:

  • Manually exit the small program, enter again;
  • Exit wechat from the background and then open and re-enter the small program;
  • Modify Nginx cache-control configuration.
  • Manually clear android wechat browser cache with debugx5.qq.com;
  • IOS takes advantage of wechat’s own clear cache function.

Failure to refresh the cache in a timely manner can result in the latest page being published while the old page is still in the applet, which can lead to many problems, such as inconsistent data on the front and back ends, new features not working in a timely manner, modifications not being resolved, and so on. This Chat is designed to solve the problem of refreshing the cache after a certain period of time (a day or hours, no obvious pattern).

3.1 Implementation of H5 page onShow and cross-page communication in small program

The first thing that comes to mind is the implementation of the onShow method, which was previously proposed with visibilityChange. However, after the survey, this method performed as expected in ios, but in Android phones, it could not be triggered as expected. Hence the following solution, which requires both h5 and the webView of the applet to do the processing.

Core idea: Take advantage of the Hash feature of webView

  • Applet passes hash parameters and the page is not updated (this is the same as the browser)
  • H5 can use HashChange to capture the latest parameters for custom logical processing
  • Finally, run window.history.go(-1)

Why do we execute window.history.go(-1)? Because the hash change causes the length of the WebView history stack to be +1, the user needs to return the operation one more time. But this step is obviously redundant. At the same time, window.history.go(-1) will remove the parameters that the WebView added to the hash, and ensure the same url as before.

3.2 pay attention to the point

For the sake of smooth access, you can’t do one size fits all. You need to ensure that the existing page continues to be accessed without any modification. New capabilities are distinguished by additional parameters, such as detecting the query part of the URL with __isonShowpro =1 and passing the parameter by hash. Change the logic so that when __isonShowPro =1, the hash processing logic has the highest priority for parameter definitions, and add two underscores in front of it to partition the normal parameters in the URL. Let’s see how the H5 SDK is implemented

import util from './util';

class WASDK {
  /**
   * Create a instance.
   * @ignore* /
  constructor(){
    // HashChang event handling
    if('onhashchange' in window && window.addEventListener && ! WASDK.hashInfo.isInit){// Update the flag bit
      WASDK.hashInfo.isInit = true;
      / / bind hashchange
      window.addEventListener('hashchange'.() = >{
        // If the webView applet modifies the hash, it will be processed
        if (util.getHash(window.location.href, '__wachangehash') = = ='1') {
          // This is a pit:
          // After the ios applet WebView has modified the HASH of the URL, the page hashchange and update can be triggered normally
          // However, h5 will fail to call some small program capabilities (e.g., ios will fail to call wx.uploadImg after setting hash, wx.config needs to be reset)
          // Because ios applet logic is, as long as the URL changes, the appId in wx.config will not be found
          // So you need to reconfigure wx.config
          // This step is to retrieve the parameters that were previously set to wx.config.
          const jsticket = window.native && window.native.adapter && window.native.adapter.jsticket || null;
          const ua = navigator.userAgent;
          // Non-Android systems need to reset wx.config
          if(jsticket && ! (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1)) {
            window.wx.config({
              debug: false.appId: jsticket.appId,
              timestamp: jsticket.timestamp,
              nonceStr: jsticket.noncestr,
              signature: jsticket.signature,
              jsApiList: ['onMenuShareTimeline'.'onMenuShareAppMessage'.'onMenuShareQQ'.'onMenuShareQZone'.'onMenuShareWeibo'.'scanQRCode'.'chooseImage'.'uploadImage'.'previewImage'.'getLocation'.'openLocation']})}// Triggers a callback to the cache array
          WASDK.hashInfo.callbackArr.forEach(callback= >{
            callback();
          })
          // Perform the return operation (this step is important!!)
          // After the webView sets the hash parameter, the WebView history stack will be +1
          // We don't need this extra history, so we need to do a return to get rid of it
          // Even the return operation is only a hash level change, so it does not trigger a page refresh
          // Use setTimeout to return on the next event loop. If there is a subsequent dom operation, it can be completed in the current event loop
          setTimeout(() = >{
            window.history.go(-1);
          }, 0); }},false)}}/** * Hash information */
  static hashInfo = {
    // Whether it has been initialized
    isInit: false.// Hash callback array
    callbackArr: []}/** * hook method * when the page is displayed again@param {Function} callback- This parameter is mandatory. The callback method is passed as an argument parsing object (*/) following the hash part of the question mark
  @execLog
  onShow(callback){
    if (typeof callback === 'function') {
      // Wrap the callback method in onshow logic and push it into the cache array
      WASDK.hashInfo.callbackArr.push(function(){
        // Check whether the specified parameter has changed
        if(util.getHash(window.location.href, '__isonshow') = = ='1') {// Triggers the onShow callbackcallback(); }})}else {
      util.console.error(Call onShow by passing the correct callback); }}/** * The business processing is completed and the message is sent *@param {Object}           obj- This parameter is mandatory. Message object *@param {String}           Obj. key - This parameter is mandatory. Message name *@param {String}           Obj. Content - Optional, message content, default empty string, if it is a content object, please convert to a string *@param {String|Number}    condition- Optional, default only postMessage * String - Can pass the specified URL path, when the small program webView opens the specified URL or onshow, will trigger the message * can also pass the small program path, This reserve * Number for later - returns to the specified test, similar to history.go(-1), e.g. -1, -2 */
  @execLog
  serviceDone(obj, condition){
    if(obj && obj.key){
      / / the message body
      const message = {
        // The message name
        key: obj.key,
        / / the message body
        content: obj.content || ' '.// Trigger condition
        trigger: {
          // Type 'immediately' fires immediately in the next onshow, 'URL' fires when the specified H5 link is found, and 'path' fires when the specified applet path is opened
          type: 'immediately'.// The condition content, immediately is empty, URL is the H5 link address, path is the applet path
          content: ' '}};// Parse the trigger condition
      condition = condition || 0;
      // If it is a path
      if(typeof condition === 'string' && (condition.indexOf('http') > -1 || condition.indexOf('pages/') > -1)) {// Set the message triggering condition
        message.trigger = {
          type: condition.indexOf('http') > -1 ? 'url' : 'path'.content: condition
        }
      }
      // Send the message
      wx.miniProgram.postMessage({
        data: {
          messageData: message
        }
      });
      // If it is not the URL or path, then the conditon should be returned
      if(message.trigger.type === 'immediately') {// Check to see if you need to return the specified hierarchy, compatible with the type of '-1' string passed in
        try{
          condition = parseInt(condition, 10);
        }catch(e){}
        // Ensure that the returned series is correct
        if(condition && typeof condition === 'number'&&!isNaN(condition)){
          this.handler.navigateBack({delta: Math.abs(condition)}); }}}else{
      util.console.error(Call serviceDone and pass in an object that does not contain the key value); }}... }window.native = new Native();
export default native;
Copy the code

This looks quite a lot, but it can be summed up in two points:

Implementation of the onShow method

Bind a HashChange event (this is done to prevent duplicate binding events), cache the onShow custom events in an array, and when hashChange is triggered, it determines whether to trigger based on the unique flag bits __isonShow and __wachangehash

Implementation of the serviceDone method

To get their data processing, processing the data of the trigger condition, immediately said a recent onShow trigger, or url specified by wx. MiniProgram. PostMessage sending data

The browser accesses resources through THE URL address. If the address embedded with H5 does not change, the web-view accesses resources from the cache, which does not have the latest data. As a result, the latest resources on the server cannot reach the browser at all. This explains why changes to Nginx’s cache-control configuration do not take effect. Therefore, in order to completely solve the timely refresh, you must let the web-view to access the new address. We assume that a small program to access the URL: https://www.yourdomain.com/101/#/index 101 is to build a version number, incrementally, ensure every time is different.

3.4 How to determine the environment of the current page of a small program

For this part, we need to plant an SDK on the H5 page. For example, the name is Bridg. js. The following is a common method summarized by my small program for several years:

// bridge.js
let ua = window.navigator.userAgent.toLowerCase();
const globalObj = {
    testDataArr: [].doJSReadyFuncExecuted: false.errorInfo: ' '.miniappSDK: null.miniappType: ' '.actionQueue: [].MINIAPP_TYPE: {
        WECHATMINIAPP:  'WECHATMINIAPP'.// miniprogram
        WECHATAPP:      'WECHATAPP'.// miniprogram + offiaccount
        OLDQUICKAPP:    'OLDQUICKAPP'.// old
        NEWQUICKAPP:    'NEWQUICKAPP'.// new
        ALIPAYAPP:      'ALIPAYAPP'.BAIDUAPP:       'BAIDUAPP'.TOUTIAOAPP:     'TOUTIAOAPP'.QQAPP:          'QQAPP'         // No longer maintained
    },
    JSSDK_URL_OBJ: {
        WECHATMINIAPP: 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'.WECHATAPP: 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'.OLDQUICKAPP: 'https://xxxxxxxx/amsweb/quickApp/mixBridge.js'.NEWQUICKAPP: 'https://quickapp/jssdk.webview.min.js'.ALIPAYAPP: 'https://appx/web-view.min.js'.BAIDUAPP: 'https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.21.js'.TOUTIAOAPP: 'https://s3.pstatp.com/toutiao/tmajssdk/jssdk-1.0.1.js'.QQAPP: 'https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js'
    },
    bversion: '1.0.0'
}

if(typeof window['__bfi'] = ='undefined') {
    window['__bfi'] = [];
};

window['__bfi'].push([
    '_tracklog'.'174537'.`ua=${ua}&pageId=\${page_id}`
]);

function isAndroid () {
    return ua.includes('android');
}

function isWechatMiniapp () {
    // @source https://developers.weixin.qq.com/community/develop/doc/00022e37c78b802f186750b5751000
    // in wechat && (in android || in ios)
    return isWechat() && (ua.includes('miniprogram') | |window.__wxjs_environment === 'miniprogram');
}

function isWechat () {
    // in wechat-web-browser
    // https://blog.csdn.net/daipianpian/article/details/86543080
    // @source blog ( https://www.jianshu.com/p/6a10f833b099 )
    return /micromessenger/i.test(ua) || /windows phone/i.test(ua);
}

function isOldQuickapp () {
    return (/(hap|OPPO\/Hybrid)\/\d/i.test(ua)) && ! isNewQuickapp(); }function isNewQuickapp () {
    // @source 2020.04.10, Vivo(Li Chunjiao) has confirmed that this method is feasible
    return ua.includes('mode-quickapp');
}

function isAlipay () {
    // @source 2020.06.15, Alipay has confirmed that this method is feasible
    let isAli = (/APXWebView/i.test(ua)) || (/MiniProgram/i.test(ua) && ! ua.includes('micromessenger'));
    / / @ source 2020.11.17, https://www.yuque.com/books/share/6d822c34-9121-47d8-a805-4c57b0b2d2f0/hiv1tn
    let isUCKuake = ua.includes('aliapp') && (ua.includes('uc') || ua.includes('quark'));
    / / @ source 2021.03.26
    let isGaode = ua.includes('aliapp') && ua.includes('amapclient');
    return isAli || isUCKuake || isGaode;
}

function isBaidu () {
    / / @ source 2020.11.05, baidu 's doc (https://smartprogram.baidu.com/docs/develop/component/open_web-view/)
    return /swan\//.test(ua) || /^webswan-/.test(window.name);
}

function isToutiao () {
    / / @ source 2020.11.05, toutiao's doc ( https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/component/open-capacity/web-view/ )
    return ua.includes("toutiaomicroapp");
}

function isQQ () {
    // @source 2021.04.21, add uA.includes ('miniprogram'), qq's doc ( https://q.qq.com/wiki/develop/miniprogram/component/open-ability/web-view.html )
    return ua.includes('qq') && ua.includes('miniprogram');
}

// return miniapp type of the environment
function isMiniProgram () {
    let appType = false;
    let typeNameObj = globalObj.MINIAPP_TYPE

    try {
        if (isWechatMiniapp()) {
            appType = typeNameObj.WECHATMINIAPP;
        } else if (isOldQuickapp()) {
            appType = typeNameObj.OLDQUICKAPP;
        } else if (isNewQuickapp()) {
            appType = typeNameObj.NEWQUICKAPP;
        } else if (isAlipay()) {
            appType = typeNameObj.ALIPAYAPP;
        } else if (isBaidu()) {
            appType = typeNameObj.BAIDUAPP;
        } else if (isToutiao()) {
            appType = typeNameObj.TOUTIAOAPP;
        } else if (isQQ()) {
            appType = typeNameObj.QQAPP;
        }

        console.log('Determine the environment, isMiniProgram returns:', appType);
        window['__bfi'].push([
            '_tracklog'.'174537'.`api_name=isMiniProgram&miniappType=${appType}&pageId=\${page_id}`
        ]);

        return appType;
    } catch (e) {
        window['__bfi'].push([
            '_tracklog'.'174537'.`api_name=isMiniProgram&err_msg=${e.message}&err_stack=${e.stack}`
        ]);
        return false; // 'catch error'}}export {
    isAndroid,       // Check whether the H5 page is running on Android
    isWechatMiniapp, // Determine whether the H5 page is in the wechat mini program environment
    isWechat,        // Determine whether the H5 page is in the wechat environment
    isOldQuickapp,   // Determine whether the H5 page is in the [old version quick application] small program environment
    isNewQuickapp,   // Determine whether the H5 page is in the [new fast application] small program environment
    isAlipay,        // Determine whether the H5 page is in the alipay small program environment
    isBaidu,         // Determine whether the H5 page is in the Baidu small program environment
    isToutiao,       // Determine whether the H5 page is in the headline applet environment
    isQQ,            // Determine whether the H5 page is in the QQ applet environment
    isMiniProgram    // Return the application name of the H5 page
}
Copy the code

Matters needing attention when using

It’s a good idea to check the documentation for the applets before using them, because each applets have different levels of API support. The reference to the js file cannot be placed in the bridge. Js will operate on the current page’s head. Because bridge-js is introduced into the JSSDK by adding a script tag to the head tag, an error will be reported if bridge-js is introduced into the head tag.

If h5 is enabled and the page access is restricted message is displayed, perform the following operations :(in this case, the message is usually displayed when the h5 url of the test environment is opened.) select ignore webview domain name validity check and ignore request domain name validity check in the IDE.

【 Kuaiapprelated 】 Currently Vivo, Oppo, Huawei three manufacturers have supported the new version of kuaiapp. VivoOPPO is online, xiaomi does not support. For the new version of the fast application, if the H5 page needs to call the API provided in the jS-SDK of the new version of the fast application, you need to configure the domain name of the H5 link to the trusted URL in advance (the configuration should be in the form of regular expression).

The redirectTo, navigateTo, and other page jump apis of the toutiao applet only support the absolute path where the URL is /

[Alipay related] The current 1.0.73 version bridg. js method to determine whether it is in alipay applet will judge that both H5 in Alipay applet and H5 in Alipay built-in browser are in Alipay applet. Therefore, before tuning my.XXXX, we need to first tune the judgment environment tool function to make sure that it is really in alipay small program, rather than alipay built-in browser.

3.5 Applet to obtain the latest version number

In the applet, we use the app’s onShow hook function to get the latest URL, but we also need to ensure that the version number is fetched before any other page can be loaded, so we use the synchronous interface call. Please refer to the following code:

// Add a synchronous request to the server to obtain the latest path
onShow: function (options) {
    this.getFEVersion()
},
getFEVersion: function () {
    // The following is how to write a synchronous call using a Promise
    return new Promise(function (resolve, reject) {
      wx.request({
        // The following is a local debugging address, online please change your server address
        url: 'http://192.168.0.168:8090/getFEVersion'.data: {},
        method: 'POST'.header: {
          'content-type': 'application/json',},success: function (res) {
          if (res.data.success) {
            const app = getApp();
            //res.data.version Is the latest FE version number returned from the server, i.e. 101 above
            app.globalData.feUrl = 'https://www.yourdomain.com/' + res.data.version + '/#/index'
          }
          resolve();
        },
        fail: function (error) {
          console.log(error); reject(); }})}); }, webView dynamic processing/ * * *@file Depending on the type of small program that is entered, the corresponding JavaScript file is dynamically loaded * specifying the SRC attribute of the <script> element, specifying the event handler (onLOAD event onError event) */

const globalObj = {
    testDataArr: [].doJSReadyFuncExecuted: false.errorInfo: ' '.miniappSDK: null.miniappType: ' '.actionQueue: [].MINIAPP_TYPE: {
        WECHATMINIAPP:  'WECHATMINIAPP'.// miniprogram
        WECHATAPP:      'WECHATAPP'.// miniprogram + offiaccount
        OLDQUICKAPP:    'OLDQUICKAPP'.// old
        NEWQUICKAPP:    'NEWQUICKAPP'.// new
        ALIPAYAPP:      'ALIPAYAPP'.BAIDUAPP:       'BAIDUAPP'.TOUTIAOAPP:     'TOUTIAOAPP'.QQAPP:          'QQAPP'         // No longer maintained
    },
    JSSDK_URL_OBJ: {
        WECHATMINIAPP: 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'.WECHATAPP: 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'.OLDQUICKAPP:'https://xxxxxxxxx/amsweb/quickApp/mixBridge.js'.NEWQUICKAPP: 'https://quickapp/jssdk.webview.min.js'.ALIPAYAPP: 'https://appx/web-view.min.js'.BAIDUAPP: 'https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.21.js'.TOUTIAOAPP: 'https://s3.pstatp.com/toutiao/tmajssdk/jssdk-1.0.1.js'.QQAPP: 'https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js'
    },
    bversion: '1.0.0'
}

let n = 0;
function loadListener (type) {
    // Execute once before entering setTimeout
    // Add more burial sites to record different types of information
    console.log('====== Retry times:${n}= = = = = = `);
    if(n === 0) {
        processAddRes(type);
    } else {
        setTimeout(function () {
            processAddRes(type);
        }, 200)}}function processAddRes(type) {
    let curMiniappType = globalObj.miniappType;
    let curLoadJsUrl = globalObj.JSSDK_URL_OBJ[curMiniappType];

    if(! addJSSDKToGlobalObj()){ n++; loadListener();if(n % 10= = =0) {
            const msg = 'Retry reaches [${n}】 time `
            console.log(msg);
            console.log(globalObj.errorInfo || '= = = = = =');
        }
        return;
    }

    let actionQueue = globalObj.actionQueue;
    if (actionQueue && actionQueue.length) {
        let aItem = null;
        while (aItem = actionQueue.shift()) {
            try {
                globalObj.miniappSDK[aItem.apiName].apply(globalObj.miniappSDK, aItem.args)
            } catch (e) {
                //}}}}// Save the methods provided by JSSDK to global
function addJSSDKToGlobalObj () {
    let curMiniappType = globalObj.miniappType;

    try{
        let _miniappSDK = null;
        switch(curMiniappType) {
            case 'WECHATMINIAPP':
            case 'WECHATAPP':
            case 'OLDQUICKAPP':
                _miniappSDK = typeofwx ! = ='undefined' && wx.miniProgram;
                break;
            case 'NEWQUICKAPP':
                _miniappSDK = qa;
                break;
            case 'ALIPAYAPP':
                _miniappSDK = my;
                break;
            case 'BAIDUAPP':
                _miniappSDK = typeofswan ! = ='undefined' && swan.webView;
                break;
            case 'TOUTIAOAPP':
                _miniappSDK = typeoftt ! = ='undefined' && tt.miniProgram;
                break;
            case 'QQAPP':
                _miniappSDK = typeofqq ! = ='undefined' && qq.miniProgram;
                break;
        }

        if(_miniappSDK) {
            globalObj.miniappSDK = _miniappSDK
        }

        if(! globalObj.miniappSDK || ! globalObj.miniappSDK.navigateTo) {console.log(globalObj)
            letg_errmsg = (! globalObj.miniappSDK ?'miniappSDK_is_undefined' : 'API_is_undefined');
            let g_errstack = 'none'
            globalObj.errorInfo = 'g_errmsg=' + g_errmsg + '&g_errstack=' + g_errstack;

            return false; }}catch (e) {
        Return false: when the loadListener triggers the burying point, record the reason why the return false is attached to the global variable
        globalObj.errorInfo = 'g_errmsg=' + e.message + '_have_catch_error' + '&g_errstack=' + e.stack;

        return false;
    }
    globalObj.errorInfo = 'g_errmsg=outof_try-catch_return_true';

    return true;
}

function parseQuery(url) {
    let query = {};
    let idx = url.indexOf("?");
    let str = url.substr(idx + 1);
    if (str == "" || idx == -1) {
        return {};
    }
    let pairs = str.split('&');
    for (let i = 0; i < pairs.length; i++) {
        let pair = pairs[i].split('=');
        // When multiple pieces of data are split according to the = sign, keep all the data from the first bit of the array.
        / / such as SRC = / issue/create? SRC: '/issue/create? Type =1752', not SRC: '/issue/create? type'
        if (pair.length > 2) {
            pair[1] = pair.slice(1).join('=');
        }
        query[decodeURIComponent(pair[0=]]decodeURIComponent(pair[1] | |' ');
    }
    return query;
};

function loadScript() {
    // Bury the point information, add the loaded jSSDK-URL, later may be able to get the wechat version number from the URL
    // Device brand, device model, wechat version number, operating system and version, client platform, client base library version Object wx.getSystemInfoSync()
    let curMiniappType = globalObj.miniappType;
    let curLoadJsUrl = globalObj.JSSDK_URL_OBJ[curMiniappType];

    let jSBridgeReady = function(type) {
        console.log('jSBridgeReady, event type: ', type);

        // Ensure that subsequent logic is executed only once
        if (globalObj.doJSReadyFuncExecuted) {
            return;
        }
        globalObj.doJSReadyFuncExecuted = true;
        console.log('script is onload, doJSReadyFuncExecuted')

        loadListener(type);
    }


    if (curMiniappType === "WECHATMINIAPP" || curMiniappType === "WECHATAPP" || curMiniappType === "OLDQUICKAPP") {
        // Before listening to WeixinJSBridgeReady and onLoad, send a bury point to see if there is wx and wx.miniProgram (because currently only wechat JSSDK is introduced in NFES).
        document.addEventListener('WeixinJSBridgeReady'.function() {
            console.log('WeixinJSBridgeReady ======');
            jSBridgeReady('WeixinJSBridgeReady')},false)}if (curMiniappType === "NEWQUICKAPP") {
        document.addEventListener('QaJSBridgeReady'.function() {
            console.log('QaJSBridgeReady ======');
            jSBridgeReady('QaJSBridgeReady')},false)}let script = document.createElement("script");
    script.src = curLoadJsUrl;
    script.async = false; Async = async; async = async

    let scriptArr = document.getElementsByTagName('script');
    console.log(scriptArr);

    for(let i = 0; i < scriptArr.length; i++) {
        let item = scriptArr[i];
        if(item.src.includes('/ares2/market/mixappBridge/') && item.src.includes('/default/bridge')) {
            // Set async dynamically
            let queryObj = parseQuery(item.src); // The bottom of the pocket is {}
            console.log('queryObj: ', queryObj);
            if(typeofqueryObj.bridgeAsync ! = ='undefined') {
                script.async = queryObj.bridgeAsync === '1' ? true : false; }}}console.log('Finally, script.async:', script.async);

    script.onload = function(e) {
        console.log('script is onload ======')
        jSBridgeReady('onload')
    }

    script.onerror = function(e) {
        console.log('script is onerror')}window.onerror = function(message, source, lineNo, columnNo, error) {
        // to do track
    }
    document.getElementsByTagName('head') [0].appendChild(script)
}

export {
    loadScript
}
Copy the code

Summary of webView business details working with small program

5.1 Distinguishing Environments

Wechat provides an environment variable. After loading H5, the first page can be obtained in time, but the subsequent pages need to be obtained after loading the wechat SDK. Therefore, it is recommended that you judge in the WX. Ready or Weixinjsbridgeready event. . The difference is that the former needs to be loaded jweixin js is, but there’s a pit, the pit is the h5 developers may not know the process takes time, you is an asynchronous process, they may need to call a page to load some API, is likely to go wrong at this time, so you must provide an API call queue and waiting for the mechanism. See the code above for details.

5.2 to pay

The second common problem is payment, because the small program WebView does not support directly adjust wechat pay, so basically when you need to pay, you need to come to the small program, pay and then go back. After doing the above, in the H5 piece of the call is a sentence can be done. For products with a large number of embedded H5 pages, it is better to divide them into two payment pages according to the business. First, some businesses H5 has its own perfect transaction system, and the order can be completed in H5, they only need small program payment, so we have a streamlined payment page, which can directly pull up wechat pay. In another case, the business needs the small program to provide a complete order and payment process, so you can directly enter the cashier of our small program. The picture is the basic logic in SDK. We decide which page to go to through the parameter payOnly.

Let’s take a look at the small program inside the simplified payment how to achieve, is onLoad directly after the CALL API pull wechat pay, pay according to the successful h5 returned parameters, if it is a small program page, it directly jump to the past, otherwise refresh a WebView page, and then go back.

5.3 Returns in the upper left corner

So to solve this problem, we added a return function from the top left corner. First entry is a blank page, and then enter the h5 page, so that the upper left corner will appear the back button, when the user presses the back button in the upper left corner, the page will be overloaded to the small program home page, this seemingly simple and tiny action, actually has a great influence on business, we look at two Numbers, through the data statistics, we found that the Upper left back button click rate is as high as 70% above, because the landing page is generally, by users to share time pure h5 can only return through the upper left corner, so the user habit so also in small programs, the second number, overloading to the home page, subsequent page views more than 10%, the two Numbers to improve business is pretty big. Its implementation principle is very simple, is the second time to trigger onShow processing.

Q: A login/logout synchronization problem may occur

A: Jump to the personal page and login is finished. At this time, the newly opened WebView synchronizes the login mode of both ends. Click return and go to the last WebView. This page is old, and the same is true when you log out, so you will skip the login of H5 on the home page instead of the small program login, resulting in the login mode is not synchronized. Solution: You need to return to the home page and swipe the H5 page.

Error: Relaunch to the home page directly after the individual login, which will cause the token replacement without calling the logout webView directly, unable to exit. Solution: When the segment returns from the personal page, set the WebView URL and add a parameter to it.