Author: Fushen Nikui, Front-end engineer of Alibaba Tao Department

Rax making Repo – github.com/alibaba/rax

Rax applet official website – rax.js.org/miniapp

After continuous iteration, Rax applet received a major upgrade to support a new runtime scheme. At the beginning of 2020, we would like to conduct a comprehensive review and summary from the characteristics of Rax applets. At the end of the paper, we attach a comparison between Rax and the current mainstream applets development framework. This article will be from API design and performance, dual-engine architecture, excellent multi-component protocol design and Webpack-based engineering architecture four directions.

I. API design and performance

When deciding the technology selection of a product, we often consider the following aspects: (1) availability ecology, that is, whether the surrounding tools meet the conditions of product development; (2) Risk rate, that is, whether problems can be quickly located and solved, and whether the technology used will be maintained continuously; (3) Starting cost, that is, whether it needs a lot of money to reach the stage of being able to use; (4) Performance, that is, to meet the established performance standards and user experience of the product.

This section mainly introduces the advantages of Rax applets on the latter two points.

API design

The overall start-up cost of the framework is relatively small. Rax applet link is inherited from Rax (the progressive React framework for building multi-terminal applications). So as long as you know how to develop Rax Web/Weex or React, you will be able to use Rax to develop small applications that can be used in other applications supported by Rax.

However, due to the particularity of small programs, there will always be areas that cannot be smoothed out and need to be handled separately. Since Rax has been working on multi-terminal solutions for a long time, we believe that the independent properties of each end should not invade the base framework itself, ensuring the purity of the base framework is conducive to more extensions.

Take the following code for example:

Taro:

import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'

export default class Index extends Component {
  config = {
    navigationBarTitleText: 'home'
  }

  componentWillMount () { }

  componentDidMount () { }

  componentWillUnmount () { }

  componentDidShow () { }

  componentDidHide () { }

  render () {
    return (
      <View>
        <Text>1</Text>
      </View>)}}Copy the code

Rax:

import { createElement, Component } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import { isMiniApp } from 'universal-api'; 
import { registerNativeListeners, addNativeEventListener, removeNativeEventListener } from 'rax-app';

function handlePageShow() {}

class Index extends Component {
  componentWillMount () { }

  componentDidMount () { 
    if (isMiniApp) {
      addNativeEventListener('onShow', handlePageShow);
    }
  }

  componentWillUnmount () {
    if (isMiniApp) {
      removeNativeEventListener('onShow', handlePageShow);
    }
  }

  render () {
    return (
      <View>
        <Text>1</Text>
      </View>)}}if (isMiniApp) {
  registerNativeListeners(Index, ['onShow']);
}

export default Index;
Copy the code

In comparison with Taro, there are two main differences: (1) Rax does not have componentDidShow componentDidHide concept, added and W3C standard similar addNativeEventLisenter removeEventListener API; (2) There is no static property called config on the component instance to set the page’s title and other configuration.

React doesn’t have anything to do with componentDidShow because it doesn’t have anything to do with the component lifecycle. We prefer to guide users to write business code using standard apis. At the same time, there are performance-related benefits to the design of this notation, which will be explained later.

Of course, the design itself will lead to a certain amount of code expansion, but through engineering means, it is possible to ensure that the volume of the final product code is almost the same.

performance

The performance problem of Rax applets is often encountered in business development, for which a lot of efforts have been made in the current compile-time scheme of Rax applets. Through ali small program real machine cloud testing function, we did a test on an infinite drop-down list.

The page structure is as follows:

According to the real machine test report, the average of native applet three times is 2008 ms, Taro is 2003ms, Rax is 1924ms, of course, there is not much difference, but the actual business scenario is far more complex than the above page structure.

Similar to Taro, the basic framework of Rax applet side does not create a VDOM in the logical layer, but instead uses data merge, traditional data diff, to prevent users from updating redundant data. What’s more, Ali applet nativly provides a private method called $spliceData for performance optimization. The Rax base layer will identify whether the value that the user needs to update is an array, and then automatically use $spliceData to optimize the rendering according to the scene.

In addition, it needs to be mentioned that the small program itself needs to be pre-registered to listen for events (this is also to ensure performance), which requires:

Page({
  onShow() {}
});
Copy the code

Can not dynamically register:

const config = {}
Page(config);
setTimeout((a)= > {
  config.onShow = (a)= > {};
}, 1000);
Copy the code

So adding concepts like componentDidShow is a bad idea, which would cause the page to register all native events because it doesn’t know if it needs to register the onShow property, which would not only cause developers to be unable to scale, but also risk memory leaks.

The Rax applet introduced registerNativeListeners, which only gave developers a new understanding that they needed to register events on the page before they could listen. This not only addresses scalability issues, but also potential performance issues.

Of course, Rax applet can do performance optimization so far? In the foreseeable future, the Rax applet compile-time solution already has some specific actions, such as further easing the framework’s management of props updates and making more use of the native capabilities of the applet to implement component updates, so as to avoid performance degradation by doing the same thing with the applet infrastructure.

Two, the twin engine architecture

Rax is (possibly) the industry’s first applet solution that supports both compile-time and run-time solutions. Switching between the two solutions is incredibly easy, and we really give the user the choice of high performance or full syntax. The architecture of the twin-engine driven Rax applets is as follows:

Compile time scheme

Rax applet compile-time scheme is a process of converting Rax code into native applet code through lexical and grammatical analysis on the premise of AST translation. Due to the dynamic capabilities of JavaScript and the fact that JSX itself is not a traditional template type syntax, a lightweight runtime shim was introduced in this scenario.

The core of the Rax applets compile-time architecture is divided into two parts, AST translation and runtime shim. The following will give a brief introduction to these two parts.

AST translation

