In recent years, with the rapid development of mobile Internet, the demand for App continues to soar. Thanks to the advantages of short RESEARCH and development cycle and fast platform of H5, making App is more or less inseparable from front-end development. Some companies use H5 development as a supplement and use Native to develop their main business modules, while others use H5 code as the main code. Native mainly provides some native capabilities for front-end invocation. There are also many such frameworks on the market. We do front-end development, should not be tightly limited to the front-end code itself, understanding the context can improve our own professional level, after all, the front-end framework principle can only be said to the front-end students, and boundary knowledge can help us cross the boundary to cross (B) flow. For example, in the requirement review, some questions or suggestions can be given to the product or related development, or the feasibility of a certain scheme, which should be professional performance!

Before we get started, ask three questions that we can take with us through the rest of the study:

  1. The browser kernel and the V8 engine are often in our sights. What are the components of the browser kernel?

  2. Why can’t wechat applet access window and Document object?

  3. What are the advantages, disadvantages and implementation methods of Hybird application, applets, RN and other cross-end solutions?

Throughout all the cross-platform technologies, most of them are implemented based on THE JS language, and the implementation of JS depends on the JS engine. So the first place to learn about cross-platform development should be JS Engine

Browser kernel

The browser kernel is the most core program that supports the browser to run, divided into two parts, one is the rendering engine, the other is the JS engine. The following table shows the differences in rendering cores and JS engines between different browsers:

The browser The kernel JS engine
IE/Edge Trident/EdgeHTML Jscript(< IE9 ) Chakra
Safari Webkit/Webkit2 JavaScriptCore
Chrome Chromium(Webkit)/Blink V8
FireFox Gecko SpiderMonkey.TraceMonkey.JaegerMonkey
  • Internet Explorer /Edge: Microsoft’s Internet Explorer browser update to Internet Explorer 10, with the launch of the Windows 10 system, migrated to the new browser Edge. The rendering engine uses the new EdgeHTML kernel, in addition to the JS engine, which has been used since IE9’s previous Chakra engine. Extended and optimized support for new technologies, so it is considered a new kernel)

  • Safari: Safari has been an Apple product browser since 2003, using Apple’s Webkit engine. The Webkit engine includes the WebCore typesetting engine and the JavaScriptCore parsing engine, which are derived from the KHTML and KJS engines of KDE. Webkit2, which was released in 2010, implements abstract drawing of components, improves reuse efficiency of components, and provides cleaner web rendering and more efficient rendering. Webkit is also the name of the Mac OS X version of the engine framework used in Safari, Dashboard, and Mail.

  • Chrome: When it comes to Chrome, the common perception is that it uses the Webkit kernel, which is not entirely accurate. Chrome was released in 2008 and uses Chromium as the rendering kernel. It is a fork from Webkit, but it makes Webkit more organized, more readable and more efficient. In 2013, due to the conflict between Webkit2 and Chromium in sandbox design, Google and Opera jointly developed and released Blink engine, gradually separated from the influence of Webkit. So think of it this way: Chromium extended from Webkit to Webkit2, and then Chrome switched to Blink engine. In addition, Chrome JS engine uses the V8 engine, should be the most famous and excellent open source JS engine, the famous Node.js is the use of V8 as the underlying architecture.

  • Firefox: Gecko, the core of Firefox, is also an open source engine, and any programmer can provide extensions and suggestions. Firefox’s JS engine has gone through SpiderMonkey, TraceMonkey, and now JaegerMonkey. JaegerMonkey borrowings some of its technology from V8, JSCore, and Webkit.

WebKit

Let’s take WebKit as an example to look at the internal architecture of the browser kernel:

To put it simply, WebKit is a page rendering and logic processing engine. The front-end code HTML, CSS, JavaScript as input, after processing by WebKit, the output becomes a Web page that we can see and operate. As you can see from the figure above, WebKit consists of four parts boxed in. The main ones are WebCore and JSCore (or other JS engines). WebKit Embedding API is responsible for the interaction between browser UI and WebKit. WebKit Ports make it more convenient to transplant WebKit to various operating systems and platforms, and provide some interfaces to call Native Library. For example, at the rendering level, Safari is handled by CoreGraphics in iOS system. On Android, Webkit is handed over to Skia.

WebCore

Up to now,WebKit has many branches and major manufacturers have also carried out a lot of optimization transformation, but WebCore is the part shared by all WebKit. WebCore is the most coded part of WebKit and the most core rendering engine in WebKit.

