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