• 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.
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            {
113              url: "https://www.example1.com/post?e=f&g=h",
114              method: "POST",
115              formData: "a=x&b=y",
116            },
117            [{
118              headerKey: "c",
119              headerValue: "z",
120            },],
121            "KeyX", 500);
122        })
123        .onPageEnd(() => {
124          // Clear the cache of prefetched resources that are no longer used.
125          webview.WebviewController.clearPrefetchedResource(["KeyX",]);
126        })
127    }
128  }
129}
130```
131
132If 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.
133
134  Here is an example of how you might initiate prefetching of a POST request for a page to visit, in the **onPageEnd** callback:
135
136```ts
137// xxx.ets
138import { webview } from '@kit.ArkWeb';
139
140@Entry
141@Component
142struct WebComponent {
143  webviewController: webview.WebviewController = new webview.WebviewController();
144
145  build() {
146    Column() {
147      Web({ src: 'https://www.example.com/', controller: this.webviewController })
148        .onPageEnd(() => {
149          // Replace https://www.example1.com/post?e=f&g=h with the actual URL of the POST request to prefetch.
150          webview.WebviewController.prefetchResource(
151            {
152              url: "https://www.example1.com/post?e=f&g=h",
153              method: "POST",
154              formData: "a=x&b=y",
155            },
156            [{
157              headerKey: "c",
158              headerValue: "z",
159            },],
160            "KeyX", 500);
161        })
162    }
163  }
164}
165```
166
167You 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.
168
169  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.
170
171```ts
172// xxx.ets
173import { webview } from '@kit.ArkWeb';
174import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
175
176export default class EntryAbility extends UIAbility {
177  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
178    console.log("EntryAbility onCreate");
179    webview.WebviewController.initializeWebEngine();
180    // Replace https://www.example1.com/post?e=f&g=h with the actual URL of the POST request to prefetch.
181    webview.WebviewController.prefetchResource(
182      {
183        url: "https://www.example1.com/post?e=f&g=h",
184        method: "POST",
185        formData: "a=x&b=y",
186      },
187      [{
188        headerKey: "c",
189        headerValue: "z",
190      },],
191      "KeyX", 500);
192    AppStorage.setOrCreate("abilityWant", want);
193    console.log("EntryAbility onCreate done");
194  }
195}
196```
197
198## Precompiling for the Compilation Cache
199
200You can use [precompileJavaScript()](../reference/apis-arkweb/js-apis-webview.md#precompilejavascript12) to generate the compilation cache of the script file before page loading.
201
202You 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:
203
2041. Save **UIContext** to **localStorage** in **EntryAbility**.
205
206   ```ts
207   // EntryAbility.ets
208   import { UIAbility } from '@kit.AbilityKit';
209   import { window } from '@kit.ArkUI';
210
211   const localStorage: LocalStorage = new LocalStorage('uiContext');
212
213   export default class EntryAbility extends UIAbility {
214     storage: LocalStorage = localStorage;
215
216     onWindowStageCreate(windowStage: window.WindowStage) {
217       windowStage.loadContent('pages/Index', this.storage, (err, data) => {
218         if (err.code) {
219           return;
220         }
221
222         this.storage.setOrCreate<UIContext>("uiContext", windowStage.getMainWindowSync().getUIContext());
223       });
224     }
225   }
226   ```
227
2282. Compile the basic code of the dynamic component.
229
230   ```ts
231   // DynamicComponent.ets
232   import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
233
234   export interface BuilderData {
235     url: string;
236     controller: WebviewController;
237   }
238
239   const storage = LocalStorage.getShared();
240
241   export class NodeControllerImpl extends NodeController {
242     private rootNode: BuilderNode<BuilderData[]> | null = null;
243     private wrappedBuilder: WrappedBuilder<BuilderData[]> | null = null;
244
245     constructor(wrappedBuilder: WrappedBuilder<BuilderData[]>) {
246       super();
247       this.wrappedBuilder = wrappedBuilder;
248     }
249
250     makeNode(): FrameNode | null {
251       if (this.rootNode != null) {
252         return this.rootNode.getFrameNode();
253       }
254       return null;
255     }
256
257     initWeb(url: string, controller: WebviewController) {
258       if(this.rootNode != null) {
259         return;
260       }
261
262       const uiContext: UIContext = storage.get<UIContext>("uiContext") as UIContext;
263       if (!uiContext) {
264         return;
265       }
266       this.rootNode = new BuilderNode(uiContext);
267       this.rootNode.build(this.wrappedBuilder, { url: url, controller: controller });
268     }
269   }
270
271   export const createNode = (wrappedBuilder: WrappedBuilder<BuilderData[]>, data: BuilderData) => {
272     const baseNode = new NodeControllerImpl(wrappedBuilder);
273     baseNode.initWeb(data.url, data.controller);
274     return baseNode;
275   }
276   ```
277
2783. 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()**.
279
280   ```ts
281   // PrecompileWebview.ets
282   import { BuilderData } from "./DynamicComponent";
283   import { Config, configs } from "./PrecompileConfig";
284
285   @Builder
286   function WebBuilder(data: BuilderData) {
287     Web({ src: data.url, controller: data.controller })
288       .onControllerAttached(() => {
289         precompile(data.controller, configs);
290       })
291       .fileAccess(true)
292   }
293
294   export const precompileWebview = wrapBuilder<BuilderData[]>(WebBuilder);
295
296   export const precompile = async (controller: WebviewController, configs: Array<Config>) => {
297     for (const config of configs) {
298       let content = await readRawFile(config.localPath);
299
300       try {
301         controller.precompileJavaScript(config.url, content, config.options)
302           .then(errCode => {
303             console.error("precompile successfully! " + errCode);
304           }).catch((errCode: number) => {
305             console.error("precompile failed. " + errCode);
306         });
307       } catch (err) {
308         console.error("precompile failed. " + err.code + " " + err.message);
309       }
310     }
311   }
312
313   async function readRawFile(path: string) {
314     try {
315       return await getContext().resourceManager.getRawFileContent(path);;
316     } catch (err) {
317       return new Uint8Array(0);
318     }
319   }
320   ```
321
322JavaScript 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.
323
3244. Compile the code of the service component.
325
326   ```ts
327   // BusinessWebview.ets
328   import { BuilderData } from "./DynamicComponent";
329
330   @Builder
331   function WebBuilder(data: BuilderData) {
332     // The component can be extended as required.
333     Web({ src: data.url, controller: data.controller })
334       .cacheMode(CacheMode.Default)
335   }
336
337   export const businessWebview = wrapBuilder<BuilderData[]>(WebBuilder);
338   ```
339
3405. Edit the resource configuration information.
341
342   ```ts
343   // PrecompileConfig.ets
344   import { webview } from '@kit.ArkWeb'
345
346   export interface Config {
347     url:  string,
348     localPath: string, // Local resource path.
349     options: webview.CacheOptions
350   }
351
352   export let configs: Array<Config> = [
353     {
354       url: "https://www.example.com/example.js",
355       localPath: "example.js",
356       options: {
357         responseHeaders: [
358           { headerKey: "E-Tag", headerValue: "aWO42N9P9dG/5xqYQCxsx+vDOoU="},
359           { headerKey: "Last-Modified", headerValue: "Wed, 21 Mar 2024 10:38:41 GMT"}
360         ]
361       }
362     }
363   ]
364   ```
365
3666. Use the components on the page.
367
368   ```ts
369   // Index.ets
370   import { webview } from '@kit.ArkWeb';
371   import { NodeController } from '@kit.ArkUI';
372   import { createNode } from "./DynamicComponent"
373   import { precompileWebview } from "./PrecompileWebview"
374   import { businessWebview } from "./BusinessWebview"
375
376   @Entry
377   @Component
378   struct Index {
379     @State precompileNode: NodeController | undefined = undefined;
380     precompileController: webview.WebviewController = new webview.WebviewController();
381
382     @State businessNode: NodeController | undefined = undefined;
383     businessController: webview.WebviewController = new webview.WebviewController();
384
385     aboutToAppear(): void {
386       // Initialize the Web component used to inject local resources.
387       this.precompileNode = createNode(precompileWebview,
388         { url: "https://www.example.com/empty.html", controller: this.precompileController});
389     }
390
391     build() {
392       Column() {
393         // Load the service Web component at a proper time. In this example, the Web component is used in a button onclick event.
394         Button ("Loading page")
395           .onClick(() => {
396             this.businessNode = createNode(businessWebview, {
397               url:  "https://www.example.com/business.html",
398               controller: this.businessController
399             });
400           })
401         // The Web component used for the service.
402         NodeContainer(this.businessNode);
403       }
404     }
405   }
406   ```
407
408If 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.
409
410## Injecting Offline Resources Without Interception
411You 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.
412
413You 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:
414
4151. Save **UIContext** to **localStorage** in **EntryAbility**.
416
417   ```ts
418   // EntryAbility.ets
419   import { UIAbility } from '@kit.AbilityKit';
420   import { window } from '@kit.ArkUI';
421
422   const localStorage: LocalStorage = new LocalStorage('uiContext');
423
424   export default class EntryAbility extends UIAbility {
425     storage: LocalStorage = localStorage;
426
427     onWindowStageCreate(windowStage: window.WindowStage) {
428       windowStage.loadContent('pages/Index', this.storage, (err, data) => {
429         if (err.code) {
430           return;
431         }
432
433         this.storage.setOrCreate<UIContext>("uiContext", windowStage.getMainWindowSync().getUIContext());
434       });
435     }
436   }
437   ```
438
4392. Compile the basic code of the dynamic component.
440
441   ```ts
442   // DynamicComponent.ets
443   import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
444
445   export interface BuilderData {
446     url: string;
447     controller: WebviewController;
448   }
449
450   const storage = LocalStorage.getShared();
451
452   export class NodeControllerImpl extends NodeController {
453     private rootNode: BuilderNode<BuilderData[]> | null = null;
454     private wrappedBuilder: WrappedBuilder<BuilderData[]> | null = null;
455
456     constructor(wrappedBuilder: WrappedBuilder<BuilderData[]>) {
457       super();
458       this.wrappedBuilder = wrappedBuilder;
459     }
460
461     makeNode(): FrameNode | null {
462       if (this.rootNode != null) {
463         return this.rootNode.getFrameNode();
464       }
465       return null;
466     }
467
468     initWeb(url: string, controller: WebviewController) {
469       if(this.rootNode != null) {
470         return;
471       }
472
473       const uiContext: UIContext = storage.get<UIContext>("uiContext") as UIContext;
474       if (!uiContext) {
475         return;
476       }
477       this.rootNode = new BuilderNode(uiContext);
478       this.rootNode.build(this.wrappedBuilder, { url: url, controller: controller });
479     }
480   }
481
482   export const createNode = (wrappedBuilder: WrappedBuilder<BuilderData[]>, data: BuilderData) => {
483     const baseNode = new NodeControllerImpl(wrappedBuilder);
484     baseNode.initWeb(data.url, data.controller);
485     return baseNode;
486   }
487   ```
488
4893. Compile the component code for injecting resources. In this example, the local resource reads the local file in the **rawfile** directory through **readRawFile()**.
490
491   <!--code_no_check-->
492   ```ts
493   // InjectWebview.ets
494   import { webview } from '@kit.ArkWeb';
495   import { resourceConfigs } from "./Resource";
496   import { BuilderData } from "./DynamicComponent";
497
498   @Builder
499   function WebBuilder(data: BuilderData) {
500     Web({ src: data.url, controller: data.controller })
501       .onControllerAttached(async () => {
502         try {
503           data.controller.injectOfflineResources(await getData ());
504         } catch (err) {
505           console.error("error: " + err.code + " " + err.message);
506         }
507       })
508       .fileAccess(true)
509   }
510
511   export const injectWebview = wrapBuilder<BuilderData[]>(WebBuilder);
512
513   export async function getData() {
514     const resourceMapArr: Array<webview.OfflineResourceMap> = [];
515
516     // Read the configuration, and read the file content from the rawfile directory.
517     for (let config of resourceConfigs) {
518       let buf: Uint8Array = new Uint8Array(0);
519       if (config.localPath) {
520         buf = await readRawFile(config.localPath);
521       }
522
523       resourceMapArr.push({
524         urlList: config.urlList,
525         resource: buf,
526         responseHeaders: config.responseHeaders,
527         type: config.type,
528       })
529     }
530
531     return resourceMapArr;
532   }
533
534   export async function readRawFile(url: string) {
535     try {
536       return await getContext().resourceManager.getRawFileContent(url);
537     } catch (err) {
538       return new Uint8Array(0);
539     }
540   }
541   ```
542
5434. Compile the code of the service component.
544
545   <!--code_no_check-->
546   ```ts
547   // BusinessWebview.ets
548   import { BuilderData } from "./DynamicComponent";
549
550   @Builder
551   function WebBuilder(data: BuilderData) {
552     // The component can be extended as required.
553     Web({ src: data.url, controller: data.controller })
554       .cacheMode(CacheMode.Default)
555   }
556
557   export const businessWebview = wrapBuilder<BuilderData[]>(WebBuilder);
558   ```
559
5605. Edit the resource configuration information.
561
562   ```ts
563   // Resource.ets
564   import { webview } from '@kit.ArkWeb';
565
566   export interface ResourceConfig {
567     urlList: Array<string>,
568     type: webview.OfflineResourceType,
569     responseHeaders: Array<Header>,
570     localPath: string, // The path for storing local resources in the rawfile directory.
571   }
572
573   export const resourceConfigs: Array<ResourceConfig> = [
574     {
575       localPath: "example.png",
576       urlList: [
577         "https://www.example.com/",
578         "https://www.example.com/path1/example.png",
579         "https://www.example.com/path2/example.png",
580       ],
581       type: webview.OfflineResourceType.IMAGE,
582       responseHeaders: [
583         { headerKey: "Cache-Control", headerValue: "max-age=1000" },
584         { headerKey: "Content-Type", headerValue: "image/png" },
585       ]
586     },
587     {
588       localPath: "example.js",
589       urlList: [ // Only one URL is provided. This URL is used as both the resource origin and the network request address of the resource.
590         "https://www.example.com/example.js",
591       ],
592       type: webview.OfflineResourceType.CLASSIC_JS,
593       responseHeaders: [
594         // Used in <script crossorigin="anoymous" /> mode to provide additional response headers.
595         { headerKey: "Cross-Origin", headerValue:"anonymous" }
596       ]
597     },
598   ];
599   ```
600
6016. Use the components on the page.
602   ```ts
603   // Index.ets
604   import { webview } from '@kit.ArkWeb';
605   import { NodeController } from '@kit.ArkUI';
606   import { createNode } from "./DynamicComponent"
607   import { injectWebview } from "./InjectWebview"
608   import { businessWebview } from "./BusinessWebview"
609
610   @Entry
611   @Component
612   struct Index {
613     @State injectNode: NodeController | undefined = undefined;
614     injectController: webview.WebviewController = new webview.WebviewController();
615
616     @State businessNode: NodeController | undefined = undefined;
617     businessController: webview.WebviewController = new webview.WebviewController();
618
619     aboutToAppear(): void {
620       // Initialize the Web component used to inject local resources and provide an empty HTML page as the URL.
621       this.injectNode = createNode(injectWebview,
622           { url: "https://www.example.com/empty.html", controller: this.injectController});
623     }
624
625     build() {
626       Column() {
627         // Load the service Web component at a proper time. In this example, the Web component is used in a button onclick event.
628         Button ("Loading page")
629           .onClick(() => {
630             this.businessNode = createNode(businessWebview, {
631               url: "https://www.example.com/business.html",
632               controller: this.businessController
633             });
634           })
635         // The Web component used for the service.
636         NodeContainer(this.businessNode);
637       }
638     }
639   }
640   ```
641
6427. The example of a loaded HTML web page is as follows.
643
644   ```HTML
645   <!DOCTYPE html>
646   <html lang="en">
647   <head></head>
648   <body>
649     <img src="https://www.example.com/path1/request.png" />
650     <img src="https://www.example.com/path2/request.png" />
651     <script src="https://www.example.com/example.js" crossorigin="anonymous"></script>
652   </body>
653   </html>
654   ```
655