Writing in the front

Hi, I’m Ren Mo. This is my topic “Beihai Kraken”, a Web rendering engine based on Flutter, on GOTC, a global open source technology summit. I mainly share some implementation principles and key technical features of Kraken from the technical point of view. Now I have sorted them into text version to share with you.

Kraken Github:github.com/openkraken/…

Kraken’s official website: openKraken.com/

North Sea technical background

When it comes to the technical background of Beihai, we have to mention the evolution of cross-end technology. Many students are familiar with the process of cross-end technology, so I would like to talk about it briefly.

As we know, browsers are the most mature natural cross-platform solution. As early as the PC era, the browser has become the entrance to the Internet, we are used to browsing the web page through the browser to absorb all kinds of information, we called this way of surfing the Internet “surfing”. However, in the mobile era, the browser does not have an eye-catching performance on mobile devices, instead, due to large memory, weak network environment blank screen for a long time, lack of sensor capability (slow standard follow-up) and other problems make all kinds of doubts.

In order to make up for the shortcomings of the above browsers in mobile terminals, Hybrid technology emerged, which realized some non-standardized supersets through the ability of containers on the Web. Meanwhile, prefetch, offline package and other technologies were used to improve the loading performance of the first screen.

Since then, RN-like solutions (typical representative React Native) emerged. Its principle is to bridge Native controls with front-end ecology through JS engine, improve efficiency through Web development business logic, and improve performance and experience through downward rendering of Native controls. But the downside of such solutions is that they don’t completely bridge the gap, don’t solve the problem of consistency, and end up exposing complexity to developers.

Flutter has gained more and more attention in the past two years as one of the new favorites of the cross-end industry. Here is a brief introduction to Flutter.

Flutter has the advantages of good performance and high cross-end consistency due to its self-rendering, but it also has its own disadvantages, such as an ecosystem that is neither front-end nor Android/iOS.

This raises a series of questions.

  • First, there is a cost to the front-end (JavaScript) or client-side (Swift/JAVA) transition, but because the end-to-end GUI architecture is pretty much the same. From the perspective of a front-end developer, the cost of language learning is not particularly high. Students with experience in front-end frameworks such as React or Vue can learn it quickly through simple learning. For some small startup teams, it is possible to take small steps to learn and develop quickly, but the cost of switching increases exponentially when the organization becomes large enough.
  • Secondly, the ecosystem is waiting to be rebuilt. Some Flutter developers may feel that there are already enough pub for Flutter development to use directly. However, the ecosphere is not only about Flutter pub, but also various existing basic links, such as building CI/CD, building etc. All of these ecosystems need to be rebuilt, and the cost is very high.
  • Third, many of the existing businesses are front-end projects developed through JavaScript + front-end frameworks, and the cost of migrating them to Dart + Widgets is very high.

While facing so many problems and the high cost of switching, we also expect Flutter to bring more technical possibilities to our business and improve some of the performance and experience issues of the Web container on the end. So, the first step in introducing a new technology is to solve the cost of introducing this new technology, so we are actively exploring a solution that combines front-end ecology with Flutter.

Hence came the protagonist of this topic — Beihai Kraken.

Kraken is a high performance Web standards rendering engine that is high performance, easy to expand, based on Flutter and compliant with Web standards.

Below, I list some application scenarios of Beihai in Ali, which are related to the implementation of Beihai on C-terminal APP or IoT devices.

North Sea technical principles

Before introducing Kraken’s technical principles, LET me show you how to develop a Kraken application. Kraken is a Web rendering engine developed based on W3C standards, so the upper layer is framework independent, regardless of whether developers use Vue or React or Rax to develop an application on Kraken.

Taking vue.js development as an example, the following is a project I started with VUe-CLI provided by Vue officially. See the official example for the code.

As you can see, the code for Vue is on the far left, and the results for Chrome (left) and Kraken (right) are exactly the same.

Now that you know how to develop a Kraken application, let’s take a look at the technology behind Kraken. To better understand this, let’s first compare the rendering flow of Flutter to Webview.

WebView rendering process I believe you are very familiar with the interview is a very classic topic is how a URL input to render to the screen. In general, it is to parse HTML, JS and CSS files, execute corresponding JS and call DOM API, and finally generate DOM Tree and CSSOM Tree, and then calculate and finally get Render Tree. A series of layers are generated through the Layout and Paint process, and finally rendered to the screen by compositing and rasterizing.

Look at the classic three trees of Flutter — Widget Tree, Element Tree, and RenderObject Tree. Widget Tree corresponds to the front-end framework layer, while Element corresponds to DOM Tree and RenderObject Tree corresponds to Render Tree. Finally, the Layer is generated through Layout and Paint calculations, and then rendered to the screen through composition and rasterization.

So, let’s add the front-end framework to our whole process to make a more intuitive comparison, again with vue.js as an example.

Vue.js generates a series of VDOMS at runtime to generate a Vdom Tree, and then invokes platform-specific apis through Platfom’s abstraction.

