Problem Description
Certain applications, such as news apps, use web components to load corresponding cloud-based HTML pages. When the content of some pages is large, the page loading speed is slow, generally taking more than 20 seconds, which affects the user experience.
Background Knowledge
Accelerating Web page access: When a Web page loads slowly, capabilities such as preconnection, preloading, and prefetching POST requests can be used to speed up Web page access.
Troubleshooting Process
-
Check Network Latency & Bandwidth
- Confirm whether the delay is caused by poor network conditions or large resources.
-
Inspect Resource Size
- Identify if the target HTML page or subresources (JS, CSS, images) are unusually large
Analysis Conclusion
For optimizing slow web component loading:
- Capabilities such as preconnection, preloading, prefetching POST requests, and precompiling to generate compilation caches can be used to accelerate Web page access.
- Loading speed can be optimized through template quick loading.
Solution
- Pre-parsing and preconnection The prepareForPageLoad() method can be used to pre-parse or preconnect the page to be loaded. The Web kernel can be initialized in advance in the onCreate method of the Ability, and the homepage can be preconnected. Example code is as follows:
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
console.info("EntryAbility onCreate");
webview.WebviewController.initializeWebEngine();
// During pre-connection, you need to replace 'https://www.example.com' with the actual website address you intend to access.
webview.WebviewController.prepareForPageLoad("https://www.example.com/", true, 2);
AppStorage.setOrCreate("abilityWant", want);
console.info("EntryAbility onCreate done");
}
}
-
Preloading
It is possible to predict the page that a Web component is about to load or the page it is about to navigate to. The page that is about to be loaded can be preloaded using the
[prefetchPage()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-webview-webviewcontroller#prefetchpage10)method. Preloading downloads the resources required for the page in advance, including main resources and sub-resources, but does not execute the JavaScript code of the webpage. Preloading is an instance method of the WebviewController and requires a WebviewController instance that has already been associated with a Web component. In the following example, the preloading of the next page to be visited is triggered at theonPageEndevent:
// xxx.ets
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
webviewController: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({ src: 'https://www.example.com/', controller: this.webviewController })
.onPageEnd(() => {
//Preload https://www.example.com/
this.webviewController.prefetchPage('https://www.example.com/');
})
}
}
}
-
Pre-fetching POST Requests
You can pre-fetch POST requests that will be loaded on the page using the
[prefetchResource()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-webview-webviewcontroller#prefetchresource12)method. When the page loading is complete, you can clear the pre-fetched resource cache that is no longer needed usingclearPrefetchedResource. In the following example, the POST requests for the page to be loaded are pre-fetched in theonAppearmethod of the Web component. In theonPageEndmethod, the pre-fetched POST request cache can be cleared:
// xxx.ets
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
webviewController: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({ src: "https://www.example.com/", controller: this.webviewController})
.onAppear(() => {
// When prefetching, you need to replace "https://www.example1.com/post?e=f&g=h" with the actual website address you want to visit.
webview.WebviewController.prefetchResource(
{url:"https://www.example1.com/post?e=f&g=h",
method:"POST",
formData:"a=x&b=y",},
[{headerKey:"c",
headerValue:"z",},],
"KeyX", 500);
})
.onPageEnd(() => {
// Clear the pre-fetched resource cache that will no longer be used.
webview.WebviewController.clearPrefetchedResource(["KeyX",]);
})
}
}
}
-
Precompile to Generate Compilation Cache
You can precompile the script files to generate a compilation cache before the page loads by using the
[precompileJavaScript()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-webview-webviewcontroller#precompilejavascript12)function. It is recommended to use this in conjunction with dynamic components. Utilize offline web components to generate bytecode caches and load business-specific web components at the appropriate time to utilize these bytecode caches. Reference code is as follows:
// CarrierAbility
// EntryAbility.ets
import { createNWeb } from "../pages/common"
onWindowStageCreate(windowStage:window.WindowStage):
void {
windowStage.loadContent('pages/Index',(err,data)=>{
// Create a Web dynamic component (need to pass in UIContext), which can be created at any time after loadContent
createNWeb
(
"https://www.example.com",
windowStage.getMainWindowSync().getUIContext());
if(err.code){
return;
}
}
);
}
// Create a Web dynamic component (need to pass in UIContext), which can be created at any time after loadContent
// common.ets
import { UIContext, NodeController, BuilderNode, Size, FrameNode } from '@kit.ArkUI';
import { webview } from '@kit.ArkWeb';
// @Builder is the specific component content of the dynamic component
// Data is the parameter encapsulation class
class Data {
url: string = "https://www.example.com";
controller: WebviewController = new webview.WebviewController();
}
@Builder
function WebBuilder(data: Data) {
Column() {
Web({ src: data.url, controller: data.controller })
.width("100%")
.height("100%")
}
}
let wrap = wrapBuilder<Data[]>(WebBuilder);
// Used to control and feedback the behavior of the node on the corresponding NodeContainer, and needs to be used together with NodeContainer
export class myNodeController extends NodeController {
private rootnode: BuilderNode<Data[]> | null = null;
// Required overridden method for building the node count and returning the node mounted in the corresponding NodeContainer
// Called when the corresponding NodeContainer is created, or refreshed via the rebuild method
makeNode(uiContext: UIContext): FrameNode | null {
console.info(" uicontext is undefined : " + (uiContext === undefined));
if (this.rootnode != null) {
// Returns the FrameNode node
return this.rootnode.getFrameNode();
}
//Return null to control the dynamic component to detach from the binding node return null;
}
// Callback when the layout size changes
aboutToResize(size: Size) {
console.info("aboutToResize width : " + size.width + " height : " + size.height);
}
// Callback when the NodeContainer corresponding to the controller appears
aboutToAppear() {
console.info("aboutToAppear");
}
//Callback when the NodeContainer corresponding to the controller disappears
aboutToDisappear() {
console.info("aboutToDisappear");
}
// This function is a custom function and can be used as an initialization function.
// Initialize the BuilderNode using UIContext, and then initialize the contents of @Builder using the build interface in BuilderNode.
initWeb(url: string, uiContext: UIContext, control: WebviewController) {
if (this.rootnode != null) {
return;
}
// Create a node, requires uiContext
this.rootnode = new BuilderNode(uiContext);
// Create dynamic web components
this.rootnode.build(wrap, { url: url, controller: control });
}
}
// Create the NodeController needed to save the Map
let NodeMap: Map<string, myNodeController | undefined> = new Map();
// Create the WebViewController required to save the Map
let controllerMap: Map<string, WebviewController | undefined> = new Map();
// Initialization requires UIContext, which must be obtained in Ability
export const createNWeb = (url: string, uiContext: UIContext) => {
//Create NodeController
let baseNode = new myNodeController();
let controller = new webview.WebviewController();
//Initializing a custom web component
baseNode.initWeb(url, uiContext, controller);
controllerMap.set(url, controller)
NodeMap.set(url, baseNode);
}
// Customize the NodeController interface
export const getNWeb = (url: string): myNodeController | undefined => {
return NodeMap.get(url);
}
// Using NodeController's Page
// Index.ets
import { getNWeb } from "./common"
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
// NodeContainer is used to bind to NodeController nodes. Rebuild triggers makeNode.
// Pages bind to NodeController via the NodeContainer interface to implement dynamic component page display.
NodeContainer(getNWeb("https://www.example.com"))
.height("90%")
.width("100%")
}
.width('100%')
}
.height('100%')
}
}
Note:
It is not recommended to have more than 200 Web instances running in the background.
Verification Result
- Expected load time reduction from 20+ seconds → ~5–8 seconds.
- Confirm reduced first-paint delay and improved user experience.
Top comments (0)