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