1. Preface
React native gave us the ability to develop native apps using JavaScript, and after using react native to complete interest tribe Android, we began to continuously optimize the interface implemented by react native. The goal is only one, while enjoying the new features brought by react native, the experience of the infinite approximation of the native implementation. As a front-end development, this article explores react native first-screen rendering best practices from the front-end perspective.
2. Time-consuming calculation method for first screen
2.1 Our focus on time-consuming
Optimizing first-screen rendering time requires defining a time-consuming measurement of the first screen. When you integrate the react native into your native app, you can define the first screen time as follows
First screen time =react native context initialization time consuming + first screen view rendering time consuming
Where the react native context initialization takes a fixed cost, which can be reduced to about 70ms by the time the initialization process is initiated asynchronously after the app is started, in Android. This article focuses on the first screen view rendering time-consuming, the optimization of the search is in the Android side react native version of the app, but its ideas and methods can be reused to the iOS side.
2.2 Rendering time-consuming measurement method
Focusing on the time-consuming rendering of the first screen view requires an understanding of the react framework view rendering process and the corresponding need to understand its life cycle approach. is a complete declaration cycle diagram of the react component, as can be seen in the top dashed box as the first phase of the life cycle, which completes the initialization and renders the component for the first time. The dashed box in the lower left corner is the component run and interaction phase, where the component may be rendered again. The bottom right corner of the dashed box is the third stage, and the component phase unloading is extinct.
In response to the above lifecycle approach, we first render the loading view at the initial stage and start pulling the data. Once the data has been fetched, the view is drawn on the screen by changing the state (the states), triggering the view to render again.
In combination with the above analysis, we define the first screen view rendering time as follows
First screen view rendering time =compontdidupdate end –compontwillmount start time
3. Turn on the exploration of rendering optimization
3.1 Output current rendering time-consuming
Now that you have a clear view rendering time-consuming calculation, you can take a time-consuming look at timing the log output. There are two ways to look at time consuming here.
A) Use debug mode to output the time difference in the Chrome debugging tool via Console.log
b) Encapsulate the native layer log method encapsulated to the react native layer call and output the log to the app log file.
The second method is recommended, which allows you to run the app in release mode, and the output is consistent with our normal installation run app.
What is the time-consuming output? Check the log that, in the WiFi environment, glory 4X mobile phone, rendering time is about 700ms, plus react native context initialization time of about 70ms times, the entire interface time-consuming needs about 770ms.
So in the native app implementation, how much does it take to find the tab rendering time? With the Android native app implementation, it was found that the tab from Oncreateviewex to Onresume ended and took about 100ms.
3.2来, plus the cache.
See the above data, my heart is a bit of a crash, said the high performance? But I'm not panicking, based on my lack of front-end development experience, I know there's a big trick that doesn't work, that is, using cached data.
React native provides us with the Asyncstorage module, Asyncstorage is a simple, asynchronous, persistent key-value storage system that is global to the app. We can use Asyncstorage as a data storage solution in the react native side, as opposed to the previous use of Localstorage to store data.
Before using the cache, our view rendering process is as follows
In the above process, you will need to wait for the network request before rendering the interface view. Here we will cache the request data, rendering the interface view, first using the cached data to render the view, while initiating a network request, the data is returned with new data to render again. The rendering process after using the cache is as shown
In the above process, when there is cached data, we will quickly get the cached data to render the interface view. The Render method is then triggered again when the network request returns data, and the view is rendered again with new data.
In order to improve performance, avoid unnecessary triggering of the Render method, you can determine the cache data and response data differences in the Shouldcomponentupdate method, only when the two copies of the data is inconsistent to trigger the Render method again. At the same time, thanks to the virtual Dom feature of the react framework, after the data returned by the network request has triggered the Render method again, react native calculates the DOM diff and determines the view update scope based on this.
At this point, we can see through the log data, in the cache scenario, we take the cache time is about 40ms, when our rendering time dropped to 400ms.
3.3 Taking over the carousel diagram
After adding the cache optimization, we reduced the rendering time to 400ms, but still had a large gap with the time-consuming implementation of the native. At this point, the optimization into the deep-waters, through the carding interface view, you can divide the view into the upper carousel map, the middle tribe list and the bottom three modules, one-to-five optimization.
In the original version, we implemented the carousel component in this way: in the return result of the request, take out the Carousel graph collection, generate the entire picture view sequentially, and add it to the Carousel view. Finally, listen to the corresponding event of the container view and set the picture view that is currently displayed.
As shown, five picture views are added to the Carousel diagram container when there are five carousel graphs in the resulting data. Initially only the first picture view is visible, and when the sliding event occurs in the container, the picture view's visible state changes.
From the above process can be seen an obvious problem: on the first screen, in the Carousel Diagram container, the non-visible state of the rest of the picture view rendering is meaningless.
Now that you have found the problem, optimization is a natural idea: when you get a collection of carousel images from the request results, instead of generating all the picture views, you generate only one copy, add the picture view to the container, and set the URL address of the current picture for him. When a container sliding event occurs, instead of toggling the visible state of the different views, the image view is reused, and the URL of the view picture is switched, as shown in the process.
With this scheme, the rendering time of the picture view is completely removed from the non-visible state, and the memory consumption is reduced by reusing the view. This scheme theoretically renders time-consuming and memory-hogging optimal, but in practice there is an experience problem: When the slide occurs, loading the next image and refreshing the picture view will cause the sliding events to lag in the container, making the slide feel a block. So we ended up with a compromise: when there are more than three carousel sets, add the current picture view to the container and add the previous and next picture views of the current picture in advance. When a sliding event occurs in a container, the three views are reused to set the current picture, previous and next picture views for this event.
With the time-consuming data before and after the log output optimization, when using this scenario, the rendering time is reduced by about 30ms, when our first screen rendering time drops to about 370ms.
3.4 Adding effects to a list
In the middle of the Discovery tab interface, is a subcategory of the tribe list. The number of subcategories is usually two, and under each subcategory there are typically seven to eight list items, each of which consists of a tribal icon and a tribal name.
In the initial version, we get the sub-categories from the request result data to the Tribe collection, to ScrollView as a container, create the list item view and add it to the container. This way, when all the list items are rendered, the tribe list of the subclass is rendered complete, and the initial implementation is shown below.
The above process can be found, in the tribal list rendering process, waiting for the non-viewable area of the list item image download and then render the view, for the first screen is meaningless.
Combining the experience of optimizing the Carousel diagram component, we try to delay loading the list item view. The ideal state is to render only a few list items visible on the first screen, a list item in a non-visible area, a deferred rendering, and the flow as shown below
Soon we ran into problems, and in the Carousel diagram component, the first screen only shows the picture. But in the Horizontal tribe list, how many list items should be rendered on the first screen? The answer is that we cannot determine in advance that the list item is visible on the first screen, and only if the item is visible at render time, based on the relative position of the current list item in the landscape list.
Continue to think, though we cannot foresee a few list items on the first screen, but using the API method provided by react native, we can get the coordinates of the current view relative to the container.
void)
With the Measurelayout method, after the view completes the layout and triggers its OnLayout event callback, the container node can be passed through the Measurelayout method of the view to get the relative position of the current view in the container. With this feature, combined with our view features-the size of the list item view is deterministic, you can render all list item views as empty views of this particular size at the beginning, and after all empty view rendering is complete, get the view in the container relative to the location, and only the list items within the viewable area are re-rendered with the real view. While listening to the scrolling events of the list container, in the scrolling event callback, the relative position of the view in the container is calculated, and when the view enters the viewable area, the empty view is replaced with a real view, as shown in the following procedure
Through the above scheme, we can realize the delay rendering feature of the list item, through the log output time, we can find that through this optimization, the rendering optimization can be reduced by about 30ms.
In the above scenario, the screen in the first time to render a blank view and re-render the first screen visible list item view, there is a momentary flicker of the feeling, in order to solve this experience problem, you can use the placeholder bitmap scheme to replace the empty view scheme, that is, the first is to use a static resource as a bitmap to render the list item icon, Its scenario is as follows
The implementation effect is as follows, the first rendering is used as a bitmap, the first screen in the list item icon view is downloaded and re-rendered. In this implementation, the empty view caused by the screen flashing problem, quickly complete the first rendering, the icon image of the delay loading, rendering time is slightly increased, compared to the empty view scheme time-consuming increase of about 10ms, at this time our first screen rendering time to about 350ms.
3.5 The destination of popular post modules
There is also a module in the Lower tab view, as shown below, which is the display area for several popular posts. For the first-screen content, this module is all part of the non-viewable area, so we need to try to take this piece of time away from the first screen time.
With the optimization experience of the above tribe list, we already know that after the layout event is complete, we can get the relative position of the view within the container, and for scrolling views such as ScrollView, we can listen to his scrolling events to determine the location of the view distance from the viewable area to determine whether to render the view, in order to optimize the experience, We then set an advance threshold aheaddistance for the rendering of the view, which renders the view in advance when the view is Aheaddistance units away from the viewable area, as shown in
With this scenario, the rendering time of the popular post module has been removed from the first screen time-consuming analysis, and the first-screen rendering time has dropped to about 270ms.
So is the optimization work done? No, in the optimization done, the review experience, we found that there are still experience problems. This experience issue is related to the view structure where tab is found. In the Find tab, the hot post module is not far from the bottom of the viewable area. Because it is close to the bottom of the visible area itself, so the ahead distance setting does not have the desired effect, resulting in the Discovery tab interface down, because the real-time to render the popular post module, so that the slide is not smooth, there is a blocking feeling.
In order to solve this experience problem, continue to the popular post module has been customized optimization, and finally to the popular post module asynchronous rendering. In the first screen rendering, or to render the popular post as an empty view, and then the ahead distance, so that the empty view falls in the ahead distance area, and then use the settimeout technique, asynchronously to calculate the empty view in the container position, And render a true view of popular posts.
With the above scheme, deferred rendering is replaced with asynchronous rendering, which is based on not increasing the time-consuming of rendering, but also solves the problem of slipping, and finally the time of first screen rendering is fixed at about 270ms.
4. Summary
The React native framework gives us new capabilities that enable us to develop native apps with JavaScript. As we walk into the deep waters of the react native application and begin to refine all aspects of him, the best practices we accumulate on the web side remain valid, such as cache usage, element reuse, lazy loading, asynchronous loading, and so on.
At the same time, the time-consuming optimization of rendering also takes into account the experience that we should pursue, not just fast. But on a fast basis, there is also a good use experience.
Optimization exploration to the end, we will take the first screen rendering time is fixed at about 270ms, plus react native initialization time of about 70ms, our first screen total time is still about 340ms, compared to the native implementation of the gap still exists. Native implementation of the rendering speed is also through the layer of optimization, and our react native best practice exploration is not over, welcome to guide, explore.
Explore react native first-screen rendering best practices