Author’s brief introduction

Feng to wood

Qunar front-end technology director

I started front-end work in 2008 and worked in Renren, Sina Weibo, iQiyi, etc. I joined Qunar in 2012 and led the upgrade of front-end technology architecture for many times, promoted the separation of front and back ends of Node, implemented the application of React, RN and other technical architectures in business. I am good at various front-end performance optimization and engineering practice. Currently, I am responsible for promoting the multi-terminal unified upgrade plan of Qunar.

Tracy

I joined Qunar in 2019. I am good at multiple front-end and back-end languages and mainly responsible for Node engineering and standardization. At present, my focus is the development and promotion of multi-front-end unified framework.

Wang Jiafeng

I joined Qunar in 2019 and was mainly responsible for the migration of public product business to RN technology stack and the migration of main process RN project to three-terminal project. It mainly involves the development of infrastructure such as React-Native, Touch, small program business development and multi-terminal convergence framework.

1 introduction

Qrn-remax-unir is a cross-end component that ADAPTS RN to the small program, which is implemented by qunar front-end technology team. Through this component library, RN source code can be directly run to the small program quickly and conveniently. The scheme refers to the react-Native Web adaptation scheme, and uses the Remax framework to implement the adaptation component library and achieve the purpose of adapting multiple applets. Like the React-Native Web, it has low intrusion into RN source code and is fairly easy to debug and replace components.

The solution comes from the community. We are just reasonable applications to solve our business problems. Finally, we developed this component library.

Let’s briefly introduce RN to the applets adaptation scheme and the bits and pieces of the adaptation component library.

2 Review of multi-terminal solutions

Qrn-remax-unir component library is a part of the whole Qunar cross-end scheme. Through the whole scheme, RN code can be directly run to H5 and applet, and the goal of cross-ios, ADR, H5 and applet can be achieved. The idea of the overall multi-terminal scheme is as follows:

  1. Use the React-native Web to adapt RN code to run on H5, which is RN’s official recommended and integrated approach.

  2. A set of RN component library is implemented on the basis of REmax component, and remax is borrowed to adapt to multi-terminal (QRn-remax-unIR).

  3. Use webPack features to achieve platform-specific packaging.

3. Rn-related multi-terminal scheme

3.1 Investigation on Alita

In the initial survey, we noticed that Alita could convert ReactNative to wechat applet and conducted a detailed survey on the scheme:

1. Alita will compile JSX syntax into fragments of wechat WXML according to code blocks in the compilation stage, and then wrap them with Templete;

2. Internal implementation of a mini-React to adapt react and applets life cycle details can be found in the following table:

www.jqhtml.com/44555.html

So I ran some demos to use the scheme and found that it still fell short of our expectations:

1. Although it is expected that there is no intrusion into RN code, there are still restrictions on RN source code. Some features cannot be used, and custom animation libraries provided by Alita are required for animations.

2. A customized mini-React is used internally to connect the life cycle of React and applets, considering the risk of adaptation schedule for new features of React in the later stage;

3. Focus on the conversion of RN to wechat applets, and do not support other applets.

Based on the above reasons, we did not directly use this scheme. In the investigation process, we felt that their processing ideas of JSX template were very novel and good for reference.

3.2 Simple Principles of REMAX

Remax can be developed in applets using real React. Remax implements itself as a React renderer, gathering host UI change instructions to generate a page VNode tree, which is then rendered in the applet rendering layer.

Due to the run-time adaptation-based solution, there are the following advantages and disadvantages:

  1. The React virtual DOM ADAPTS to the React virtual DOM. There are no restrictions on the React stack. Redux, mobx, hooks, etc, can be used freely.
  2. The dynamic processing of template may be inefficient in rendering, so it needs to be solved accordingly.
  3. Because there is no compilation processing, the runtime is platform source code, more convenient to check the problem;
  4. React component based, can be very good support for small program native components;
  5. There is no cross-end component for each small program, and only some basic components are encapsulated and adapted.

