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