Applets and ordinary web development differences

The main development language of small program is JavaScript, and the development of small program is very similar to the common web development. Moving from web development to applets is not expensive for front-end developers, but there are a few differences.

The web development rendering thread and script thread are mutually exclusive, which is why long script runs can cause the page to become unresponsive, whereas in applets the two are kept separate and run on separate threads. Web developers can use the DOM API exposed by various browsers for DOM selection and manipulation. As mentioned above, the logic layer of the applet is separate from the render layer. The logic layer runs in JSCore and does not have a full browser object, so it lacks the DOM API and BOM API. This difference results in some libraries familiar to front-end development, such as jQuery, Zepto, etc., not running in small programs. The JSCore environment is also different from the NodeJS environment, so some NPM packages will not run in applets.

Web developers need to face a variety of browsers, IE, Chrome, QQ browsers on the PC side, Safari, Chrome and various WebViews in iOS and Android system on the mobile side. In the process of small program development, we need to face the wechat client of the two major operating systems iOS and Android, as well as the small program developer tools for auxiliary development. The three running environments in the small program are also different

Operating limits

For security reasons, dynamic execution of JS code is not supported in applets, that is:

JS code execution using eval is not supported. New Function creation is not supported

Web developers only need to use a browser and some auxiliary tools or editors to develop web pages. The development of small program is different, need to apply for small program account, install small program developer tools, configuration projects and so on process can be completed.

Applets running mechanism

Small program start

There are two cases of small program start, one is “cold start”, one is “hot start”.

Hot start: if the user has opened a small program, and then in a certain period of time to open the small program again, at this time do not need to restart, just switch the background state of the small program to the foreground, this process is hot start;

Cold start: the user opens it for the first time or opens it again after the mini program is actively destroyed by wechat. In this case, the mini program needs to be reloaded and started, that is, cold start. Applets have no concept of restart.

Foreground/background status

When users click the capsule button in the upper right corner to close the mini program, or press the device Home button to leave wechat, the mini program is not directly destroyed, but entered the background state;

When the user enters wechat again or opens the small program again, the small program will enter the foreground from the background.

Small program destruction

It should be noted that only when the small program enters the background for a certain amount of time, or the system resource usage is too high, it will be truly destroyed.

When the mini program enters the background, the client will maintain the running state for a period of time. After a certain period of time (currently 5 minutes), the mini program will be actively destroyed by wechat. When small programs occupy too much system resources, they may be destroyed by the system or recycled by the wechat client. On iOS, when the wechat client receives two or more system memory alarms in a certain period of time (currently 5 seconds), it will actively destroy the small program and prompt the user “This small program may cause the slow response of wechat and be terminated”. It is recommended that applets use wx.onMemoryWarning to listen for memory alarm events and clean up memory if necessary.

Applets update mechanism

Updated when not started

After the developer releases a new version of the applet in the admin background, if a user has a local historical version of the applet, it may still open the old version. The wechat client will have several times to check whether there is an updated version of the locally cached applets, and if there is, it will silently update to the new version. In general, developers can’t immediately affect all live network users after releasing a new version in the background, but in the worst case, they can send the new version information to users within 24 hours after the release. Users will update the latest version the next time they open it.

Update at startup

Each time a small program is cold started, the system checks whether there is an updated version. If a new version is found, the system asynchronously downloads the code package of the new version and starts the program with the local package of the client. That is, the small program of the new version will be applied only after the next cold start.

If you need to apply the latest version immediately, you can use the Wx.getupDatemanager API to do so.

Difficulties and emphases in the development of small program probe

  • Unable to intercept/listen on requests directly

    Wechat requests are uniformly completed through wechat API. The request module has been encapsulated by wechat, and the running environment of the small program is not a browser object, so it is not easily rewritten and encapsulated like web applications.

  • Ensure the monitoring compatibility of the three operating environments

    • On Android, js running environment is X5 kernel
    • On iOS, the JAVASCRIPT environment is JavaScriptCore
    • Development tools, js runtime environment is NWJS (Chrome kernel)
  • User behavior cannot be monitored directly

    The applet logic layer cannot capture the DOM and BOM at runtime, cannot use the DOM event API like traditional web development, and cannot listen for events globally.

  • The SDK should be lightweight

    The size of a small program package is limited. The maximum size of a single package is 2M, and in the case of subcontracting, it cannot exceed 8M. Therefore, the SDK should be lightweight

  • Large amount of data collection to minimize performance loss

    You need to design a cache pool and formulate a reporting strategy

  • No impact on business (basic requirements)

