DEV Community

陈杨
陈杨

Posted on

Hong Mong 5 Development Treasure Case Sharing Web Development Optimization Case Sharing

Alright, folks! There are actually quite a few "hardcore" performance optimization cases hidden in the official HarmonyOS documentation, and I didn't notice them before—feels like I missed out big time! Especially the optimization tips for loading web pages with the ArkWeb component, which are simply magic weapons for improving app smoothness. The official docs are written pretty formally, so let me break them down, add my own understanding, and throw in some code examples to make everything clear and practical for you! 🚀

Opening Greeting:

Hey HarmonyOS developers! Are you often troubled by slow or laggy web page loading? Especially when embedding an H5 page in your HarmonyOS app, users might be waiting forever, and the experience drops instantly. Don't worry! Today, I'm sharing a "performance optimization treasure map" I dug out from the official HarmonyOS docs—specifically for optimizing web loading speed with the ArkWeb component. The official docs actually provide a lot of practical cases and guidance, but they might be a bit hidden. Today, I'll walk you through the most effective optimization techniques, with code explanations, so you can learn and use them right away!

Detailed Explanation (Cases + Analysis + Code):

The official docs break down the web loading process in detail and propose a series of optimizations based on the idea of "preprocessing." The core idea is: do as much work as possible before the user actually needs to see the page! Let's go through these "pre-" tricks one by one:

  1. Pre-Launch Web Rendering Process
    • Pain Point: Every time you open a WebView, the system spends time (about 140ms) starting the rendering process, and the user is waiting during this time.
    • Trick: Before the user might use the WebView (such as during app cold start, after the homepage loads, or when showing ads), quietly create a blank web component and load a blank page (like about:blank). As long as this "shadow warrior" is alive, the rendering process stays alive.
    • Effect: When the user actually clicks to open the target web page, you can reuse this existing process, saving the startup time, and the page appears instantly!
    • Cost: Slightly more memory and a bit of CPU usage (to keep the "shadow" alive).
    • Applicable Scenarios: Web pages that are frequently used in the app (such as important homepage entries or H5 modules in the user center).
    • Code Example (ArkTS):
import webview from '@ohos.web.webview';

// Assume this is called at an appropriate time during app cold start (e.g., onWindowStageCreate)
let preloadWebView: webview.WebviewController | null = null;

function preLaunchWebProcess() {
  // 1. Create the Web component (UI construction simplified; in practice, you may need to add it to a container and set it invisible or tiny)
  preloadWebView = webview.createWebview();
  // 2. Load a very lightweight page (blank page is best practice)
  preloadWebView.loadUrl('about:blank');
  // 3. Note: You need to manage the lifecycle of preloadWebView. When you actually need to show the target page, you can reuse this controller or destroy it and create a new one (the process remains alive).
}

// When you actually need to open the target page, e.g., on button click:
function openTargetWebPage() {
  // Method 1: If preloadWebView is still alive and available, use it to load the target URL
  if (preloadWebView) {
    preloadWebView.loadUrl('https://www.your-target-page.com');
    // ... Then display this WebView (could be the same component or a new one reusing the same process)
  } else {
    // Method 2: If prelaunch is invalid, create a new one as usual. Ideally, prelaunch remains valid.
    let targetWebView = webview.createWebview();
    targetWebView.loadUrl('https://www.your-target-page.com');
    // ... Display targetWebView
  }
}