First, the browser locates a bunch of HTML, CSS, and JS resource files through the URL, and feeds the resource files to WebCore through the loader (the implementation of this loader is also complicated, so I won’t go into details here). Then the HTML Parser will parse the HTML into a DOM tree, and the CSS Parser will parse the CSS into a CSSOM tree. Finally, the two trees are combined to generate the final required rendering tree. After layout, the rendering tree is rendered and output to the screen through the rendering interface of specific WebKit Ports, which becomes the final Web page presented to the user.

JSCore

The JS engine’s job is to interpret and execute JS scripts. As can be seen from the above rendering flow chart, there is a correlation between JS and DOM tree, because the main function of JS script in browser is to manipulate and interact with DOM tree. Again, let’s take a look at how it works:

It is mainly divided into three parts: Lexer, Parser, and LLInt.

Lexical analysis is easy to understand. It is the process of breaking a piece of source code we have written into Token sequences. This process is also called lexical segmentation. At JSCore, lexical analysis is done by lexers (some compilers or interpreters call word segmentation Scanner).

Parser parses the token sequences generated after Lexer analysis and generates a corresponding abstract syntax tree (AST). What does the tree look like? One site recommended here is esprima Parser, which generates the AST we need immediately by typing in JS statements.

Interpreted execution (LLInt) JS source code goes through two steps — lexical analysis and syntax analysis — to become bytecode, which is essentially what any programming language goes through — compilation.

Cross-platform history

  • In 2009, PhoneGap0.6 was released, the first stable release in PhoneGap’s history
  • In 2010, Cordova was announced and Hybird was in full swing
  • In 2013, after the release of iOS 7, JSCore was offered to developers as a system-level Framework by Apple, which opened up even more possibilities
  • React Native was born in 2015
  • In 2016, Weex was born

Common mobile development patterns

Hybrid development

The first step to solve the cross-end problem is to solve the communication between the two languages, such as OC/Swift and JS communication on the iOS side and Java and JS communication on the Android side. Let’s take an example of how communication is done on iOS

In the iOS JSCore

  1. JSVirtualMachine — JavaScript virtual machine

    JavaScriptCore provides a class named JSVirtualMachine. As the name implies, this class can be understood as a JS virtual machine. In Native, you can create as many JSVirtualMachine objects as you like. The JSViretualMachine objects are independent of each other. They cannot share or pass data between them. They can execute unrelated JS tasks in parallel.

  2. JSContext — JavaScript runtime environment

    JSContext context object can be understood as the running environment of JS. The same JSVirtualMachine object can be associated with multiple JSContext objects, and after each refresh operation in the WebView, The JS runtime environment for this WebView is a different JSContext object. Its function is to execute JS code and transfer data between Native and JS.

  3. JSValue – JavaScript value object

    Although JavaScript and Objective-C are both object-oriented languages, their implementation mechanisms are completely different. OC is class-based, JS is prototype-based, and there are great differences between their data types. Therefore, in order to carry out barrier-free data transfer between Native and JS, an intermediate object is needed to bridge, and this object is JSValue.

  4. JSExport

    JSExport is a protocol that Native classes that comply with this resolution can convert methods and properties into JS interfaces for JS to call.

Several modes of interaction

  1. Intercepting urls (for UIWebView and WKWebView)

  2. JavaScriptCore (only for UIWebView, iOS7+)

  3. WKScriptMessageHandler (WKWebView, iOS8+ only)

  4. WebViewJavascriptBridge (for UIWebView and WKWebView, a third-party framework)

Intercept the url

[1]. Make agreements with native developers, for example, zmlean://scan indicates to start two-dimensional code scanning, zmlean://location indicates to obtain location.

