With the rise of mobile wave, a variety of apps emerge one after another. The rapid development of business increases the team’s requirement for development efficiency. At this time, the cost of purely using Native development technology will inevitably be higher. The low cost, high efficiency and cross-platform features of H5 were immediately taken advantage of, forming a new development mode: Hybrid App

As a Hybrid development mode, Hybrid App relies on the container (Webview) provided by Native at the bottom, and uses various front-end technologies to complete business development at the top (Vue, React and Angular are currently competing among the three), with transparency at the bottom and diversification at the top. This scenario is very conducive to front-end involvement and is ideal for rapid business iteration. So Hybrid caught on.

Everyone knows the truth, but as far as I know, there are still many people and companies that have not done a good job in Hybrid. Therefore, I will summarize my experience, hoping to help the majority of developers in the selection of technology

A current situation of Hybrid

Perhaps in the early stage, it was all PC web development. With the development of mobile Internet and the popularity of iOS and Android smart phones, a lot of business and scenes have been transferred from PC to mobile. There are front-end developers developing web pages for mobile. This packaging of early resources into Native App will increase the volume of application package. More and more businesses begin to try to use H5, which inevitably requires a place to access Native functions. In this way, Native developers who know some front-end technology may encapsulate or expose Native capabilities to JS end by themselves in the early stage, which is obviously unrealistic when there are more businesses. You need a Hybrid team to do that; Quantity is big, need rules, need norms.

Conclusion:

  1. Hybrid has high efficiency, cross-platform and low cost
  2. From a business perspective, Hybrid has no version problems and bugs can be fixed in time

Hybrid requires certain specifications when it is applied in a large number of applications, so this paper will discuss a Hybrid design knowledge.

  • What do Hybrid, Native and front-end do
  • How to design Hybrid interactive interface
  • How do I design the Header for Hybrid
  • How does Hybrid design the directory structure and implement the incremental mechanism
  • Resource caching policy, blank screen…

Native and front-end division of labor

Before designing the Hybird architecture, we need to distinguish between Native and front-end. First of all, Native provides the host environment. To make reasonable use of the capabilities provided by Native, to realize the universal Hybrid architecture and the vision of standing at the front end, I think the following core design issues need to be considered.

Interaction design

The first consideration in Hybrid architecture design is how to design the interaction between the front end and Native. If this design is not good, it will have a profound impact on subsequent development and maintenance of the front end framework. And this kind of influence is irreversible, accumulate irreversibly. Therefore, in the early stage, the front-end needs to cooperate well with Native to provide a universal interface. Such as

  1. Native UI components, Header components, and message components
  2. Address book, system, device information reading interface
  3. The mutual jump between H5 and Native. For example, how does H5 jump to a Native page? How does H5 open a new Webview and animate to jump to another H5 page

Account Information Design

The account system is important and unavoidable. Native needs to design a good and secure authentication mechanism to ensure that it is transparent enough for business developers and open the account system

Hybrid development and debugging

Functional design and coding are not really finished. Native and the front end need to discuss a set of developable and debuggable model, otherwise it is difficult to continue a lot of business development work.

IOS Debugging Tips

Android Debugging Tips:

  • Open the App Webview debugging (Webview. SetWebContentsDebuggingEnabled (true); )
  • Type chrome://inspect/# Devices to access a list of webViews that can be debugged
  • Need to climb the wall of the environment

Hybrid interaction design

Hybrid interaction is nothing more than Native calling THE JS methods of H5 page, or H5 page calling the interface provided by Native through JS. The bridge of communication is Webview. The mainstream communication methods in the industry: 1. Bridge object (timing problem, do not advocate this way); 2. Customize the Url scheme

The App itself defines a URL scheme and registers the customized URL with the dispatch center, such as Weixin :// wechat can be opened.

Check out this article if you’re not sure about Url schemes

JS to Native

Native provides some apis in each version, and a corresponding framework team at the front end encapsulates them and releases business interfaces. For example,

Sdghybrid.http.get () // Obtains data from the service server sdghybrid.http.post () // submits data to the service server sdghybrid.http.sign () // calculates the signature Sdghybrid.http.getua () // Get the UserAgentCopy the code
SDGHybridReady(function(arg){sdghybrid.http. Post ({url: arg. Baseurl + '/feedback', params:{title: 'order very slow ', content: }, success: (data) => {renderUI(data); }, fail: (err) => { console.log(err); }})})Copy the code