Probe cache pool and reporting policy

The data collected by the probe can be divided into two types: one is basic data and the other is event characteristic data. Feature data will be covered in key events below

Basic data

Basic data is the data contained in each reported log. Some of these are retrieved after the probe is initialized and do not change. The service-related data is configured by the user, and the rest is generated by the probe or obtained by calling the Wx. getSystemInfoSync API

The other part will change with user behavior, such as page switching, login, or environmental changes, such as network changes.

Network data is obtained by wx.getNetworkType and wx.onNetworkStatusChange as described in the critical events section below

Event characteristic data

Report the strategy

The probe caches corresponding logs to prevent data loss when the mini program Storage is cleared.

Data reporting Once reported, the system clears the cached logs to prevent the accumulation of cached logs caused by the report failure

Probe critical event capture

Critical Event type

Rewrite the App config

“OnShow “, “onHide”, “onError” and” onLaunch “should be rewritten in config for App classCache hook function Attach hooks to methods on config and add default lifecycle callbacks for unconfigured life cycles in configConfig contains the “onShow”, “onHide”, “onError”, and “onLaunch” lifecycle functions, and then calls the hook function after executing the original method

  • Start event

    Applet start, get the applet start scenario value. Rewrite App config, triggered by onLaunch. Get the values of scene, page path, page search, and page title from the page path and __wxConfig object.

  • Exit background (pause)

    The applets switch to the background and rewrite the App config, triggered by onHide

  • Cut back to the front desk (resume)

    Rewrite App config, trigger via onShow (except the first onShow trigger)

  • Exception handling

    Since the global listening method of the applet wx.onError is only supported in 2.1.2 and above, it is necessary to rewrite the App config and trigger through onError for compatibility.

Rewrite the Page config

This part is similar to rewriting App Config, mainly depending on the event acquisition

  • Page stay (page_stay)

    Triggered when onHide and onUnload get how long the user has been on the current page. If onHide is triggered due to sharing and forwarding pages, no page stay is reported.

  • Page Switching (Page)

    Trigger every time switch page (onShow), get the current page path, parameters,title

  • The first rendering time of the page

    The time it takes for a Page to render the first time the Page is opened or destroyed, override the Page config, triggered by onReady.

  • Page Share

    Triggered when the user shares a forwarding Page, by overwriting the Page config,onShareAppMessage. Since page sharing triggers the onShow and onHide life cycles of the current page, use the variable isPause to check for data accuracy.

User behavior capture

Since user Behavior is always related to events, the applet cannot directly listen to DOM events. The solution adopted here is to rewrite the config of App, Page, Component and Behavior to determine whether the attribute on the config is a function and whether the parameter of the function is an event source. If so, the applet can not directly listen to DOM events Event source, indicating that the function is now associated with user behavior

For Component and Behavior, just hook the methods on config.method Determines whether the current is an event function by whether the parameter has the currentTarget attribute

Click events that do not have custom event attributes are regarded as click events, and those that do exist are regarded as custom events

  • Click events

    Since the logic layer of the appllet is separate from the rendering layer, the logic layer runs in JSCore, and there is no complete browser object, lack of DOM and BOM related API, and cannot set the global click event listening method on the body.

    To listen for events, the probe overwrites the Page , Component, and Behavior config to distinguish all attributes on config, determine whether the current attribute is a function, and when the function fires, whether the parameter has a currentTargey attribute to distinguish whether the parameter is an event object, so as to listen for page events. For tap and Longpress events, the probe identifies them as click events.

    type The trigger condition
    tap Touch your finger and leave immediately
    longpress If the event callback function is specified and the event is triggered, the TAP event will not be triggered

  • Custom Events (log)

    Calling the custom event reporting method exposed by the probe directly within the event function results in too much coupling between the business code and the probe.

    By adding custom attributes to the applet tag bound to the event, the probe and event listener can report custom events.

    Since the event source when the event is triggered has been encapsulated inside wechat, the acquisition of custom attribute only supports the acquisition of data attribute data-xxx at present. Therefore, when it is not manually called, data-event and data-log can be added to the small program label that triggers the click event to add low coupling custom event code.