[2]. Implement UIWebView agent shouldStartLoadWithRequest: navigationType: If it is a protocol defined in step <1>, the corresponding native code is executed and false is returned; otherwise, true is returned to continue loading the original URL.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ if ([request.URL.absoluteString hasPrefix:@"jxaction://scan"]) { // Call native scan qr code return NO; } return YES; }Copy the code

After the scan qr code, need to scan the results back to the web page, direct call UIWebView stringByEvaluatingJavaScriptFromString: Method, or WKWebView evaluateJavaScript: completionHandler: method.

[the self. The webView stringByEvaluatingJavaScriptFromString: @ "scanResult (' I am scan results ~ ')");Copy the code
JavaScriptCore

The method one Web call native is only good for simple calls if you want to pass parameters, although it is possible to concatenate the URL, such as jxAction ://scan? Method = AAA, but we need to split and parse the string ourselves, and special characters need to be encoded. JavaScriptCore is provided in iOS7, which can more elegantly realize THE interaction between JS and native.

@protocol AppJSObjectDelegate <JSExport> -(void)scan:(NSString *)message; @end @interface AppJSObject : NSObject<AppJSObjectDelegate> @property(nonatomic,weak) id<AppJSObjectDelegate> delegate; -(void)webViewDidFinishLoad:(UIWebView *)webView {JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; AppJSObject *jsObject = [AppJSObject new]; jsObject.delegate = self; context[@"app"] = jsObject; / / - also can be realized by block - context [@ "openAlbum"] = ^ () {NSLog (@ "js calls oc open photo album"); }; JSValue *value = [self.jsContext evaluateScript:@"document.title"]; self.navigationItem.title = value.toString; }Copy the code
<! DOCTYPE html><html>
<body>
    <input type="button" name="" value="Sweep it." onclick="scan()">
    <p id="result">Scanning results:</p>

    <script type="text/javascript">
        // Call the APP scan method h5-> APP
        function scan(){
            app.scan('scanResult');
        }

        // Scan result callback app->h5
        function scanResult(result){
            document.getElementById("result").innerHTML = 'Scan result:' + result;
        }
    </script>
</body>
</html>
Copy the code

The process of creating a Webview page Native is roughly as follows: Create a UIViewController, initialize a Webview control, set some basic properties, such as: length and width, color, margin, etc. Add to the current page, and then call the load method to load the URL for the page you want to open. Through the framework of JavaScriptCore provided by the system, we can easily get the JS running environment in WebView, just like Chrome browser opened the console mode, You can optionally enter JS on the Console to have some effect on the current context.

Somewhat similar to how JSONP implements HTTP across domains, but with richer usage scenarios.

“JSONP cross-domain “: The front end accesses a URL with a Script tag and a Callback method. The server receives the request, returns a piece of spliced JS code, and triggers a Callback function to complete the cross-domain request.

After obtaining the JSContext, Native can not only read JS methods, but also write methods to them for JS invocation.

Attached is the classic Hybird framework: Cordova architecture diagram

Miniprogram (wechat miniprogram)

Wechat mini program is not just a simple WebView to load the page, but on the basis of the above improvement. We all know that in browsers, the UI thread and the JS thread are mutually exclusive, because the results of JS execution affect the results of the UI thread, which is frozen while the JS thread is running. If the JS thread is blocked, the page will stall and the experience will be very bad. Applets use a two-threaded model to avoid this problem.

Two-thread model

The rendering layer and logic layer of the applet are managed by two threads respectively: the interface of the rendering layer is rendered by WebView; The logical layer uses JSCore to run JavaScript code. A small program has multiple interfaces, so the render layer has multiple WebViews. The communication between the two threads is transferred through the Native side of the applet, and the network request sent by the logical layer is also forwarded through the Native side. The communication model of the applet is shown in the following figure.

  • Logical layer App Service

    The business logic layer runs in the same JSCore thread. The logic layer processes the data and sends it to the view layer, and receives the event feedback from the view layer. Ios is JavaScriptCore, Android is X5 JSCore, developer tools are in WebView; The logical layer of the applets framework does not run in the browser, so JavaScript in the Web some capabilities are not available, such as Window, document and so on.

  • The view layer

    The View view layer renders in a WebView, one page for each WebView,

A small demo

H5 page running a block of code, the page input box in JS thread blocking period will not receive page input, the same code into the small program above, the page input box will not have any impact


function calculation() {
    for (let i = 0; i < 1000100; i++) {
    for (let j = 0; j < 10000; j++) {
        let ret = i * j
    }
    }
}

Copy the code

Why do they do that?

Control and security, wechat mini programs prevent developers from using open interfaces provided by some browsers, such as jumping to pages, manipulating DOM, and dynamically executing scripts. Separating the logical layer from the view layer, only data communication between the view layer and the logical layer can prevent developers from arbitrarily operating the interface, and better ensure user data security.

Wechat applet view layer is WebView, logic layer is JS engine. The three-end script execution environment 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
Applets developer tools NWJS Chrome WebView

Let’s take a look at the differences in code execution between a single WebView instance and a small program with two threaded multiple instances.

In single WebView mode, Page view and App logic share the same JSContext, so that all pages can share global data and methods, and achieve global state management. In multi-WebView mode, each WebView has an independent JSContext. Although data can be transmitted through window communication, data and methods cannot be shared, and the global state management is relatively complex. This can be solved by isolating a generic WebView or JS Engine as the application’s JSContext.

A diagram of the life cycle of a two-threaded interaction:

ReactNative

Let’s review how browsers work

  • Browser passDom RenderTo render all the elements.
  • The browser has a whole set of UI controls that are styled and functioned according to the HTML standard
  • Browsers can read HTML and CSS.
  • HTML tells the browser what control to draw (HTML tag), and CSS tells the browser what each type of control (HTML tag) looks like.
  • The main function of a browser is to form a Dom tree by parsing HTML, and then decorate each node of the tree with CSS

UI description and presentation are separated

  1. The HTML text describes what the page should do, and CSS tells the browser what it should look like.

  2. The browser engine interprets HTML and CSS into a list of predefined UI controls,

  3. The UI control then invokes the operating system drawing command to draw an image to present to the user.

  4. Javascript is optional, mainly used in HTML for user event responses, DOM manipulation, asynchronous network requests, and some simple calculations

  • Green is the part of our application development. Basically all the code we write is in this layer
  • Blue represents common cross-platform code and tool engines, so we don’t touch blue code
  • Yellow code platform related code, do customization will be added to modify the code. Instead of cross-platform, write different code for the platform. IOS write OC, Android write Java, Web write JS. Each bridge has a corresponding JS file, and the JS part can be shared
  • The red is the system platform stuff. A dotted line above the red indicates that all platform-related things are separated by the Bridge
  • Most of the time we’ll just write the green part, less of the time we’ll write the yellow part. The red part is independent of React Native

JavaScriptCore + ReactJS + Bridges make React Native

  • JavaScriptCoreResponsible for JS code interpretation and execution
  • ReactJSResponsible for description and managementVirtualDomDirects native components to draw and update, while much of the calculation logic is done in JS. ReactJS itself does not draw the UI directly, which is a time-consuming operation that native components are best at.
  • BridgesThe ReactJS draw instructions are translated to the native component for drawing, and user events received by the native component are fed backReactJS. To achieve different effects on different platforms can be customizedBridgesTo implement the
  • The Bridge’s job is to provide an extension of the native interface to the RN embedded JS Engine for JS calls. All local storage, image resource access, graphics and image rendering, 3D acceleration, network access, vibration effects, NFC, native control rendering, map, location, notification, etc. are encapsulated by Bridge into JS interface and then injected into JS Engine for JS invocation. In theory, any effect that native code can achieve can be encapsulated by Bridge into components and methods that JS can call, which RN can use as JS modules.
  • Every native function that supports RN must have both a native module and a JS module. A JS module is a wrapper around a native module that makes it easy for Javascript to call its interface. Bridge is responsible for managing the communication between native modules and their JS counterparts. Through Bridge, JS code can drive all native interfaces to achieve all kinds of cool native effects.
  • The separation between JS and Native in RN is very clear. JS will not directly reference the object instance of Native layer, nor will Native directly reference the object instance of JS layer (all the interaction between Native and JS is connected by several basic methods through the Bridge layer).
  • BridgeThe native code is responsible for managing the native module and generating the corresponding JS module information for the JS code to call. The encapsulation of each functional JS layer is mainly for ReactJS adaptation, so that the functions of native modules can be more easily invoked with ReactJS.MessageQueue.jsisBridgeAt the JS layer proxy, all JS2N and N2JS calls go throughMessageQueue.jsCome forward. There is no pointer passing between JS and Native, all parameters are passed as strings. All instances are numbered on both sides of JS and Native, then a mapping is made, and that number/string number is used as a lookup basis to locate the crossover object.

conclusion

Each cross-end solution has its own advantages and disadvantages. You can choose which solution to use based on the actual situation. The Hybird solution, while simple to implement, is not as interactive as the other two, and is suitable for nesting some simple pages. The small program program seems to be cool, but the research and development cost is also high. Currently, App supports small programs are some big factories, once the framework is developed, it can be said that once and for all. RN program has the best experience, but the learning cost is relatively high. Therefore, native students need to use plug-ins to solve some tricky demands.

reference

Five major browsers and four cores

An in-depth look at WebKit

In-depth understanding of JSCore

Micro channel small program technical principle analysis

React Native