Then we find that we can achieve what we ultimately want to achieve (upper Web development, lower rendering based on Flutter) by simply switching the flow of the part I have circled in red.

With that in mind, the rendering process for the North Sea came out.

Most of the current front-end frameworks will Bundle the product into a JS Bundle, using the standard DOM API to manipulate the specific view, while HTML generally has only one root node. On the Web, the page requests an HTML file and then parses the Script tag to load the corresponding JS file. Kraken’s entry is designed as a JS file, which reduces one request and speeds up the rendering of the first screen.

Kraken runtime provides a series of Web standard apis via the JS Engine Binding. Calling the API executes the logic and creates a set of instructions that need to be sent to the Dart layer for processing, stored in a struct. Dart uses FFI to send the address underlying the instruction to Dart, which processes the instruction and generates the Dom Tree. CSS also generates the corresponding CSSOM Tree through the Parser. Finally, the RenderObject Tree of the Flutter is combined with the Layout and Paint calculation to generate the corresponding series of layters. And then the final screen display through synthetic rasterization.

Similarly, in the latest implementation, we took the SSR application scenario into consideration, so we added the HTML for the entrance of beihai application development mode, the CORRESPONDING HTML files can be parsed through HTML Parser, the subsequent process is the same. SSR support also makes the opening rate of the first screen to the next level.

So knowing Kraken’s entire rendering process, how can we develop a Web standard rendering engine based on Flutter?

So to do this based on Flutter, you must first understand the architecture of Flutter.

The top layer of Flutter is the Dart implementation Framework. It contains the responsive Framework, the official component library, and the implementation of the layout and drawing protocol. In the middle is the C++ implementation of the Flutter Engine, which is the lower part of the rendering process and provides some basic capabilities, as well as layer composition and rasterization output. The Embedder layer, the lowest layer, is responsible for some of the implementation of the specific platform to achieve cross-platform.

It is not hard to find that the most Widget of the Dart Framework is the ABSTRACTION of THE UI, which implements a set of responsive frameworks corresponding to front-end frameworks such as Vue/React. The following layout protocols can correspond to W3C standards to achieve a set of layout and drawing protocols based on front-end standards.

Then we can come up with an architectural design for the North Sea.

The Widget capabilities of Flutter can be registered with Kraken in the form of a plug-in, which becomes a front-end standard Tag. JS can dynamically invoke and control rendering. The whole left Flutter architecture supports the upper Flutter ecology, so that the Flutter ecology can also be integrated into Kraken’s rendering system through plug-ins.

On the right is Kraken’s architectural implementation. Kraken’s implementation does not invade the Flutter Engine. In the Dart layer, a list of standardized capabilities, such as Element, CSS, and various Web standard Modules, are provided to the upper layer by implementing a set of W3C standard layout and rendering capabilities. Kraken runtime provides a set of Web standard apis via JS Engine Binding. Calling the APIS executes logic and creates a set of instructions that need to be sent to the Dart layer for processing. Instructions are stored in a struct. Dart uses FFI to send the address at the bottom of the corresponding instruction to Dart. Dart calls the standardization capability mentioned above to complete the docking. Through this implementation, it provides support for the upper front-end ecology. With rich front-end ecology, developers can enjoy the efficient development experience brought by front-end ecology.

Key technical features

The loading performance of the first screen is a key indicator in a C-terminal scenario. A blank screen for a long time greatly affects user experience.

Kraken needs to create a large number of nodes during the initialization of the first screen, and a large amount of time is spent on communication, so optimizing the performance of the first screen is imminent.

As we know from the technical principles above, Kraken needs to communicate with Dart through the Bridge to transfer instructions to the Dart layer. The Bridge architecture has also undergone three versions of evolution.

In the original first-generation solution, we hacked into the Flutter Engine, passed data from the JS Engine to the Flutter Engine, and then sent data to the Dart layer via native bingding. The obvious disadvantage of this generation of solutions is that the build up of the Flutter Engine takes a lot of time. Also, hacking into the Flutter Engine is not a logical design for Kraken’s architecture.

Then came the Dart FFI, which enables efficient communication between C++ and Dart, resulting in a second-generation solution. The second-generation Bridge solution serializes JSON data and passes the data to the Dart layer through Dart FFI, which then deserializes JSON to retrieve the final data. Compared with the previous generation, this solution can solve the disadvantages of invading the Flutter Engine, but introduces the problems of string copy and JSON serialization deserialization time.

In order to solve the above problems, the third generation Bridge solution was created. The third-generation Bridge scheme defines a standard 40-byte Struct to store instructions in shared memory, while Dart FFI passes only the address of the instruction. Both C++ and Dart rely on the address to access relevant data. This solves JSON serialization deserialization problems, saves time, and saves one data copy. Also, memory is 40 Bytes aligned, which improves memory access efficiency.

Here are some first-screen revenues from actual online pages.

Scrolling long lists is a historical problem that has plagued front-end developers for a long time. Lots of layouts cause the page to freeze, and Paint takes too long to scroll, causing frames to drop. The community also has a number of front-end solutions to address this problem, and on Kraken we expected to address this problem at the container level as well.