Rewrite wX objects to implement API event capture

  • API events (API)

    Override wx object, rewrite wx.request method config, obtain API (data interface address), API_method (data interface request mode), API_STATUS (data interface response status code), API_response_time (data interface response time(MS)) , API_response_content_length (byte)

    The applets’ API is basically mounted on the global object Wx. If you modify the attributes of wX directly, an error will be reported and the assignment will fail.

    thirdScriptError 
     sdk uncaught third Error 
     Cannot set property request of# <Object> which has only a getter 
     TypeError: Cannot set property request of# <Object> which has only a getter
    Copy the code

    alternative

    Use Object. The getOwnPropertyDescriptors access to attributes of the Object wx descriptors, WeChat Object to assign a value to an empty Object, circulation property descriptors, judge whether the current key descriptor for the request, modification request property descriptors, and other conditions using the Object The.defineProperty method defines attributes

    Because of the for loop gain is less than in Symbol type of key, in order to compatible with wx Object in the future introduction of Symbol as wx Object keys, use Object. GetOwnPropertySymbols method to get the Symbol of property descriptors, custom property again

    This piece of code is too much, not good screenshots, directly on the code

    / / rewrite wx. Request
      rewriteWxRequest() {
        const that = this;
        
        // return 
        // Override the wx object start
        const descriptorObj = Object.getOwnPropertyDescriptors(wx);
        let oldWx = this.oldWx = wx;
        wx = {};
        for (let i in descriptorObj) {
          if (i === 'request') {
            const desObj = descriptorObj[i];
            let oldGet = desObj.get;
            desObj.get = function(. args){
              let oldRequest = oldGet.apply(this, args);
              return function(params){
                const {
                  url,
                  method = 'GET',
                  success = function(){},
          
                } = params;
                // Check if the API request is in the ignored URL
                const ignoreUrls = that.conf.api_ignore_urls;
                if (url && isIgnoreApi(url, ignoreUrls)) {
                  return oldRequest.call(this, params);
                }
                // Handle custom API URL trim func
                let apiTrimUrl = null;
                if (that.conf.api_property_cb) {
                  try {
                    apiTrimUrl = that.conf.api_property_cb(url) || null;
                  } catch (e) {
                    apiTrimUrl = null; }}const timeStamp = Date.now();
                const apiData = {
                  api: apiTrimUrl || cutAPIUrl(url),
                  api_method: method.toUpperCase(),
                  api_status: undefined.api_response_time: 0.api_response_content_length: 0,}return oldRequest.call(this, {
                  ...params,
                  success (res) { // Successful callback
                    try {
                      const {
                        data,
                        statusCode
                      } = res
                      apiData.api_status = statusCode;
                      apiData.api_response_time = Date.now() - timeStamp;
                      if (data) {
                        let AB = {};
                        if(typeof ArrayBuffer! = =undefined) {
                          AB = ArrayBuffer;
                        }
                        if (data instanceofAB && data.byteLength ! = =undefined) {
                          apiData.api_response_content_length = data.byteLength;
                        } else {
                          if (typeof data === 'string') {
                            apiData.api_response_content_length = data.length || 0;
                          } else {
                            apiData.api_response_content_length = JSON.stringify(data).length || 0; }}}else {
                        apiData.api_response_content_length = 0;
                      }
                      that.reportApi(apiData)
                    } catch (e) {
                      that.consoleErr(e);
                    }
                    success.call(this, res); }})}}Object.defineProperty(wx, i, desObj)
          } else {
            Object.defineProperty(wx, i, descriptorObj[i])
          }
        }
        // Compatibility for wechat's future introduction of Symbol, to prevent the loss of Symbol keys
        if (Object.getOwnPropertySymbols && typeof Object.getOwnPropertySymbols === 'function') {
          Object.getOwnPropertySymbols(descriptorObj).forEach(val= > {
            Object.defineProperty(wx, val, descriptorObj[val])
          })
        }
        // Override the wx object end
      }
    Copy the code

To optimize

  • Error exception cannot locate source code
  • Currently, only native framework and MPvue framework are supported, and third-party plug-ins of wechat are not applicable
  • Custom events cannot be added to arbitrary tags like web probes