// Note: When the app exits or you no longer need preloading, remember to destroy preloadWebView to free resources
function cleanupPreload() {
  if (preloadWebView) {
    preloadWebView.destroy();
    preloadWebView = null;
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Pre-Resolve & Pre-Connect
    • Pain Point: When a user visits a URL, the first step is DNS resolution (~66ms), and the second is establishing a network connection (TCP handshake/TLS negotiation, ~80ms). On a poor network, this 100+ ms is lost.
    • Trick:
      • Pre-Resolve: Proactively resolve the domains you know the user is very likely to visit next, and cache the IP addresses.
      • Pre-Connect: Go further! On top of pre-resolving, establish a socket connection with the server in advance (even complete the TLS handshake). When the user actually visits, data is sent over this "VIP channel"!
    • Effect: Cuts out DNS and connection time, making network requests start faster.
    • Cost: May consume some network resources (traffic, connections) in advance; if the user doesn't visit, it's wasted.
    • Applicable Scenarios: Domains that are essential in the main flow or very likely to be clicked by the user.
    • Code Example (Conceptual, ArkWeb may encapsulate or require system network API):
      • Currently, HarmonyOS ArkWeb may not expose a direct preconnect API.
      • One approach is to use system network capabilities (like @ohos.net.http) for pre-connection, but be mindful of connection management and reuse.
      • More common practice: When preloading/prerendering (see below) a page, its internal resource requests will naturally trigger DNS and connection, which is also a form of "pre-". So you can combine with preloading.
      • The official docs highlight this optimization. Developers should watch for API updates or leverage this feature in preloading strategies.
  2. Pre-Fetch/Download
    • Pain Point: Images, CSS, JS, and other resources in the page are downloaded and parsed/rendered on the fly. Large files or slow networks cause blocking.
    • Trick: Before the user visits the page, download these key static resources (images, CSS, JS, fonts, etc.) in advance and cache them (in memory or on disk).
    • Effect: When the page loads, resources are fetched locally, skipping network wait, greatly reducing blocking (~641ms), and making rendering smoother.
    • Cost: Extra network traffic and storage. Downloading too much or the wrong resources is wasteful.
    • Applicable Scenarios: Core, large, or slow-loading resources of key pages.
    • Code Example (Resource Interception + Caching):
      • HarmonyOS ArkWeb provides powerful onInterceptRequest interception. You can combine with local caching for prefetching.
import webview from '@ohos.web.webview';

let cachedResources: Map<string, ArrayBuffer> = new Map(); // Simple in-memory cache; you can use file cache in practice

// Step 1: Prefetch key resources (during idle time or when preloading pages)
async function prefetchResource(url: string) {
  try {
    // Use network library to download resource (e.g., @ohos.net.http)
    let response = await myHttpModule.request(url); // Pseudocode, use actual http module API
    if (response && response.data) {
      // Assume response.data is ArrayBuffer
      cachedResources.set(url, response.data);
      console.log(`Prefetched and cached: ${url}`);
    }
  } catch (error) {
    console.error(`Prefetch failed for ${url}:`, error);
  }
}

// Step 2: Set up interception in WebView to hit the cache
let webViewController = webview.createWebview();
webViewController.onInterceptRequest((request) => {
  let url = request.requestUrl;
  if (cachedResources.has(url)) {
    console.log(`Intercepting and serving from cache: ${url}`);
    // Construct an interception response
    let interceptResponse: webview.WebResourceResponse = {
      data: cachedResources.get(url),
      mimeType: getMimeTypeFromUrl(url), // Infer MIME type from url
      encoding: 'utf-8', // Adjust as needed
      statusCode: 200,
      reasonPhrase: 'OK',
      responseHeaders: { 'from-cache': 'prefetch' },
    };
    return interceptResponse; // Return cached data, WebView skips network request
  }
  return null; // No interception, WebView proceeds as normal
});

// Step 3: Load the target page (could be during preloading or user action)
webViewController.loadUrl('https://www.your-target-page.com');
Enter fullscreen mode Exit fullscreen mode
  1. Pre-Render
    • Pain Point: Even if resources are downloaded, the browser still needs to parse HTML, build the DOM tree, apply CSS, execute JS, layout, and paint... Only after all this can the full page be seen.
    • Trick: The ultimate move! In the background, fully and secretly load, parse, and render the entire target page (including executing JS). When the user clicks in, instantly switch this pre-rendered "finished" page to the foreground for a true "instant open" (~486ms gain)!
    • Effect: Explosive experience! Users barely notice any loading.
    • Cost: High! Network (downloads all resources), CPU (executes JS, renders), memory (stores render result), storage. If the user doesn't visit, it's wasted.
    • Applicable Scenarios: Extremely high-probability, must-have experience core entry pages (e.g., a key homepage operation or event page).
    • Code Example (ArkTS):
      • HarmonyOS ArkWeb's WebviewController provides a prerender method.
import webview from '@ohos.web.webview';

let prerenderController: webview.WebviewController | null = null;

// When you are very sure the user will visit a specific URL (e.g., after homepage loads and user behavior analysis points to the page)
function startPrerender(targetUrl: string) {
  // 1. Create a dedicated WebView for prerendering
  prerenderController = webview.createWebview();

  // 2. Key! Listen for prerender completion (usually by listening to a specific event or checking status)
  // Note: ArkWeb's prerender completion event may need to be checked in the latest API docs. Here, a pseudo event name `onPrerenderFinished` is used
  prerenderController.on('prerenderFinished', (event) => { // Event name needs confirmation; `onPageFinished` may not be precise enough
    console.log('Prerender complete! Page is ready for instant open!');
    // At this point, prerenderController has the fully rendered page
  });

  // 3. Start prerendering (specify target URL)
  prerenderController.prerender(targetUrl); // Use prerender, not loadUrl
}

// When the user clicks to open the page:
function showPrerenderedPage() {
  if (prerenderController && prerenderController.isPrerendered) { // Assume there is a status check
    // Method 1: Directly show this prerendered WebView (if UI structure allows)
    // myContainer.addChild(prerenderController.getComponent()); // Pseudocode, show component

    // Method 2 (more common): "Transfer" or "activate" the prerendered content to a visible WebView
    // Note: ArkWeb's specific activation API needs confirmation, possibly like:
    let visibleWebView = webview.createWebview();
    visibleWebView.activatePrerenderedPage(prerenderController); // Pseudo API, activate prerendered result
    // ... Show visibleWebView
  } else {
    // Prerender not complete or failed, fallback to normal loading
    let fallbackWebView = webview.createWebview();
    fallbackWebView.loadUrl(targetUrl);
    // ... Show fallbackWebView
  }
}

// Important: Use with caution, and promptly destroy unused prerender instances to free resources
Enter fullscreen mode Exit fullscreen mode
  1. Pre-Fetch POST
    • Pain Point: Some pages send time-consuming POST requests on load (e.g., submitting initial data, fetching dynamic content), and users have to wait for the response to see the full content (~313ms).
    • Trick: Send the POST request in advance, prefetch and cache the response data. When the user opens the page and JS sends the POST request, intercept it and return the cached data directly!
    • Effect: Eliminates network delay for key POST requests.
    • Cost: Extra network requests and storage for response data. POST requests usually have side effects, so make sure prefetching is safe (read-only, no side effects)! Otherwise, sending data early can cause big problems!
    • Applicable Scenarios: Safe, idempotent, time-consuming initial POST requests.
    • Code Example (Interception + Caching):
      • Similar to prefetch interception, but focuses on identifying specific POST requests.
import webview from '@ohos.web.webview';

let cachedPostResponse: Map<string, any> = new Map(); // Cache key can be URL + request body signature

// Step 1: Safely prefetch POST data (ensure no side effects!)
async function prefetchPostData(url: string, postData: string | Object) {
  try {
    // Use network library to simulate POST (e.g., @ohos.net.http)
    let response = await myHttpModule.post(url, postData); // Pseudocode
    if (response && response.data) {
      let cacheKey = `${url}_${hash(postData)}`; // Generate unique key for request
      cachedPostResponse.set(cacheKey, response.data);
      console.log(`Prefetched POST data for ${cacheKey}`);
    }
  } catch (error) {
    console.error(`Prefetch POST failed:`, error);
  }
}

// Step 2: Intercept specific POST requests in WebView
webViewController.onInterceptRequest((request) => {
  if (request.method === 'POST') {
    let url = request.requestUrl;
    let postBody = request.body; // Get request body, could be string or ArrayBuffer
    // Generate key from URL and body (implement a reliable hash/serialization function)
    let cacheKey = generateCacheKey(url, postBody);

    if (cachedPostResponse.has(cacheKey)) {
      console.log(`Intercepting and serving cached POST response for ${cacheKey}`);
      let cachedData = cachedPostResponse.get(cacheKey);
      // Construct interception response (same format as prefetch)
      let interceptResponse: webview.WebResourceResponse = {
        data: cachedData,
        mimeType: 'application/json', // Adjust as needed
        encoding: 'utf-8',
        statusCode: 200,
        reasonPhrase: 'OK',
        responseHeaders: { 'from-cache': 'prefetch-post' },
      };
      return interceptResponse;
    }
  }
  return null; // No interception
});
Enter fullscreen mode Exit fullscreen mode
  1. Precompile JavaScript to Generate Bytecode Cache (Code Cache)
    • Pain Point: After downloading JS files, the browser needs to compile them into bytecode before execution. The larger and more complex the JS, the longer the compilation (official example: 5.76MB JS compiles in ~2915ms!).
    • Trick:
      • First Load Optimization (V8 Code Cache): Browsers (like V8) cache compiled bytecode after the first execution. Next time the same JS file (URL matches and unchanged) is loaded, it uses the cached bytecode, skipping compilation.
      • Resource Interception Replacement Optimization: If you use resource interception replacement (like prefetch above) and replace network JS with local content, ArkWeb also provides setJavaScriptProxy with removeJavaScriptCache to manage the cache, ensuring intercepted JS can also benefit from bytecode cache for faster subsequent loads (official example: 2.4MB JS saves ~67ms on later loads).
    • Effect: Saves JS compilation time, especially for large JS files.
    • Cost: Extra storage for bytecode.
    • Applicable Scenarios: All web pages with JS, especially those with large JS files. For intercepted/replaced resources, manage the cache properly.
    • Code Example (Mainly browser/V8 mechanism, HarmonyOS provides cache management API):
import webview from '@ohos.web.webview';

// For intercepted/replaced resources, to ensure subsequent loads use bytecode cache, usually:
// 1. Return correct resource data during interception
// 2. (Optional but recommended) When resource content **changes**, clear the old bytecode cache
webViewController.setJavaScriptProxy({
  // ... other methods
  onResourceLoad: (request) => {
    if (request.resourceType === webview.ResourceType.SCRIPT && request.url === yourJsUrl) {
      let newJsContent = getUpdatedJsContent(); // Get latest JS content
      // Clear old cache (very important! Otherwise, even if content changes, old bytecode may be used)
      webViewController.removeJavaScriptCache(request.url); // Remove JS cache for the URL
      return { data: newJsContent }; // Return new JS content
    }
    return null;
  },
});

// Note: The browser's first compilation cache is automatic, no special code needed. The key is cache consistency when intercepting/replacing resources.
Enter fullscreen mode Exit fullscreen mode
  1. Offline Resource Injection Without Interception
    • Pain Point: Even with interception, loading resources into memory for the first time takes time.
    • Trick: Go further! Before the page loads, directly inject needed resources (images, CSS, JS) into the WebView's memory cache.
    • Effect: Even the interception matching process is skipped, resources are instantly available (~1240ms for 25MB).
    • Cost: Higher memory usage (resources stay in memory).
    • Applicable Scenarios: Small, extremely high-frequency core resources (like core JS libraries, main CSS, small icons).
    • Code Example (Conceptual, API needs confirmation):
      • HarmonyOS ArkWeb may provide APIs like loadWithContent or direct memory cache manipulation (check docs/examples). One idea is to inject after creating the WebView but before loadUrl.
// Pseudocode for concept. Actual API may be `injectResource` or `preloadToCache`
webViewController.injectResource('https://cdn.example.com/core.js', jsContentArrayBuffer);
webViewController.injectResource('https://cdn.example.com/styles.css', cssContentArrayBuffer);
webViewController.injectResource('https://cdn.example.com/logo.png', imageDataArrayBuffer, 'image/png');

// Then load the page. When the page requests these URLs, resources are read directly from memory cache, no interception or network needed.
webViewController.loadUrl('https://www.your-target-page.com');
Enter fullscreen mode Exit fullscreen mode
  1. Accelerated Resource Interception Replacement (ArrayBuffer Optimization)
    • Pain Point: When using onInterceptRequest for resource replacement, if the replacement data is in ArrayBuffer format (images, audio/video, fonts, compressed JS/CSS, etc.), you may need to convert formats at the app layer (e.g., string to ArrayBuffer), or ArkWeb may need to convert internally, which takes time.
    • Trick: The interception interface of ArkWeb natively supports directly returning ArrayBuffer data. When intercepting, provide ArrayBuffer data directly to avoid unnecessary conversion overhead.
    • Effect: Saves format conversion time (~20ms for 10Kb), especially for large or many binary resources.
    • Cost: Developers must ensure they can obtain resources as ArrayBuffer (when downloading or reading locally).
    • Applicable Scenarios: All binary resources that need interception/replacement (images, fonts, wasm, audio/video, compressed JS/CSS).
    • Code Example (Best Practice):
webViewController.onInterceptRequest((request) => {
  if (shouldIntercept(request.url)) {
    // Best practice: your cache/resource logic returns ArrayBuffer directly
    let resourceArrayBuffer: ArrayBuffer = getResourceAsArrayBuffer(request.url);
    return {
      data: resourceArrayBuffer, // Return ArrayBuffer directly!
      mimeType: getMimeType(request.url),
      encoding: 'identity', // Binary resources usually don't need character encoding
      statusCode: 200,
      reasonPhrase: 'OK',
    };
  }
  return null;
});
Enter fullscreen mode Exit fullscreen mode

Summary & Conclusion:

So, folks, don't you think these "black tech" web optimizations from HarmonyOS are awesome? From "pre-launching" processes to ultimate "pre-rendering," from "pre-downloading" resources to making good use of "bytecode cache" and "ArrayBuffer optimization," this combo can make H5 pages in your HarmonyOS app lightning fast, with user experience maxed out! 🌟

The official docs are like a treasure trove, and this time the ArkWeb performance optimization guide is full of real value. In actual development, you don't need to use everything at once—choose as needed and within your means:

  • Pre-launching processes are highly cost-effective and great for high-frequency pages.
  • Pre-download/interception replacement is flexible and practical, especially with ArrayBuffer and bytecode cache management.
  • Pre-rendering is explosive but costly—save it for the most important "trump card" pages.
  • Pre-connect/pre-resolve are best used with other optimizations (like preloading).
  • Pre-fetch POST—be sure it's safe!
  • Bytecode cache and ArrayBuffer optimization are basic skills—do them well.

I recommend you bookmark this breakdown. Next time you optimize HarmonyOS app performance, don't forget to try these tricks! Tested and effective—whoever uses them knows!

Ending Interaction:

Have you used any of these optimization techniques in your projects? How did they work? Or have you encountered any pitfalls? Feel free to discuss in the comments! Also, share any HarmonyOS development treasures you've found! Let's learn and improve together! 💪 #HarmonyOS #ArkWeb #PerformanceOptimization #WebLoading #DeveloperTreasure

Top comments (0)