4. Our adaptation scheme

Remax supports remax’s syntax, but it is based on the syntax DSL provided by custom components that have nothing to do with RN. However, their react syntax ADAPTS to applets in a bold and practical way. It also inspires us to follow a similar adaptation of RN components to applets so that RN can be run into applets.

4.1 ReactNative to REmax components

Research the adaptation of Remax, although the principle of the scheme is simple, but really to do such a set of things under the evaluation is very time consuming.

After re-evaluation, remax can adapt react to applets and provides several basic cross-end components. RN components that we implement directly with REmax components can be quickly adapted to the small program side, although there may be some performance loss.

Here’s an example of adapting RN’s Text component to a remax component:

4.2 Adaptation of the ReactNative API

The implementation of the React-Native API is simpler and more straightforward. Here, please allow me to divide the react-Native components into two categories:

One is the pure function API, which simply means you call a function provided by the framework and get the data you want to write or return, such as Dimensions, a generic API, Qrn-remax-unir’s solution is to be able to encapsulate the API provided by the call applet, and there are two platforms for grinding parameter differences can be perfectly adapted. Qrn-remax-unir is often implemented nullly and informed to developers where the same effect cannot be achieved by calling the applets API or where the applets scenario does not logically exist.

The second is to call a method provided by the framework to render on the page. Let’s call it an API component. For example: Alert, ToastAndroid, ActionSheetIOS, etc., are implemented by constructing displayed template components that expose methods that can modify the state of components indirectly. For example, Alert is populated by getting the content passed in by calling methods. Modify to hide undisplayed state when retrieving user action and callback component callback method.

4.3 Adaptation of complex components and apis

Complex components and third-party components are actually a combination of basic element components, we complete adaptation from the bottom of the basic components (View Text, etc.), so that most of the complex components can be directly adapted to the small program side. As shown below, complete adaptation of upper-layer RN services and components can be guaranteed as long as the basic components of QRN-remax-UNIR are fully adapted.

5 Development Process

Although the development ideas are generally clear, but in the development process or encountered some more intractable problems.

5.1 Style Handling

To convert RN’s styles into styles that can be used on the applet side, we initially identified two directions:

  1. Extract and transform at compile time
  2. Dynamically generated at runtime

Extract and transform at compile time

This method is actually the feasible solution we discussed at the beginning. The specific method is to write Webpack and Babel plug-in to extract RN code for style correlation during compilation and generate the final WXSS file.

The benefits of this scheme are:

Statically compiled files can be better managed, for the style transformation of the problem can be directly modified, equivalent to a visible native product to debug, for developers appear more friendly, more intuitive.

The result is ideally something like this.

For source code:

const styles = StyleSheet.create({ container:{ width:100, height:100 } }) 
< Viewstyle= {styles.container}> 
< Text> Hello World </ Text> 
</ View>
Copy the code

.container-test{width: 100px, height: 100px}

In a project, there are some irregular assignments due to style values such as screen width that need to be retrieved at runtime: Constheight = deviceInfo.isandroid? 100-120; conststyles = StyleSheet.create ({ height })

When this dynamic fetch of run-time values occurs, our strategy can’t handle it, so the solution has to be abandoned.

Dynamically generated at runtime

The basic implementation method of this solution is actually borrowed from the practice of React-native Web. Maybe Twitter also tried to generate CSS files during compilation and convert them to Web, but failed. The run-time scheme used to extract RN styles into classes and assign values to DOM elements. Of course, by analyzing the React-Native Web source library, we made some changes to adapt to the situation of wechat applets. Specific practices are as follows:

Take the most basic component View as an example:

import{ View , Text } from'react-native';
<View style={{flex:1}}> <Text>Hello World</Text> </View>
Copy the code

The converted className and style will be passed to the View component of Remax along with some default completion style. For a general View component, we will assign style as RN normally writes. The core processing that our adaptation layer does is to parse these styles, transform and process the Class and array styles, and generate the classList and style variables.

