• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Accelerating Web Page Access
2
3When the web page loads slowly, you can use the capabilities of pre-connection, preloading, and prefetching POST requests to accelerate the access to the web page.
4For details about how to optimize the web page loading performance, see [Performance Optimization for Web Page Loading](https://developer.huawei.com/consumer/en/doc/best-practices/bpta-web-develop-optimization#section128761465256).
5
6## Preparsing and Preconnecting
7
8You can preparse or preconnect to the page to be loaded using [prepareForPageLoad()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prepareforpageload10), which is used for domain name-level optimization. This method only performs DNS resolution on URLs and establishes TCP connections, but does not obtain main resources and subresources.
9
10  In the following example, the page to be loaded is preconnected in the **onAppear** callback of the **Web** component.
11
12```ts
13// xxx.ets
14import { webview } from '@kit.ArkWeb';
15
16@Entry
17@Component
18struct WebComponent {
19  webviewController: webview.WebviewController = new webview.WebviewController();
20
21  build() {
22    Column() {
23      Button('loadData')
24        .onClick(() => {
25          if (this.webviewController.accessBackward()) {
26            this.webviewController.backward();
27          }
28        })
29      Web({ src: 'https://www.example.com/', controller: this.webviewController })
30        .onAppear(() => {
31          // The second parameter specifies whether to preconnect to a URL. The value false means that only DNS resolution is conducted on the URL.
32          // The third parameter indicates the number of sockets to be preconnected. A maximum of six sockets are allowed.
33          webview.WebviewController.prepareForPageLoad('https://www.example.com/', true, 2);
34        })
35    }
36  }
37}
38```
39
40You can also use [initializeWebEngine()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine) to initialize the web kernel in advance, and then call
41[prepareForPageLoad()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prepareforpageload10) after the kernel is initialized. This method is applicable to preparsing and preconnecting of the home page.
42
43
44  In the following example, the web kernel is initialized in advance and the home page is preconnected in **onCreate** of the UIAbility.
45
46```ts
47// xxx.ets
48import { webview } from '@kit.ArkWeb';
49import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
50
51export default class EntryAbility extends UIAbility {
52  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
53    console.log("EntryAbility onCreate");
54    webview.WebviewController.initializeWebEngine();
55    // Replace 'https://www.example.com' with the actual URL to be accessed.
56    webview.WebviewController.prepareForPageLoad("https://www.example.com/", true, 2);
57    AppStorage.setOrCreate("abilityWant", want);
58    console.log("EntryAbility onCreate done");
59  }
60}
61```
62
63## Prefetching
64
65This method is used for resource-level optimization. Based on predictions as to what page is to be loaded or visited, you can use [prefetchPage()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prefetchpage10) for prefetching.
66
67Prefetching downloads the resources required by the page in advance, including main resources and subresources, to avoid blocking page rendering. However, the JavaScript code of the web page is not executed. Before calling **prefetchPage()**, you must create a **WebviewController** instance bound to the **Web** component.
68
69In the following example, prefetching of a page is triggered in **onPageEnd**.
70
71```ts
72// xxx.ets
73import { webview } from '@kit.ArkWeb';
74
75@Entry
76@Component
77struct WebComponent {
78  webviewController: webview.WebviewController = new webview.WebviewController();
79
80  build() {
81    Column() {
82      Web({ src: 'https://www.example.com/', controller: this.webviewController })
83        .onPageEnd(() => {
84          // Prefetch the page at https://www.iana.org/help/example-domains.
85          this.webviewController.prefetchPage('https://www.iana.org/help/example-domains');
86        })
87    }
88  }
89}
90```
91
92## Prefetching a POST Request
93
94This method is used for request-level optimization. You can use [prefetchResource()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prefetchresource12) to prefetch a POST request on the page to be loaded. When the page loading is complete, you can use [clearPrefetchedResource()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#clearprefetchedresource12) to clear the cached prefetched resources that are no longer used.
95
96  The following is an example: In the **onAppear** event of the **Web** component, prefetch the POST request for the page that is about to be loaded; in the **onPageEnd** event, you can clear the cache of the prefetched POST request that is no longer needed.
97
98```ts
99// xxx.ets
100import { webview } from '@kit.ArkWeb';
101
102@Entry
103@Component
104struct WebComponent {
105  webviewController: webview.WebviewController = new webview.WebviewController();
106
107  build() {
108    Column() {
109      Web({ src: "https://www.example.com/", controller: this.webviewController })
110        .onAppear(() => {
111          // Replace https://www.example1.com/post?e=f&g=h with the actual URL of the POST request to prefetch.
112          webview.WebviewController.prefetchResource(
113            {
114              url: "https://www.example1.com/post?e=f&g=h",
115              method: "POST",
116              formData: "a=x&b=y",
117            },
118            [{
119              headerKey: "c",
120              headerValue: "z",
121            },],
122            "KeyX", 500);
123        })
124        .onPageEnd(() => {
125          // Clear the cache of prefetched resources that are no longer used.
126          webview.WebviewController.clearPrefetchedResource(["KeyX",]);
127        })
128    }
129  }
130}
131```
132
133If you can predict that a **Web** component is about to load a page or is about to navigate to a page that includes a POST request, you can use [prefetchResource()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prefetchresource12) to prefetch the POST request on the page to be loaded.
134
135  Here is an example of how you might initiate prefetching of a POST request for a page to visit, in the **onPageEnd** callback:
136
137```ts
138// xxx.ets
139import { webview } from '@kit.ArkWeb';
140
141@Entry
142@Component
143struct WebComponent {
144  webviewController: webview.WebviewController = new webview.WebviewController();
145
146  build() {
147    Column() {
148      Web({ src: 'https://www.example.com/', controller: this.webviewController })
149        .onPageEnd(() => {
150          // Replace https://www.example1.com/post?e=f&g=h with the actual URL of the POST request to prefetch.
151          webview.WebviewController.prefetchResource(
152            {
153              url: "https://www.example1.com/post?e=f&g=h",
154              method: "POST",
155              formData: "a=x&b=y",
156            },
157            [{
158              headerKey: "c",
159              headerValue: "z",
160            },],
161            "KeyX", 500);
162        })
163    }
164  }
165}
166```
167
168You can also use [initializeWebEngine()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine) to initialize the kernel in advance, and then call [prefetchResource()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#prefetchresource12) to prefetch the POST request on the page to be loaded after the kernel is initialized. This approach is suitable for prefetching POST requests for the home page in advance.
169
170  In the following example, the web engine is initialized in advance and the POST request of the home page is preobtained in **onCreate()** of the ability.
171
172```ts
173// xxx.ets
174import { webview } from '@kit.ArkWeb';
175import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
176
177export default class EntryAbility extends UIAbility {
178  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
179    console.log("EntryAbility onCreate");
180    webview.WebviewController.initializeWebEngine();
181    // Replace https://www.example1.com/post?e=f&g=h with the actual URL of the POST request to prefetch.
182    webview.WebviewController.prefetchResource(
183      {
184        url: "https://www.example1.com/post?e=f&g=h",
185        method: "POST",
186        formData: "a=x&b=y",
187      },
188      [{
189        headerKey: "c",
190        headerValue: "z",
191      },],
192      "KeyX", 500);
193    AppStorage.setOrCreate("abilityWant", want);
194    console.log("EntryAbility onCreate done");
195  }
196}
197```
198
199## Precompiling for the Compilation Cache
200
201You can use [precompileJavaScript()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#precompilejavascript12) to generate the compilation cache of the script file before page loading.
202
203You are advised to use this function together with dynamic components, use offline **Web** components to generate bytecode caches, and load the service **Web** component at the appropriate time to use the bytecode caches. The example code is as follows:
204
2051. Save **UIContext** to **localStorage** in **EntryAbility**.
206
207   ```ts
208   // EntryAbility.ets
209   import { UIAbility } from '@kit.AbilityKit';
210   import { window } from '@kit.ArkUI';
211
212   const localStorage: LocalStorage = new LocalStorage('uiContext');
213
214   export default class EntryAbility extends UIAbility {
215     storage: LocalStorage = localStorage;
216
217     onWindowStageCreate(windowStage: window.WindowStage) {
218       windowStage.loadContent('pages/Index', this.storage, (err, data) => {
219         if (err.code) {
220           return;
221         }
222
223         this.storage.setOrCreate<UIContext>("uiContext", windowStage.getMainWindowSync().getUIContext());
224       });
225     }
226   }
227   ```
228
2292. Compile the basic code of the dynamic component.
230
231   ```ts
232   // DynamicComponent.ets
233   import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
234
235   export interface BuilderData {
236     url: string;
237     controller: WebviewController;
238     context: UIContext;
239   }
240
241   let storage : LocalStorage | undefined = undefined;
242
243   export class NodeControllerImpl extends NodeController {
244     private rootNode: BuilderNode<BuilderData[]> | null = null;
245     private wrappedBuilder: WrappedBuilder<BuilderData[]> | null = null;
246
247     constructor(wrappedBuilder: WrappedBuilder<BuilderData[]>, context: UIContext) {
248       storage = context.getSharedLocalStorage();
249       super();
250       this.wrappedBuilder = wrappedBuilder;
251     }
252
253     makeNode(): FrameNode | null {
254       if (this.rootNode != null) {
255         return this.rootNode.getFrameNode();
256       }
257       return null;
258     }
259
260     initWeb(url: string, controller: WebviewController) {
261       if(this.rootNode != null) {
262         return;
263       }
264
265       const uiContext: UIContext = storage!.get<UIContext>("uiContext") as UIContext;
266       if (!uiContext) {
267         return;
268       }
269       this.rootNode = new BuilderNode(uiContext);
270       this.rootNode.build(this.wrappedBuilder, { url: url, controller: controller });
271     }
272   }
273
274   export const createNode = (wrappedBuilder: WrappedBuilder<BuilderData[]>, data: BuilderData) => {
275     const baseNode = new NodeControllerImpl(wrappedBuilder, data.context);
276     baseNode.initWeb(data.url, data.controller);
277     return baseNode;
278   }
279   ```
280
2813. Compile the component for generating the bytecode cache. In this example, the local Javascript resource reads the local file in the **rawfile** directory through **readRawFile()**.
282
283   ```ts
284   // PrecompileWebview.ets
285   import { BuilderData } from "./DynamicComponent";
286   import { Config, configs } from "./PrecompileConfig";
287
288   @Builder
289   function WebBuilder(data: BuilderData) {
290     Web({ src: data.url, controller: data.controller })
291       .onControllerAttached(() => {
292         precompile(data.controller, configs, data.context);
293       })
294       .fileAccess(true)
295   }
296
297   export const precompileWebview = wrapBuilder<BuilderData[]>(WebBuilder);
298
299   export const precompile = async (controller: WebviewController, configs: Array<Config>, context: UIContext) => {
300     for (const config of configs) {
301       let content = await readRawFile(config.localPath, context);
302
303       try {
304         controller.precompileJavaScript(config.url, content, config.options)
305           .then(errCode => {
306             console.error("precompile successfully! " + errCode);
307           }).catch((errCode: number) => {
308             console.error("precompile failed. " + errCode);
309         });
310       } catch (err) {
311         console.error("precompile failed. " + err.code + " " + err.message);
312       }
313     }
314   }
315
316   async function readRawFile(path: string, context: UIContext) {
317     try {
318       return await context.getHostContext()!.resourceManager.getRawFileContent(path);;
319     } catch (err) {
320       return new Uint8Array(0);
321     }
322   }
323   ```
324
325JavaScript resources can also be obtained through [Data Request](../reference/apis-network-kit/js-apis-http.md). However, the format of HTTP response header obtained using this method is not standard. Additional steps are required to convert the response header into the standard HTTP response header format before use. If the response header obtained through a network request is **e-tag**, convert it to **E-Tag** before using it.
326
3274. Compile the code of the service component.
328
329   ```ts
330   // BusinessWebview.ets
331   import { BuilderData } from "./DynamicComponent";
332
333   @Builder
334   function WebBuilder(data: BuilderData) {
335     // The component can be extended as required.
336     Web({ src: data.url, controller: data.controller })
337       .cacheMode(CacheMode.Default)
338   }
339
340   export const businessWebview = wrapBuilder<BuilderData[]>(WebBuilder);
341   ```
342
3435. Edit the resource configuration information.
344
345   ```ts
346   // PrecompileConfig.ets
347   import { webview } from '@kit.ArkWeb'
348
349   export interface Config {
350     url:  string,
351     localPath: string, // Local resource path.
352     options: webview.CacheOptions
353   }
354
355   export let configs: Array<Config> = [
356     {
357       url: "https://www.example.com/example.js",
358       localPath: "example.js",
359       options: {
360         responseHeaders: [
361           { headerKey: "E-Tag", headerValue: "aWO42N9P9dG/5xqYQCxsx+vDOoU="},
362           { headerKey: "Last-Modified", headerValue: "Wed, 21 Mar 2024 10:38:41 GMT"}
363         ]
364       }
365     }
366   ]
367   ```
368
3696. Use the components on the page.
370
371   ```ts
372   // Index.ets
373   import { webview } from '@kit.ArkWeb';
374   import { NodeController } from '@kit.ArkUI';
375   import { createNode } from "./DynamicComponent"
376   import { precompileWebview } from "./PrecompileWebview"
377   import { businessWebview } from "./BusinessWebview"
378
379   @Entry
380   @Component
381   struct Index {
382     @State precompileNode: NodeController | undefined = undefined;
383     precompileController: webview.WebviewController = new webview.WebviewController();
384
385     @State businessNode: NodeController | undefined = undefined;
386     businessController: webview.WebviewController = new webview.WebviewController();
387
388     aboutToAppear(): void {
389       // Initialize the Web component used to inject local resources.
390       this.precompileNode = createNode(precompileWebview,
391         { url: "https://www.example.com/empty.html", controller: this.precompileController, context: this.getUIContext()});
392     }
393
394     build() {
395       Column() {
396         // Load the service Web component at a proper time. In this example, the Web component is used in a button onclick event.
397         Button ("Loading page")
398           .onClick(() => {
399             this.businessNode = createNode(businessWebview, {
400               url:  "https://www.example.com/business.html",
401               controller: this.businessController,
402               context: this.getUIContext()
403             });
404           })
405         // The Web component used for the service.
406         NodeContainer(this.businessNode);
407       }
408     }
409   }
410   ```
411
412If you want to update the local generated compiled bytecode, edit the value of **E-Tag** or **Last-Modified** in the **responseHeaders** parameter of **cacheOptions**, and call the API again.
413
414## Injecting Offline Resources Without Interception
415You can use [injectOfflineResources()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#injectofflineresources12) to inject images, style sheets, or script resources to the memory cache of applications before page loading.
416
417You are advised to use this function together with dynamic components, use offline **Web** components to inject resources into the memory cache of the kernel, and load the service **Web** component at the appropriate time to use these resources. The example code is as follows:
418
4191. Save **UIContext** to **localStorage** in **EntryAbility**.
420
421   ```ts
422   // EntryAbility.ets
423   import { UIAbility } from '@kit.AbilityKit';
424   import { window } from '@kit.ArkUI';
425
426   const localStorage: LocalStorage = new LocalStorage('uiContext');
427
428   export default class EntryAbility extends UIAbility {
429     storage: LocalStorage = localStorage;
430
431     onWindowStageCreate(windowStage: window.WindowStage) {
432       windowStage.loadContent('pages/Index', this.storage, (err, data) => {
433         if (err.code) {
434           return;
435         }
436
437         this.storage.setOrCreate<UIContext>("uiContext", windowStage.getMainWindowSync().getUIContext());
438       });
439     }
440   }
441   ```
442
4432. Compile the basic code of the dynamic component.
444
445   ```ts
446   // DynamicComponent.ets
447   import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
448
449   export interface BuilderData {
450     url: string;
451     controller: WebviewController;
452     context: UIContext;
453   }
454
455   let storage : LocalStorage | undefined = undefined;
456
457   export class NodeControllerImpl extends NodeController {
458     private rootNode: BuilderNode<BuilderData[]> | null = null;
459     private wrappedBuilder: WrappedBuilder<BuilderData[]> | null = null;
460
461     constructor(wrappedBuilder: WrappedBuilder<BuilderData[]>,  context: UIContext) {
462      storage = context.getSharedLocalStorage();
463       super();
464       this.wrappedBuilder = wrappedBuilder;
465     }
466
467     makeNode(): FrameNode | null {
468       if (this.rootNode != null) {
469         return this.rootNode.getFrameNode();
470       }
471       return null;
472     }
473
474     initWeb(url: string, controller: WebviewController) {
475       if(this.rootNode != null) {
476         return;
477       }
478
479       const uiContext: UIContext = storage!.get<UIContext>("uiContext") as UIContext;
480       if (!uiContext) {
481         return;
482       }
483       this.rootNode = new BuilderNode(uiContext);
484       this.rootNode.build(this.wrappedBuilder, { url: url, controller: controller });
485     }
486   }
487
488   export const createNode = (wrappedBuilder: WrappedBuilder<BuilderData[]>, data: BuilderData) => {
489     const baseNode = new NodeControllerImpl(wrappedBuilder, data.context);
490     baseNode.initWeb(data.url, data.controller);
491     return baseNode;
492   }
493   ```
494
4953. Compile the component code for injecting resources. In this example, the local resource reads the local file in the **rawfile** directory through **readRawFile()**.
496
497   <!--code_no_check-->
498   ```ts
499   // InjectWebview.ets
500   import { webview } from '@kit.ArkWeb';
501   import { resourceConfigs } from "./Resource";
502   import { BuilderData } from "./DynamicComponent";
503
504   @Builder
505   function WebBuilder(data: BuilderData) {
506     Web({ src: data.url, controller: data.controller })
507       .onControllerAttached(async () => {
508         try {
509           data.controller.injectOfflineResources(await getData (data.context));
510         } catch (err) {
511           console.error("error: " + err.code + " " + err.message);
512         }
513       })
514       .fileAccess(true)
515   }
516
517   export const injectWebview = wrapBuilder<BuilderData[]>(WebBuilder);
518
519   export async function getData(context: UIContext) {
520     const resourceMapArr: Array<webview.OfflineResourceMap> = [];
521
522     // Read the configuration, and read the file content from the rawfile directory.
523     for (let config of resourceConfigs) {
524       let buf: Uint8Array = new Uint8Array(0);
525       if (config.localPath) {
526         buf = await readRawFile(config.localPath, context);
527       }
528
529       resourceMapArr.push({
530         urlList: config.urlList,
531         resource: buf,
532         responseHeaders: config.responseHeaders,
533         type: config.type,
534       })
535     }
536
537     return resourceMapArr;
538   }
539
540   export async function readRawFile(url: string, context: UIContext) {
541     try {
542       return await context.getHostContext()!.resourceManager.getRawFileContent(url);
543     } catch (err) {
544       return new Uint8Array(0);
545     }
546   }
547   ```
548
5494. Compile the code of the service component.
550
551   <!--code_no_check-->
552   ```ts
553   // BusinessWebview.ets
554   import { BuilderData } from "./DynamicComponent";
555
556   @Builder
557   function WebBuilder(data: BuilderData) {
558     // The component can be extended as required.
559     Web({ src: data.url, controller: data.controller })
560       .cacheMode(CacheMode.Default)
561   }
562
563   export const businessWebview = wrapBuilder<BuilderData[]>(WebBuilder);
564   ```
565
5665. Edit the resource configuration information.
567
568   ```ts
569   // Resource.ets
570   import { webview } from '@kit.ArkWeb';
571
572   export interface ResourceConfig {
573     urlList: Array<string>,
574     type: webview.OfflineResourceType,
575     responseHeaders: Array<Header>,
576     localPath: string, // The path for storing local resources in the rawfile directory.
577   }
578
579   export const resourceConfigs: Array<ResourceConfig> = [
580     {
581       localPath: "example.png",
582       urlList: [
583         "https://www.example.com/",
584         "https://www.example.com/path1/example.png",
585         "https://www.example.com/path2/example.png",
586       ],
587       type: webview.OfflineResourceType.IMAGE,
588       responseHeaders: [
589         { headerKey: "Cache-Control", headerValue: "max-age=1000" },
590         { headerKey: "Content-Type", headerValue: "image/png" },
591       ]
592     },
593     {
594       localPath: "example.js",
595       urlList: [ // Only one URL is provided. This URL is used as both the resource origin and the network request address of the resource.
596         "https://www.example.com/example.js",
597       ],
598       type: webview.OfflineResourceType.CLASSIC_JS,
599       responseHeaders: [
600         // Used in <script crossorigin="anonymous"/> mode to provide additional response headers.
601         { headerKey: "Cross-Origin", headerValue:"anonymous" }
602       ]
603     },
604   ];
605   ```
606
6076. Use the components on the page.
608   ```ts
609   // Index.ets
610   import { webview } from '@kit.ArkWeb';
611   import { NodeController } from '@kit.ArkUI';
612   import { createNode } from "./DynamicComponent"
613   import { injectWebview } from "./InjectWebview"
614   import { businessWebview } from "./BusinessWebview"
615
616   @Entry
617   @Component
618   struct Index {
619     @State injectNode: NodeController | undefined = undefined;
620     injectController: webview.WebviewController = new webview.WebviewController();
621
622     @State businessNode: NodeController | undefined = undefined;
623     businessController: webview.WebviewController = new webview.WebviewController();
624
625     aboutToAppear(): void {
626       // Initialize the Web component used to inject local resources and provide an empty HTML page as the URL.
627       this.injectNode = createNode(injectWebview,
628           { url: "https://www.example.com/empty.html", controller: this.injectController, context: this.getUIContext()});
629     }
630
631     build() {
632       Column() {
633         // Load the service Web component at a proper time. In this example, the Web component is used in a button onclick event.
634         Button ("Loading page")
635           .onClick(() => {
636             this.businessNode = createNode(businessWebview, {
637               url: "https://www.example.com/business.html",
638               controller: this.businessController,
639               context: this.getUIContext()
640             });
641           })
642         // The Web component used for the service.
643         NodeContainer(this.businessNode);
644       }
645     }
646   }
647   ```
648
6497. Example of a loaded HTML web page:
650
651   ```HTML
652   <!DOCTYPE html>
653   <html lang="en">
654   <head></head>
655   <body>
656     <img src="https://www.example.com/path1/request.png" />
657     <img src="https://www.example.com/path2/request.png" />
658     <script src="https://www.example.com/example.js" crossorigin="anonymous"></script>
659   </body>
660   </html>
661   ```
662