Unlike traditional H5 applications, the miniapp runtime architecture consists of two parts: a webview and a worker. The webview handles rendering, while the worker stores data and executes business logic.
Communication between the webview and the worker is asynchronous. This means that when you call setData, your data is not rendered immediately. It must be transferred asynchronously from the worker to the webview.
During data transfer, the data is serialized into a string and then transferred using
evaluateJavascript. The data volume affects performance.
Optimize the first screen
The term "first screen" can be defined in several ways. In this context, it refers to the first meaningful rendering from a business perspective. For example, on a list page, the first screen is the content that is rendered when the list first appears.
Control the miniapp resource package size
When a user accesses a miniapp, the Alipay client first downloads the miniapp resource package from a CDN. Therefore, the size of the resource package affects the startup performance of the miniapp.
Optimization suggestions:
Delete unused image resources promptly because all image resources are included in the package by default.
Control image sizes and avoid using large images. Upload large images from a CDN.
Clean up unused code promptly.
Move data requests to onLoad
Some miniapps send requests in onReady, which delays the rendering of the first screen.
When a miniapp runs, the onLoad lifecycle function of the page is triggered first. Then, the initial page data is sent from the worker to the webview for the initial rendering.
After the initial page rendering is complete, the webview sends a notification to the worker, which triggers the onReady lifecycle function.
Optimization suggestion: Move data requests to onLoad.
Control the number of nodes rendered at once on the first screen
After a business request is returned, setData is typically called to trigger a page re-rendering. The process is as follows:
Data is transferred from the worker to the webview.
The webview constructs a virtual DOM based on the received data, compares it with the previous DOM for differences starting from the root node, and then renders the updates.
Because data must be serialized for communication between the worker and the webview and then executed in the webview using evaluateJavascript, transferring a large amount of data at once affects the rendering performance of the first screen.
Additionally, if too many nodes with deep nesting levels are constructed in the webview, the diffing time increases. For example, some miniapp list pages render over 100 list items at once, each with nested content, even though the screen can display fewer than 10. This initial rendering constructs many DOM nodes at once, which negatively affects rendering performance.
Optimization suggestions:
Keep the data volume for setData small. Avoid passing excessively long lists at one time.
On the first screen, do not construct too many nodes at once. The server-side might return a large amount of data in a single request. Instead of passing all the data to setData at once, pass only a portion of the data first. Then, after a short delay, such as 400 ms that you can adjust as needed, call
$spliceDatato transfer the remaining data.
Optimize setData logic
Any page change triggers setData. Multiple setData calls can trigger page re-rendering at the same time. The following four interfaces all trigger webview page re-rendering.
Page.prototype.setData: Triggers a diffing comparison for the entire page.Page.prototype.$spliceData: This is an optimization for long lists that avoids passing the entire list with each update, preventing a diff comparison on the entire page.Component.prototype.setData: Starts the diffing comparison only from the corresponding component node.Component.prototype.$spliceData: Optimized for long lists. This method avoids passing the entire list and starts the diffing comparison only from the corresponding component node.
Optimization suggestions:
Avoid frequently triggering setData or
$spliceDataat both the page and component levels. For example, some pages that we analyzed had countdown logic that was triggered too frequently, such as every few milliseconds.When frequent re-rendering is required, avoid using page-level setData and $spliceData. Instead, encapsulate this logic into a custom component. Then, use the component-level setData or
$spliceDatato trigger component re-rendering.When you render data for a long list, use
$spliceDatato append data in multiple calls instead of passing the entire list at once.For complex pages, encapsulate logic into custom components to reduce page-level setData calls.
Optimization example:
Use $spliceData for long lists:
this.$spliceData({ 'a.b': [1, 0, 5, 6] })Recommended: Set data by specifying a path:
this.setData({
'array[0]': 1,
'obj.x':2,
});Not recommended: The following usage is not recommended. Although this.data is copied, its properties are still modified directly:
const array = this.data.array.concat();
array[0] = 1;
const obj={...this.data.obj};
obj.x=2;
this.setData({array,obj});Strongly not recommended: Do not directly modify this.data. This violates the immutable data principle:
this.data.array[0]=1;
this.data.obj.x=2;
this.setData(this.data)onPageScroll event on the page, you need to notify the corresponding component to re-render when the event is triggered. The following code shows how to handle this: // /pages/index/index.js
Page({
onPageScroll(e) {
if (this.xxcomponent) {
this.xxcomponent.setData({
scrollTop: e.scrollTop
})
}
}
}) // /components/index/index.js
Component({
didMount(){
this.$page.xxcomponent = this;
}
})Mount the component to its page in the component's didMount method. This lets you call the component-level setData from the page to re-render only that component.
Use the key parameter
Use the key parameter in a for loop to improve performance.
The key parameter cannot be set on a block.
Example code:
<view a:for="{{array}}" key="{{item.id}}"></view>
<block a:for="{{array}}"><view key="{{item.id}}"></view></block>