The AST translation architecture is much clearer and more maintainable than Taro, a similar product. It has to be mentioned here that it is divided into syntactic scene translation and onion model. We can take a look at the syntax scene translation section of the code structure:

It can be clearly seen that there is a module specifically responsible for translation for each grammatical scene that needs to be translated, which makes the whole process of translation easier. As long as the translation result of each part meets the expectation, the translation result will meet the expectation. This design allows us to make full use of unit tests to compare code before and after translation.

The onion model design is another major design of AST translation. The whole translation process is actually divided into four steps:

The onion model mainly carries out the following three steps: the original AST tree is modified to a new AST tree meeting expectations in the Parser layer, and then the new AST tree is translated into small program code in the generate layer.

Run time gasket

Due to the dynamic capabilities of JSX and the fact that Rax originally provided some features like hooks. Therefore, the Rax applet compile time scheme provides a runtime shim to align the simulated Rax core API.

Since the runtime is introduced, it is natural to do more management of data flow based on this mechanism, and provide API of Rax project on other end, such as history location of route.

Runtime scheme

Rax applet’s runtime scheme is not developed by itself, but “standing on the shoulders of giants”, reusing kBONE architecture and making a certain degree of transformation to access Rax applet’s engineering system. The implementation principle of the run-time solution can be viewed here, which is not detailed here. First of all, Rax applets are also the advantages of KBone:

  1. Support for more complete front-end framework features. In contrast to the Rax compile-time scheme, you can now use the full Rax syntax, and all Rax features are already supported. Forget the grammar rules;
  2. Highly reusable Web side logic. If the user has the Rax program code on the Web side, the whole application can be completely transplanted to the small program side with a little modification, which greatly improves the development efficiency.
  3. When running on the applet side, you can still use the features of the applet itself, such as the built-in components of the applet.

On the basis of KBone, Rax applet runtime scheme also added many features, which can be summarized as follows:

  1. Support alipay small procedures. Due to the positioning of KBone, it only supports wechat small programs. Based on Kbone and combined with the characteristics of Alipay mini program, Rax has expanded its support for Alipay mini program. Now, if you want to use the runtime solution when developing alipay applets, you can not only use Remax, but also Rax;
  2. Access to the complete Rax engineering system. Now, you can experience all the features of Rax projects, such as Rax multi-side apis, multi-side components, multi-side builders, etc., while using the runtime solution, and enjoy a complete and consistent experience.

Finally, we cannot avoid the fact that the Rax applet runtime scheme suffers from the same problem as all run-time schemes: performance degradation. In fact, the run-time solution is to trade some performance loss for more comprehensive Support for Web side features. Therefore, if you have certain performance requirements for small programs, the recommended use of compile time scheme; If performance is not critical, run-time solutions are a great tool to help you develop small programs quickly. Twin-engine Rax mini program, there is always a place to hit your heart.

Excellent multi-terminal component protocol design

The Rax applet compile time scheme supports both project-level and component-level development. Unlike Taro, which compiles components into small program code in a unified project, Rax can build small program components in a component project. Combined with a set of excellent multi-component protocol design, we achieved the normal use of Rax applets in both Rax applets and native applets, while maintaining a unified multi-terminal development experience. This protocol is defined in the package.json miniappConfig field, and its usage design can be found in the documentation Rax Applets – Multi-terminal Component Development.

Support progressive access to Rax

For developers who have developed complete applets using native syntax, it is a reasonable requirement to gradually switch to the Rax development link, since the entire project migration can be costly. Rax, on the other hand, relies on the multi-component protocol to help smooth the transition.

By design, the Rax applet component project builds components that conform to the applet syntax, so it stands to reason that they can be used directly in native applet projects. This means that if you want to develop applets using Rax incrementally, you can gradually migrate applets developed using native syntax to Rax on a component or page basis. This is something that Taro and other frameworks do not have. Among the users of Rax, the small program of Alipay of Zhejiang Provincial online government affairs platform “Zhejiang Provincial Office” adopts the way of gradual access to Rax.

Multi-terminal unified component use experience

When a applet component published using Rax Component Engineering is used in a Rax project, the builder automatically finds the applet implementation of the component through the path specified by miniappConfig to replace it. Users do not need to write specific paths for service code writing in the traditional way of importing native applets, but keep the paths consistent with those on the Web/Weex, as shown in the following example:

// Wrong
import CustomComponent from 'custom-component/miniapp/index'

// Correct
import CustomComponent from 'custom-component'
Copy the code

In addition, the multi-endpoint protocol can be extended to the multi-endpoint library protocol, which supports more flexible writing like import {Button} from ‘fusion-mobile’. Using the Rax Multicomponent protocol, you can quickly develop common components or component libraries for your multicomponent projects. For example, the Rax base components were developed in this way.

4. Engineering architecture based on Webpack

Rax project is based on alibaba Group’s unified front-end CLI tool @alib/build-script. It relies on Webpack, supports various scenarios through plug-in system, and provides flexible Webpack configuration capability based on Webpack-chain. Users can implement engineering requirements by combining various plug-ins. The compile-time scheme for Rax applets handles its own logic through webpack Loader. The WebPack Loader classified by file roles such as APP/Page/Component will call JSX-Compiler for AST analysis and processing of code, and then hand over the processed code to Loader to generate corresponding small program files. The runtime solution directly reuses the compiled configuration on the Web side and generates the specific applet code through additional Webpack plug-ins.

Compared with other self-developed engineering systems, the whole architecture has the following advantages:

  • Webpack based, flexible and extensible
  • Plug-in system, strong reusability
  • Simple commands and unified experience

conclusion

Finally, a comparison between Rax and existing mainstream applets is presented.

For more information about Rax applets, visit rax.js.org/miniapp!