1.1 How applets work
1.1.1 Dual-thread Architecture and Exparser Framework
Common page rendering methods
- Naitve: Smooth, but not flexible enough to modify and publish
- Web rendering: It is flexible to modify and publish, but loading can be erratic and rendering is sometimes not smooth
- Hybrid rendering: There are many technical solutions, which require selection and careful design to find a solution that gives consideration to both speed and fluency
Hybrid rendering
Naitve and the Web both have their strengths and weaknesses, and we wanted a hybrid solution to balance them out. Here are three solutions
- PhoneGap: It runs on the same common web pages, just a shell of the page, native provides some API calls, and it does have some native experience
- React-native: provides the same experience as Native, but not as stable
- Js-sdk: Provides some native ability to take pictures and scan
Wechat applet selection
Hybrid scheme based on JS-SDK and resource offline cache
- The interface is rendered using Web technology
- The page is rendered using a separate Webview
- Resources are packaged once downloaded and cached for use, and updated asynchronously
- Provides native components and native capability invocation interfaces
Two-threaded architecture
Small programs are divided into logic layer and rendering layer. Communication between them needs to be through Native. Each page opened opens up a WebView, which is rendered independently; So when you return, you load it directly from memory
Advantages:
- Fluency: Render layer and logic layer run on different threads without blocking
- Security: The logical layer cannot directly modify the content of the render layer, nor can it directly retrieve sensitive data
- Flexible and lightweight: The basic library is integrated in wechat, and the business code is lightweight. Both can be updated remotely
- Rendering is mainly web technology, with the efficiency and flexibility of Web development
Disadvantages:
- Development cost: different from traditional development syntax and structure, existing functions can not directly run on the small program, need to transplant costs
- Non-real-time: Due to the separation of rendering layer and logic layer, most operations become asynchronous, complicated scene processing cumbersome
- Power limitations: Page size, number of open pages, and memory reclamation are all limited and uncontrollable
- Performance optimization: Different from traditional Web development performance issues, performance needs to be optimized separately
Exparser framework
Responsible for the organizational framework of components in wechat applets, including the management of built-in components and custom components
- Based on the Shadow DOM model, but independent of browsers and other tool libraries
- Can be run in the JS environment, so that the logical layer and render layer model is consistent
- High efficiency, light weight, good performance, especially in the environment with a large number of component instances, and small code size
Custom component creation process
- Creates a Component object from the contents of the Component constructor
- Adds the data declared at registration time to the component object
- Combined with WXML, the component Shadow Tree is generated
- The Shadow Tree loads the Compose Tree
- Created and Attached events of components are triggered in sequence
1.1.2 Communication principle between Web and Native
Web sending data to Native is important
- URL interception Scheme: A Native WebView intercepts Web requests in a specific format sent by a Web page
- Intercept Prompt API: Native WebView intercepts calls to the Window. prompt API in Web pages
- Native API injection: Native injects javaScript methods directly into the context of the javaScript environment for invocation
1. Block the URL Scheme
net://post? Id =123 NET is a user-defined protocol, which can be defined as NET or ABC
- Scheme is the URL protocol name of the application, for example, NetEase in the preceding code
- The client can capture the WebView request through API and decide whether to intercept and parse according to Scheme
// This method is not recommended and only applies to demo or low-frequency scenarios
// Because the client will only receive the last message when many consecutive calls are made
location.href="net://post? id=3"
// It is recommended to use this method so that messages are not lost
const iframe = doucment.createElement('iframe')
iframe.style.display='none'
iframe.src='net://post? id=123'
doucment.body.appendChild(iframe)
setTimeout(() = >{
iframe.remove()
},100)
// oN ios: can receive urls with at least 3000W characters;
// Android can receive urls of at least 200W characters
// Message length is bad for performance; You need to consider whether to transmit such a large amount of data directly
Copy the code
Advantages:
- Send simple (location.href or iframe.src)
- Strong compatibility (IOS2.0+ Android 1.0+)
- Low intrusion disadvantages:
- Message length limit (?)
- Too flexible, lack of restrictions, prone to error
2. Intercept prompt APIS
-
Native can capture and intercept the WebView in alert/confirm/console/prompt API calls
-
Intercept prompt is more common because it is used less frequently and is less prone to conflicts
- IOS: runJavaScriptTextInputPanelWithPrompt
- Android: WebChromeClient. OnJsPrompt
// It is best to check whether the WebView is in the APP before calling
const isInAPP = /net/i.test(navigator.userAgent)
isInAPP && prompt('post? id=1')
Copy the code
2.Native direct injection
- Javascript in WebView runs in the environment provided by WebView, so Native can directly intervene in the context of javascript execution through WebView.
// IOS UIWebviewJScontext * context = [uiWebViewvalueForKeyPath:"documentView.webView.mainFrame.javascriptContext"];
context["postBridgeMessage"]= ^(NSArray<NSArray *>*calls){
/ / Native logic
}
// Android
// Where @javascriptInterface annotation is a security mechanism, you can search for details
public class BridgeLogic {
// @JavascriptInterface
public void sendData(String message) {
/ / native logic
}
}
private void initView() {
webView=(WebView) findViewById(R.id.webview);
// Enable webView Javascript manual execution default is false
webview.getSettings().setJavascriptEnabled(true)
// Pass in an instance of BridgeLogic and mount it to Javascript window.jsbridge
webview.addJavascriptInterface(new BridgeLogic(),"JSBridge")}// Javascript calling code on the Web side
/ / IOS UIWebView
window.postBridgeMessage('post? id=123')
window.JSBridge.sendData('post? id=123')
Copy the code
Advantages:
- Relatively high execution efficiency disadvantages:
- Data formats are flexible, but learning costs are high
- Both ends have strong coupling
- Possible namespace conflicts (Window objects)
Native is important for sending data to the Web
We can register a method on the window,native can call this method and pass the value
- Ios: stringByEvaluatingjavaScriptFromString (this method can get directly to the javascript execution results)
// Swift
let title = webview.stringByEvakuatingjavaScriptFromString("document.title")
// OC
NSString*title = [webview stringByEvakuatingjavaScriptFromString:@"document.title"]
Copy the code
- Android: loadUrl(4.4, cannot get javascript execution results directly)
// java
webView.loadUrl("javascript:alert('NativeMessage')")
Copy the code
- Evaluatejavascript Android: Evaluatejavascript (4.4+)
// java
mWebView.evaluatejavascript("javascript:document.title".new ValueCallback<string>(){
@Override
public void onReceiveValue(Strinf value){
// return the result for js}})Copy the code
1.1.3 Native Components and same-layer rendering
The native components
Advantages of native components:
- Extended Web capabilities: better keyboard control for input fields, for example
- Better experience: Map and video interaction rendered by Native thread without occupying Webview rendering thread
- Faster performance L: In some high-frequency operating scenarios, it is possible to bypass the setData communication flow and render directly
Limitations of native components:
- The CSS is not fully supported
- Limitations in UI rendering
- Only at the highest level
- Limitations of the event model
When debugging native components are involved in development, it is best to verify them in a real machine environment. Developer tools cannot fully simulate the features of native components
The render tree
Due to the limitations brought by native components, wechat development in addition to the same layer rendering, native components of the Webview is the same layer, to solve the event binding and CSS problems
Realize the principle of
ios
- WkWebView will generate the native render layer WKChildScrollView for overflow: Scroll elements
- The rendering relationship between the native render layer and other Web elements is maintained internally by WKWebView
- Native components can be inserted into the native render layer generated for Scroll
Android
- There are embed type nodes in Chromium
- Chromium creates Webplugin instances for Webview embed nodes and generates a RenderLayer
- RenderLayer can render externally set content
- Applets can render the content of native components onto RenderLayer generated by the corresponding Embed node
Using Embed rendering PDF, small program is also using this feature to achieve the same layer rendering
Same layer after rendering
Position, margin, box-shadow, transform, and so on are basically supported by the rendering hierarchy controllable: z-index can be used to control the rendering hierarchy of native components. Events on a native component can bubble up to the parent element for listening or blocking, still with some limitations:
- Avoid frequent changes to the CSS
- You cannot set border-radius to capture display content through this component or its parent
- Lower versions of Android may not support it
- After the native component is in full screen, elements outside the native component are not visible
1.2 Performance Optimization
Implications for performance optimization
Applets also need performance optimization
- Cold start takes a long time to load and the loading state is long (cold start: first start)
- Pages with large data volume are not updated in time, and scrolling is stuck
- Some touch feedback classes don’t function smoothly
1.2.1 Speed up the startup of small programs
startup
Core: reduce the size of packages that need to be downloaded when small programs start
-
Use of the subcontract
- The non-home page or non-important page of a small program can be subpacked to reduce the size of the package downloaded for the first time
- Subcontracting can also increase the overall package size limit of the applet and help speed up subsequent first-screen rendering
{
"pages": ["pages/index/index"]."subpackages": [{"root":"packageA"."pages": ["pages/share/index"]]}}Copy the code
- Subcontract predownload
- Normally, subcontracting will trigger the download only when the subcontracting page is accessed, which reduces the opening speed of the subcontracting page
- The subcontracted predownload can automatically download the subcontracted content when visiting a page, so as to download the subcontracted content in advance without affecting the loading speed of the main package
{
"pages": ["pages/index/index"]."subpackages": [{"root":"packageA"."name":"nameA"."pages": ["pages/share/index"]}],"preloadRule": {"pages/share/index": {"network":"all".// What network case of pre-download, wifi/all
"packages": ["packageA"] // Subcontract root and name}}}Copy the code
-
Independent of the subcontract
- Standalone subcontracting can run independently of the main package without the need to download the main package
- The main package is downloaded only when you enter the normal subcontracting or main package page
- Standalone subcontracting can also be configured to pre-download the main package
- A small program can have multiple independent subcontractors
{
"pages": ["pages/index/index"]."subpackages": [{"root":"packageA"."name":"nameA"."pages": ["pages/share/index"]."independent":true}]."preloadRule": {"pages/share/index": {"network":"all".// What network case of pre-download, wifi/all
"packages": ["__APP__"] // You can pre-download the main package}}}Copy the code
- Other ways to optimize startup speed
- Delete useless code and enable upload compression to reduce package size
- Reduce the size and number of images in your package and recommend using Webp images
- Use web-view components appropriately
1.2.2 Speed up the first screen rendering of small programs
-
Block rendering: Render screen height content first, and render other content when ready
-
Data prepull: Prepull or periodically update data interfaces on the first screen to save data request time
-
Skeleton screen: Use skeleton screen to reduce user perceived page rendering time
-
Avoid redundant data:
- Avoid empty lifecycle methods
- Reduce synchronous method calls during initialization
- Avoid too many or too complex main package pages and too many custom components
- Data that does not participate in rendering uses pure data fields
Data is prepulled and periodically updated
- Data prepull When the applet starts cold, the data on the specified interface can be pulled in advance, saving the data request time. - Periodic update The data on the specified interface can be pulled every 12 hours according to the configuration of the applet. The applet does not need to be started, but must have been used within 7 daysCopy the code
App({
onLaunch() {
wx.setBackgroundFethcToken({
token:'xxx' // The user id stored in the applet can be sent
})
wx.getBackgroundFetchData({
fetch:'pre/periodic'.// Prepull or periodic update type
success(res) {
console.log(res.fetchData) // Cache data
console.log(res.timeStamp) // The client gets the timestamp of the cached data}})}})Copy the code
First screen rendering – Skeleton screen
Skeleton screen document
Use developer tools to generate, and then introduce the business page, show hide
1.2.3 Accelerate the dynamic rendering of small programs
Reduce the amount of data passed by setData
- SetData involves interprocess communication, especially Native delivery to the Web using EvaluateScript. Large amounts of data require longer processing time
- You can pass only the changing parts of data, or you can combine multiple setData into a single call
Use custom components correctly
- If frequent updates to the UI of a part of the rendering layer are unavoidable, you can declare that part as a separate custom component because the data updates within these components are independent and less computationally expensive
- Remove unnecessary dataset attributes from the custom component because the data is collected and passed to the logical layer each time the time is fired
- The number of custom components in a page should not be too high, otherwise it will affect the rendering speed of the first screen due to the registration cost of custom components
The use of WXS
WXS is a script that runs directly in the render layer, in the browser, so there are some capabilities and limitations.
- Syntactically, only es5-like standards are supported
- Isolated from the logical layer javascript scope
- Indirect communication with the logical layer can be achieved by calling methods of component instances or by issuing custom events
- You can get or modify the style of elements on the component directly
// Export the variable pull<wxs module="pull" src="./pull.wxs">
<scroll-view
class="pulldown-wrapper"
bindtouchstart="{{pull.touchStart}}"
bindtouchstart="{{pull.touchMove}}"s
bindtouchstart="{{pull.touchEnd}}"
change:prop="{{pull.monitorShowRefresh}}"
prop="{{showRefresh}}"
>
<view>{{text}}</view>
</scroll-view>
Copy the code
WXS communication mode
Dynamically set data to WXML
WXS portal