Author: Duan Jiashun
background
As React Native technology is widely applied in businesses, some important functions are implemented by React Native technology solutions, which puts forward higher requirements on the opening speed of React Native pages, because the opening speed is one of the important reasons affecting the user bounce rate.
unpacking
As for the optimization of React Native opening speed, the most common solution in the industry is to preheat and split the base package, which reduces the container initialization time and base library loading time.
Unpacking React Native can rely on the official tools, but the official capability is a split loading inside JS. If we need to do container preheating, we cannot use the official loading solution, but need to transform the original client logic into multi-step loading.
We need to modify the React Native logic, so we need to understand the React Native initialization logic.
The diagram above shows an overview of the process. Here we briefly illustrate some of the key steps.
- After the RCTBridge is instantiated, the JS runtime thread and native module are first prepared.
- A JSExcutor is then created, which determines whether the JS execution environment is client-side or remote debugging (Android can be its own custom executor, such as V8).
- Load the source code (bundle), which may be loaded locally or remotely via a URL, depending on the source.
- Since initializing the JS executor and the code are triggered in parallel, a fence is needed to synchronize the results of the two and then start putting the code into the executor (JS code running).
- After that, the client listens for a vSYNC signal, which means that the page needs to be refreshed when it changes.
- At this point, the RootView receives the notification that JS loading is complete and starts to trigger the RunApp logic, which is to start the corresponding application in the app registry of the front-end.
The whole process is quite long, but the division of labor is quite clear, and the place for the unpacking and transformation is also very clear.
The green box in the figure above is the point of our transformation. From the perspective of simplicity of implementation and current requirements, the loading code is designed as serial loading. If further optimization is needed, the loading process can also be designed concurrently.
Here we abstract the loading capability once, loading a piece of code is defined as a SourceLoader, then a unpacked Bridge is equivalent to a list of loaders, corresponding to the attributes on the bridge is very simple.
@property (nonatomic.strong) NSArray<id<RCTBridgeSourceLoaderProtocol>> *preloadSourceLoaders; // Preloaded loader
- (void)preloadSourceWithCompletion:(void(^) (NSError *error))completion; // Triggers the preload loader
@property (nonatomic.strong) NSArray<id<RCTBridgeSourceLoaderProtocol>> *sourceLoaders; // Non-preloaded loader
- (void)loadSourceWithCompletion:(void(^) (NSError *error))completion; // Triggers the non-preloader
- (void)loadAllSourcesWithCompletion:(void(^) (NSError *error))completion; // Load the preloaded code first, then load the non-preloaded code
Copy the code
One important thing to note here is that we need to enable a vSYNC signal listener, which for performance purposes needs to be enabled when the warmup container is loaded into the real view, so we add a flag to the loader so that the listener can only be enabled after the loader is loaded.
React Native now supports multipackage distributed loading. We can pack some basic JS code into the app and reduce the package size.
Container preheating
Subcontracting loading alone does not have much effect on the loading speed, and the real optimization point is the preheating of the container. Preheating can do a lot of preparatory work first, and only trigger the loading of business code and rendering of the page during business loading.
Due to the limitations of the phone’s performance, and some of Apple’s official policies, it’s unlikely that we’ll be able to use this capability indefinitely, so here are 3 ways to warm up.
Preheating trigger time
At present, the timing of preheating trigger mainly includes the following three points
- Cold start
- Warm start
- After container reuse
After these timing triggers, a certain amount of time (5 seconds) is delayed to create the preheated instance. The reason for the delay is that CPU intensive tasks are most likely to be performed during these times. If non-essential tasks such as creating a preheated container are added at this time, some performance of the main service may be affected.
Preheated container destruction
At present, the destruction opportunities mainly include the following two
- Memory warning
- Into the background
The purpose of background destruction is mainly to reduce the memory of background operation. Although the proportion of this memory is not large, Apple still has a strict strategy for background operation at present, so we try to reduce the existing influence.
Scenario of preheating
Since React Native is not responsible for major scenarios in our business, most users may not use React Native, and it is not an optimal way for us to enable the preheating function for all users without any difference. At present, we do not have the ability to perform intelligent analysis of user scenarios such as machine learning, so this time we will make a simple classification of some scenarios:
- If the user has not used React Native in the past three days, it is assumed that the user will not use React Native in the future
- No records of destruction due to memory warnings within 3 days
- Within the current application startup period:
- If the preloading fails for three times, the preloading is no longer enabled in this week
The key point of container preheating is to improve the hit ratio of container preheating as much as possible without affecting other user experiences. Some of the current strategies are relatively simple, and if we want to optimize them later, we need to go deep into business scenarios.
The effect
When the unpacking and preheating capacity are enabled, the size of the bread on the mall page is reduced from 1.1m to 856K, and the page opening time is reduced from 450ms to 200ms in iOS and from 4500ms to 2500ms in Android. The optimization effect of both ends is very obvious. It shows that our optimization scheme is very effective.
conclusion
The optimization of React Native is divided into two parts: unpacking and preheating, with their respective capabilities independent and AB control respectively to ensure maximum stability. Later, I will go deep into business scenarios to do some optimization strategies.
This article is published from netease Cloud Music big front end team, the article is prohibited to be reproduced in any form without authorization. Grp.music – Fe (at) Corp.Netease.com We recruit front-end, iOS and Android all year long. If you are ready to change your job and you like cloud music, join us!