In Android and iOS, there are RecyclerView and TableView respectively to solve this problem. Their principle is to define a buffer area outside the viewport area, dynamically release the node when it exceeds the area, and dynamically create the node when it enters the area. And through a series of nodes to replace attributes to ensure that the number of nodes does not explode. Flutter provides a similar implementation of Sliver, so can we solve this problem with a Sliver enabling front end?

Kraken defines a new display property, sliver. By setting the display property of a node to sliver, you can directly use the sliver capabilities of Flutter to achieve the ability to dynamically recover nodes that are out of sight and cache. As you can see, we tested it with a 1000 card DEMO, and sliver had a significant benefit over Block.

At the same time, the standard has been discussed in the W3C Chinese interest group, and it is expected that after full discussion and consensus is reached, we will try to submit the proposal to W3C and feed back to the front-end community.

A large front-end team often has both a client and a front end, settling a range of on-side capabilities. Different requirements will have different technology selection, for example, a player is often developed through Native technology. We want to integrate the capabilities of the Flutter widgets, Web, Native, and third-party SDKS into one big front-end end development system. So how do we integrate these capabilities in Kraken? At the same time, we also expect to optimize the package volume on demand. In different business domains, we expect to be able to quickly customize development and quickly develop a set of domain capabilities for vertical business domains.

Kraken provides a set of extension capabilities to solve these problems. Through the rendering capability extension interface, developers can quickly integrate standard-compliant Flutter widgets and Native rendering capabilities into Kraken’s system. Finally, the ability to make dynamic calls is provided through JavaScript. Also, with MethodChannel, developers can invoke some Native or Dart API capabilities, such as some binary or tripartite SDK capabilities.

Developers can customize the capabilities required by the service domain by extending the capabilities, and plug and remove the capabilities as needed to optimize the package volume. Similarly, plug-ins registered with Kraken can be controlled through JavaScript code, providing dynamism.

Here are a series of demos that extend Flutter widgets, Native apis, and Native players within Kraken.

Here’s how to improve interactivity. Before introducing Kraken’s interactivity, let’s take a look at some of the interaction issues under the Web.

When developing interactive applications on the Web, front-end developers often need to introduce an additional lib to provide enhanced gesture capabilities (such as a gesture library like Hammer.js). Therefore, when the front-end developer introduces lib, it will lead to the need for additional request corresponding JS library after loading index. HTML, resulting in an additional request cost and prolonging the interaction time of the first screen.

When a user performs an operation on the screen, the user may operate with his hand, or with devices such as Apple Pencil or mouse. Therefore, in the W3C standard, a pointer is abstracted as a pointer that forms a gesture based on the operation, namely down, move, and up, of which move can be omitted (for example click).

On the Web, you dispatch these Pointers to the Element Tree, bubble them frequently to the JS layer, and then the JS recognizes the interaction by encapsulating the Touch API. There are several problems with this, first of all, passing pointer frequently from C++ to JS brings unnecessary overhead, plus the ability to encapsulate standards creates additional development costs and ease of use is not outstanding. At this point, if some solutions of the community are used, it will also lead to disstandardization and misalignment of standards, resulting in inconsistent interactive experience of different pages in the same application.

In order to solve the above problems, we expect to provide a set of standardized interaction capabilities from the aspects of standardization, ease of use and standardization. By encapsulating pointer at the bottom to get different gesture capabilities, developers can quickly develop interactive applications.

Below is a flowchart for enhanced interaction in Kraken. When the user performs some interactive operations, the pointer of each touch point will be passed to Kraken from Native, and the pointer will be sent to GestureManager (gesture recognizer management class) and Scroll recognizer. GestureManager identifies developers through a Web standard to monitor behavior (EventTarget. AddEventListener) to sign up and distributed to the corresponding gesture recognizer, same Scroll identifier will be distributed pointer. These recognizers are added to the Flutter competition field for gesture competition to ensure that only one specific action is triggered (interactive control). The Scroll recognizer triggers the Scroll operation of the Scroll area, the gesture recognizer bubbles and dispatches through standard Web flow, and finally the developer listens for events to complete the custom behavior.

Debugging is essential when developing applications, and front-end development efficiency is not only due to a thriving ecosystem, but also a friendly debugging experience.

Kraken abstracts Inspector to connect to Chrome DevTools via the Chrome DevTools Protocol, providing a series of debugging experiences that are exactly the same as those for front-end Web application development. Whether developers prefer to use console. log or JS Debugger, they can get started quickly.

In addition, Kraken also supports all the standard Web APIS of HMR to provide the ability of local hot update, so that the development of Kraken applications can be consistent with the debugging experience of local hot update under the Web, greatly improving the development and debugging experience of developers.

Finally, all the code of Kraken has been open source. Kraken provides an open TSC mechanism expecting all developers to communicate and make decisions equally, so that Kraken can develop better. We also welcome more developers to build Kraken together.

Kraken Github:github.com/openkraken/…

Kraken’s official website: openKraken.com/