constconvertStyle = ( styles) => { constclassList = []; conststyle = {}; Constconvert = (value) => {// For Class, Classlist if(typeofValue === 'string') {classList.push(value)} else{// For arrays, If (array.isarray (value)) {value.foreach (item => {convert(item)})} else{// For general attributes, Value && Object.assign(style, inline(value))}}} convert(styles) return{classList, style}}Copy the code

The converted className and style are passed to the View component of Remax, along with some default completion styles.

// Default style supportedProps. ClassName = "view-default "+ classlist.join (" "); supportedProps.style = style; supportedProps.id = this.props.id; return(<RemaxView {... supportedProps}/>)Copy the code

At this point, Dom rendering at runtime yields the corresponding class name and inline style values.

So, for compile-time assignments such as DeviceInfo, the final value is also obtained at run time and added to the component’s style property, thus resolving issues that cannot be controlled at compile time.

Style summary and thinking

There are a lot of details to pay attention to when dealing with styles, such as unit conversion for different ends, completion Settings for some default styles for different ends, etc. This part focuses on the introduction of the solution selection process, without listing the specific style differences. More people are needed to make contributions and continue to invest in following up the Versions of React-Native and Remax. In addition, for the details of StyleSheet processing, students who are interested in react-Native Web can also check the source code, which is worth further study and study for students who are interested in cross-end development. As for the solution, we are also considering whether we can further improve it, such as combining compilation and runtime, so as to reduce in-line styles. Currently, writing in-line styles directly will still cause the attribute values on our DOM elements to be too long and unreadable, which is not conducive to development and debugging. On the other hand, this excessively long attribute also affects the performance of small programs

5.2 Processing of animation

Qrn-remax-unir adopts the idea of not introducing new writing method and keeping consistent with the original writing method of React-native as far as possible in realizing the animation scheme, so as to achieve zero invasion of RN code.

Here is a demo. The animation method in the code is the same as that in the official document of React-Native. Let’s have a look at the effect.

The single animation that defines React-Native looks like this:

Animated.timing( this.state.opacity, { toValue: 1, // 1 duration: 3000, // 2 easing:Easing.ease // 3 } ).start; / / 5Copy the code

Qrn-remax-unir converts the above code into the code similar to the one below that can be run in the small program. Key information of animation 1-5 is processed into one-to-one correspondence through the framework layer. The transition time is the animation time of React-native, i.e. duration. Animation-function: animation-function: animation-function: animation-function: animation-function: animation-function: animation-function: animation-function: animation-function Easing (some of the complex curves are difficult to fit and will be degraded in a small program, such as Linear). The initial and target values are simply passed through and the interpolator parses the original unit regulars into animated property values. The transition looks like this: 3000ms Opacity cubic- Bezier (0.42, 0, 1, 1);

Animated sequences are composed of Animated sequences for Animated combinations, parallel animations, and sequentially delayed animations, and Animated sequences are handled by the react-native Animated processing mechanism itself.

6 Use of component libraries

Component library address: github.com/qunarcorp/q…

Component Demo address: github.com/qunarcorp/q…

Qunarcorp.github. IO/qRn-remax-u…

To learn more about how to use our component library, download our sample project for development learning: Portal (github.com/qunarcorp/q…)

This project integrates the compilation and development capabilities of RN, Web and wechat small program, and it is very simple to start. You can carry out cross-end project transformation based on this project. The development effect of this project is shown as follows (from left to right :RN->Web→ wechat applet)

The project startup process is simple, go to the project root directory:

1. Install dependencies

yarn

2. Run dev environment

Yarn dev //RN,web environment YARN dev:wx // wechat small program, which needs to be opened with small program IDE./dist/wechat directory for debugging and development

At this time to modify the code can be real-time hot update each end of the code, is not very convenient! Get started on your journey of cross-end development!

If you want to learn more about how to build a cross-end project, see our github introduction: Portal and try your hand at it.