The front-end framework defines a global variable SDGHybrid as a bridge between the Native and the front-end. The front-end can access the Native through this object

Api interaction

Calling the Native Api interface is similar to using traditional Ajax calls to the server or Native web requests

So what we need to encapsulate is to simulate a Native request to create an Ajax-like model.

Format contract

The first step in the interaction is to design the data format. It is divided into request data format and response data format, referring to the Ajax model:

$.ajax({ type: "GET", url: "test.json", data: {username:$("#username").val(), content:$("#content").val()}, dataType: "json", success: function(data){ renderUI(data); }});Copy the code
$. Ajax (options) = > XMLHTTPRequest type (default: GET), the HTTP request method (GET | POST | DELETE |...). Url (default: current URL), the url of the request data(default: ") The data in the request is unchanged if it is a String, if it is Object, it needs to be converted to String, and if it contains Chinese characters, it will be encodeURICopy the code

Therefore, the request model in Hybrid is:

RequestHybrid ({// H5 request done by Native tagname: 'NativeRequest', // request parameter param: requestObject, // result callback: function (data) { renderUI(data); }});Copy the code

This method can form a URL, such as: SDGHybrid: / / NativeRequest? t=1545840397616&callback=Hybrid_1545840397616&param=%7B%22url%22%3A%22https%3A%2F%2Fwww.datacubr.com%2FApi%2FSearchInfo% 2FgetLawsInfo%22%2C%22params%22%3A%7B%22key%22%3A%22%22%2C%22page%22%3A1%2C%22encryption%22%3A1%7D%2C%22Hybrid_Request_M ethod%22%3A0%7D

Native Webview environment can monitor any internal resource request, determine if it is SDGHybrid, then distribute the event, and may carry parameters at the end of processing. The parameters need to be urldecode and then the resulting data is passed through the Webview to fetch the callback (Hybrid_ timestamp) in the window object.

The data is returned in a format similar to the normal interface return format

{errno: 1, message: 'App version is too old, please upgrade App version ', data: {}}Copy the code

Note here: The real data is in the data node. If errno is not 0, message is displayed.

Simple version code implementation.

// Hybrid call Native
window.SDGbrHybrid = window.SDGbrHybrid || {};
var loadURL = function (url) {
	var iframe = document.createElement('iframe');
	iframe.style.display = "none";
	iframe.style.width = '1px';
	iframe.style.height = '1px';
	iframe.src = url;
	document.body.appendChild(iframe);
	setTimeout(function () {
		iframe.remove();
	}, 100);
};

var _getHybridUrl = function (params) {
	var paramStr = ' ', url = 'SDGHybrid://';
	url += params.tagname + "? t=" + new Date().getTime();
	if (params.callback) {
		url += "&callback=" + params.callback;
		delete params.callback;
	}

	if (params.param) {
		paramStr = typeof params.param == "object" ? JSON.stringify(params.param) : params.param;
		url += "&param=" + encodeURIComponent(paramStr);
	}
	return url;
};


var requestHybrid = function (params) {
	// Generate random functions
	var tt = (new Date().getTime());
	var t = "Hybrid_" + tt;
	var tmpFn;

	if (params.callback) {
		tmpFn = params.callback;
		params.callback = t;
		window.SDGHybrid[t] = function (data) {
			tmpFn(data);
			delete window.SDGHybrid[t];
		}
	}
	loadURL(_getHybridUrl(params));
};

// Get the version information. The convention is that the APP navigator. UserAgent version contains the version information: Scheme /xx.xx.xx
var getHybridInfo = function () {
    var platform_version = {};
    var na = navigator.userAgent;
    var info = na.match(/scheme\/\d\.\d\.\d/);
 
    if (info && info[0]) {
      info = info[0].split('/');
      if (info && info.length == 2) {
        platform_version.platform = info[0];
        platform_version.version = info[1]; }}return platform_version;
};
Copy the code

Native has a Webview container for H5, and the bottom layer of framework && does not care much about the business implementation of H5, so there are few scenes of Native calling H5 in real business.

The network access Native code above (iOS for example)

typedef NS_ENUM(NSInteger){ Hybrid_Request_Method_Post = 0, Hybrid_Request_Method_Get = 1 } Hybrid_Request_Method; @interface RequestModel : NSObject @property (nonatomic, strong) NSString *url; @property (nonatomic, assign) Hybrid_Request_Method Hybrid_Request_Method; @property (nonatomic, strong) NSDictionary *params; @end @interface HybridRequest : NSObject + (void)requestWithNative:(RequestModel *)requestModel hybridRequestSuccess:(void (^)(id responseObject))success hybridRequestfail:(void (^)(void))fail; + (void)requestWithNative:(RequestModel *)requestModel hybridRequestSuccess:(void (^)(id responseObject))success HybridRequestfail (void (^) (void)) fail {/ / handle the situation without all requests NSAssert (requestModel | | success | | fail, @ "Something goes wrong"); NSString *url = requestModel.url; NSDictionary *params = requestModel.params; if (requestModel.Hybrid_Request_Method == Hybrid_Request_Method_Get) { [AFNetPackage getJSONWithUrl:url parameters:params success:^(id responseObject) { success(responseObject); } fail:^{ fail(); }]; } else if (requestModel.Hybrid_Request_Method == Hybrid_Request_Method_Post) { [AFNetPackage postJSONWithUrl:url parameters:params success:^(id responseObject) { success(responseObject); } fail:^{ fail(); }]; }}Copy the code

Common Interaction apis

Good interaction design is the first step, and there are some apis that must be used in real business development scenarios.

jump

Jump is one of Hybrid’s required apis. For the front end, there are the following:

  • In-page jump, unrelated to Hybrid
  • H5 Jumps to Native interface
  • H5 New Webview jump to H5 page, general animation switch page

If animation is used, it can be divided into forward and backward according to business. Forward and backword are specified as follows. First, H5 jumps to Native a page

//H5 jump Native page //=>SDGHybrid://forward? t=1446297487682&param=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A2%7D requestHybrid({ Tagname: 'forward', param: {// topage: 'home', // jump mode, H5 jump type: 'Native ', // other parameters datA2:2}});Copy the code

H5 page to Native a page

//=>SDGHybrid://forward? t=1446297653344&param=%7B%22topage%22%253A%22Goods%252Fdetail%20%20%22%252C%22type%22%253A%22h2n%22%252C%22id%22%253A201 51031%7D requestHybrid({tagname: 'forward', param: {topage: 'Goods/detail', // jump, H5 jump Native type: 'native', // Other parameters ID: 20151031}});Copy the code

H5 new open Webview to jump to H5

RequestHybrid ({tagname: 'forward', param: {// To go to the page, first find the goods channel, then locate the detail module topage: Type: 'Webview ', // other parameters id: 20151031}});Copy the code

Back is the same as forward, and may have an animateType parameter that determines how the page will be animated when switching. In real use, global encapsulation may omit the tagName detail.

Header component design

Native is “slow” every time it changes, so something like the Header is needed.

  1. Mainstream containers do this, such as wechat, mobile Baidu, Ctrip
  2. Without the Header, the App will fall into suspended animation if there is a network error or a blank screen

PS: If Native opens H5, if there is no response in 300ms, loading component is required to avoid a blank screen, because H5 App itself has Header component. In terms of the front-end framework layer, it is necessary to ensure that the business code is consistent, and all differences need to be transparent in the framework layer. To put it simply, the design of the Header needs to follow:

  • The H5 Header component is consistent with the call layer interface provided by Native
  • The front-end framework layer decides whether to use H5 Header components or Native Header components according to the environment

In general, the Header component needs to do the following:

  1. The left and right sides of the Header can be configured to display text or ICONS (the Header is required to realize the mainstream icon and can also be controlled by the service), and the callback needs to be controlled

  2. The title of the Header can be set to a single title, main title, or subtitle type, and can be set to lefticon and righticon (icon centered).

  3. Special configurations, such as the tag class Header

Therefore, on the front end, the Header can be used as follows (tagname cannot be repeated) :

 //Native and the front-end framework make a default callback for a particular tagname. If no callback is registered, or if clicking callback returns nothing, the default method is executed
 // History. Back is executed by default. If it is not retractable, you return to the specified URL
 // Home returns the specified URL by default. Native returns the large home page by default
  this.header.set({
      left: [{// If a value field is present, icon is not used by default
          tagname: 'back'.value: 'back'.// If lefticon or righticon is set, icon is displayed
          // Native provides icon mapping for common ICONS. If you cannot find it, you will go to the directory dedicated to the current business channel to obtain the icon
          lefticon: 'back'.callback: function () {}}].right: [{// The default icon is tagname
        tagname: 'search'.callback: function () {}},// Customize the icon
      {
        tagname: 'me'.// Will go to the Hotel channel to store the static header icon resource directory to search for the icon, if not, use the default icon
        icon: 'hotel/me.png'.callback: function () {}}].title: 'title'.// Display main title, sub-title scenes
    title: ['title'.'subtitle'].// Customize title
    title: {
      value: 'title'.// The icon to the right of the title
      righticon: 'down'.// You can also set lefticon
      // The title type, which is empty by default, requires special treatment
      //type: 'tabs',
      // The callback when the title is clicked, which defaults to empty
      callback: function () {}}});Copy the code

Since there is typically only one button to the left of the Header, the object can be of this form:

this.header.set({
  back: function () {},title: ' '
});
// syntax sugar =>
this.header.set({
    left: [{
        tagname: 'back'.callback: function(){}}].title: ' '});Copy the code

To complete the implementation of Native end, two new interfaces will be added here, including registering events with Native and deregistering events:

var registerHybridCallback = function (ns, name, callback) {
  if(!window.Hybrid[ns]) window.Hybrid[ns] = {};
  window.Hybrid[ns][name] = callback;
};

var unRegisterHybridCallback = function (ns) {
  if(!window.Hybrid[ns]) return;
  delete window.Hybrid[ns];
};
Copy the code

Native Header component implementation:

define([], function () {
    'use strict';

    return _.inherit({

        propertys: function () {

            this.left = [];
            this.right = [];
            this.title = {};
            this.view = null;

            this.hybridEventFlag = 'Header_Event';

        },

        // All updates
        set: function (opts) {
            if(! opts)return;

            var left = [];
            var right = [];
            var title = {};
            var tmp = {};

            // Syntax sugar fit
            if (opts.back) {
                tmp = { tagname: 'back' };
                if (typeof opts.back == 'string') tmp.value = opts.back;
                else if (typeof opts.back == 'function') tmp.callback = opts.back;
                else if (typeof opts.back == 'object') _.extend(tmp, opts.back);
                left.push(tmp);
            } else {
                if (opts.left) left = opts.left;
            }

            // The right button must maintain data consistency
            if (typeof opts.right == 'object' && opts.right.length) right = opts.right

            if (typeof opts.title == 'string') {
                title.title = opts.title;
            } else if (_.isArray(opts.title) && opts.title.length > 1) {
                title.title = opts.title[0];
                title.subtitle = opts.title[1];
            } else if (typeof opts.title == 'object') {
                _.extend(title, opts.title);
            }

            this.left = left;
            this.right = right;
            this.title = title;
            this.view = opts.view;

            this.registerEvents();

            _.requestHybrid({
                tagname: 'updateheader'.param: {
                    left: this.left,
                    right: this.right,
                    title: this.title
                }
            });

        },

        // Register events to store them locally
        registerEvents: function () {
            _.unRegisterHybridCallback(this.hybridEventFlag);
            this._addEvent(this.left);
            this._addEvent(this.right);
            this._addEvent(this.title);
        },

        _addEvent: function (data) {
            if(! _.isArray(data)) data = [data];var i, len, tmp, fn, tagname;
            var t = 'header_' + (new Date().getTime());

            for (i = 0, len = data.length; i < len; i++) {
                tmp = data[i];
                tagname = tmp.tagname || ' ';
                if (tmp.callback) {
                    fn = $.proxy(tmp.callback, this.view);
                    tmp.callback = t;
                    _.registerHeaderCallback(this.hybridEventFlag, t + '_'+ tagname, fn); }}},/ / display the header
        show: function () {
            _.requestHybrid({
                tagname: 'showheader'
            });
        },

        / / hide the header
        hide: function () {
            _.requestHybrid({
                tagname: 'hideheader'.param: {
                    animate: true}}); },// Update only the title, do not reset the event, do not change the rest of the header, only the simplest header can do this
        update: function (title) {
            _.requestHybrid({
                tagname: 'updateheadertitle'.param: {
                    title: 'aaaaa'}}); },initialize: function () {
            this.propertys(); }}); });Copy the code

Request class

While GET requests can bypass the cross-domain problem with jSONP, POST requests are a stumbling block. For security reasons, the server will set cORS for only a few domain names. Hybrid embedded static resources may be read through local files, so CORS will not work. Another problem is to prevent crawlers from acquiring data. Since Native has made security Settings for the network (authentication, anti-packet capture, etc.), the network request of H5 is completed by Native. Maybe some people say H5’s network request is safe to let Native go? I can keep crawling your Dom nodes. This is tool number one for anti-crawlers. For more anti-crawler strategies, check out my article Web anti-crawler solutions

This usage scenario is consistent with the Header component. The front-end framework layer must be transparent to the business, and the business does not actually care whether the network request is issued by Native or browser.

HybridGet = function (url, param, callback) {}; HybridPost =function (url, param, callback) {};Copy the code

The real business scenario will encapsulate it into the data request module and make adaptation at the bottom level. Ajax request will be used under the H5 site and sent by proxy when embedded in Native. The convention with Native is

requestHybrid({
  tagname: 'NativeRequest'.param: {
    url: arg.Api + "SearchInfo/getLawsInfo".params: requestparams,
    Hybrid_Request_Method: 0.encryption: 1
  },
  callback: function (data) { renderUI(data); }});Copy the code

NativeUI components are commonly used

In general, Native usually provides common UI, such as loading layer loading, message box Toast

var HybridUI = {};
HybridUI.showLoading();
/ / = >
requestHybrid({
    tagname: 'showLoading'
});

HybridUI.showToast({
    title: '111'.// Automatically close the prompt box after a few seconds, -1 needs to be clicked to close
    hidesec: 3.// A callback when the pop-up layer is closed
    callback: function () {}});/ / = >
requestHybrid({
    tagname: 'showToast'.param: {
        title: '111'.hidesec: 3.callback: function () {}}});Copy the code

It is not easy to connect the Native UI with the front-end UI, so in the real business development process, only a few key Native UI will be used.

Design of account system

Web pages run in Webview, account login is determined by whether the key cookie is carried (the validity of the key cannot be guaranteed). Since Native does not pay attention to business implementation, each loading may be the result of successful login jump back. Therefore, each loading needs to pay attention to the change of key cookie to achieve the consistency of login-state data.

  • Native proxy is used as the request interface. If there is no login, the Native layer invokes the login page
  • The direct approach uses the Ajax request interface and invokes the login page at the bottom if there is no login (H5)
The login box is closed regardless of success. Parameters include: SUCCESS Callback for successful login ERROR callback url for failed login If SUCCESS is not set, or success does not return true after execution, this URL is skipped by default */
HybridUI.Login = function (opts) {
    / /...
};
/ / = >
requestHybrid({
    tagname: 'login'.param: {
       success: function () {},error: function () {},url: '... '}});// The parameters are consistent with the login interface
HybridUI.logout = function () {
	/ /...
};
Copy the code

When designing the Hybrid layer, the interface should be willing to obtain the user account information stored at the Native end through the interface for codes in the Hybrid environment. In a traditional web environment, you can get online account information through the interface, then store non-sensitive information in LocalStorage, and then read data from LocalStorage into memory each time the page loads (such as Vuex in vue.js framework, Redux in react.js)

Hybrid Resource Management

Hybrid resources require incremental updates and easy splitting, so a Hybrid resource structure looks something like this

Suppose there are two lines of business: mall and shopping cart

WebApp │ -mall │ -cart │ index. If it is not a single page application will have more than one entry │ │ main. Js/packaging/business all js resources │ │ │ └ ─ static / / static style resources │ ├ ─ CSS │ ├ ─ hybrid / / store business custom class Native Header icon │ └ ─ images ├─ ├─ CSS │ ├─ ├─ CSS ├─Copy the code

Incremental updating

After each service is developed, it needs to be deployed online on the package distribution platform, and then a version number will be generated.

Channel Version md5
Mall 1.0.1 12233000ww
Cart 1.1.2 28211122wt2

When the Native App starts, it will request an interface from the server, and the interface will return a JSON string containing the version number and MD5 information of each H5 line of business contained in the App.

After getting the JSON and comparing it with the version information stored locally in the App, the user will request the corresponding interface if any change is found, and the interface will return the corresponding MD5 file. Native gets it and then decompresses and replaces it.

After all the replacement is complete, the version information of the resource requested by the interface is saved and replaced to the Native.

Because each resource has a version number, if there is a problem with a version on the line, you can roll back to the stable version based on the corresponding stable version number.

Some piecemeal solutions

  1. Static straight out

The concept of “straight out” is not strange to the front end students. In order to optimize the first screen experience, most mainstream pages will be rendered by NodeJs on the server side after pulling the first screen data, and then generate an Html file containing the first screen data, which can solve the problem of content transfer when displaying the first screen. Of course, this “straight out” approach also brings a problem, the server needs to pull the first screen data, which means that the server processing time is increased. However, because Html is now published to the CDN, WebView is directly fetched from the CDN, this time does not affect the user. Mobile Q has an automatic build system called Vnues. After the product manager modifies and releases data, the Vnues system can start the build task with one click. The Vnues system automatically synchronizes the latest code and data, generates new Html containing the first screen, and publishes it to the CDN.

We can do a similar thing, automatically synchronize the latest code and data, then generate new Html with the first screen, and publish it to the CDN

  1. Offline pushing

After the page is posted to the CDN, the WebView needs to make a network request to pull it. When the user is on a weak network or a poor network speed, the load time may be long. Therefore, we pull the resources of the page to the local area in advance through offline pre-push. When the user loads the resources, it is equivalent to loading from the local area. Even without the network, the first screen page can be displayed. This is also known as the offline package. Hand Q generated offline packets using 7 z, off-line package server will be the new package with the business at the same time the corresponding historical offline package BsDiff binary difference, generate incremental package, further reduce the bandwidth of the download the offline package cost, download the consumed flow from a complete offline package (253 KB) reduced to a delta packages (3 KB).

Mp.weixin.qq.com/s?__biz=MzU…

  1. Intercept load

In fact, in a highly customized WAP page scenario, we have strict control over the types of pages that can appear in a WebView. We can use content control to avoid external page jumps in WAP pages, and we can also use the corresponding proxy method of WebView to prohibit the types of jumps we do not want to appear, or use double protection to ensure that the current WebView container will only appear our customized content. Since the type of WAP page is limited, it naturally occurs that the same type of page is mostly generated by the front end using templates, and the HTML, CSS, JS resources used by the page are likely to be the same, or a limited number of copies, and it becomes feasible to package them directly with the client locally. When the corresponding URL is loaded, local resources are directly loaded. For network requests in webView, the client can also take over, for example in your Hybrid framework, register an interface for the front-end to initiate network requests. All network requests on waP pages are sent through this interface. In this way, the client can do a lot of things, for example, NSURLProtocol can not intercept the network request initiated by WKWebview, using Hybrid mode to send to the client, can achieve the corresponding interception. Based on the above scheme, the complete presentation flow of our WAP page looks like this: The client loads a URL in the WebView, judges that it meets the rules, and loads the local template HTML. The internal implementation of this page is to initiate a network request to obtain the specific page content through the network request interface provided by the client, and obtain the filled data to complete the display.

NSURLProtocol allows you to redefine the behavior of Apple’s URL Loading System, which has classes for handling URL requests such as NSURL, NSURLRequest, NSURLConnection and NSURLSession. When URL Loading System uses NSURLRequest to obtain resources, it creates an instance of a subclass of NSURLProtocol. You should not instantiate NSURLProtocol directly. NSURLProtocol looks like a protocol. But this is actually a class, and you have to use a subclass of that class, and you need to be registered.

  1. WKWebView Network request interception

Method 1 (Native side) : The Native WKWebView executes the network request in a process independent of the APP process, and the request data does not go through the main process. Therefore, it is impossible to intercept the request using NSURLProtocol directly on WKWebView.

However, the offline package mechanism of mPaas strongly relies on network interception, so based on this, mPaas uses the hidden API of WKWebview to register and intercept network requests to meet the requirements of offline package business scenarios. The reference code is as follows:

[WKBrowsingContextController registerSchemeForCustomProtocol:@"https"]
Copy the code

However, for performance reasons, WKWebView’s network request will remove the body of the request when transmitting data to the main process, resulting in the loss of the body parameter of the request after interception.

In the offline package scenario, since the page resources do not require body data, the offline package can be used normally without impact. However, other POST requests within the H5 page will lose the data parameter.

To solve the problem of missing POST parameters, mPaas hooks XMLHTTPRequest objects in the JS context by injecting code into THE JS.

WKWebView’s messageHandler mechanism sends the content to the main process, stores the corresponding HTTPBody and then notifies the JS side to continue the request. After the network request is sent to the main process, A POST request is processed by adding the corresponding HttpBody to the POST request. The overall process can be referred to as follows:The above mechanism not only meets the resource interception demands of offline packages, but also solves the problem of post request body loss. However, there are still some problems in some scenarios that need to be adapted by developers.

Method 2 (on the JS side) : Proxy the network request information to the client by hook mode of AJAX request. If you can get the post request information in WKWebView, the rest is not a problem. An AJAX hook implementation can be seen in this Repo.