9afce6f6创建于 2025年5月7日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { webview } from '@kit.ArkWeb';
import { OfflineResourceManager, OfflineResourceManagerConfig } from '../common/OfflineResourceManager';
import { http } from '@kit.NetworkKit';
import { ResponseDataType } from '../common/ResponseDataType';
import { BusinessError } from '@kit.BasicServicesKit';
import { MimeType } from '../common/MimeType';

@Component
export struct SubWeb {
  // Web组件url
  @State url: ResourceStr = '';
  // NavDestination组件title
  @State title?: string = '';
  controller: webview.WebviewController = new webview.WebviewController();
  // 内存缓存容量
  memoryCacheCapacity?: number;
  // 磁盘缓存容量
  diskCacheCapacity?: number;
  // 需要被缓存的资源请求url,正则字符串数组
  cacheableResourceUrls?: Array<string>;
  // 离线资源管理器
  offlineResourceManager?: OfflineResourceManager;
  // 离线包路径
  offlinePath?: string;
  // 自盘缓存路径
  diskCachePath?: string;

  aboutToAppear(): void {
    webview.WebviewController.setWebDebuggingAccess(true);
    let offlineResourceManagerConfig: OfflineResourceManagerConfig = {
      offlinePath: this.offlinePath,
      memoryCacheCapacity: this.memoryCacheCapacity,
      diskCacheCapacity: this.diskCacheCapacity,
      diskCachePath: this.diskCachePath
    }
    this.offlineResourceManager = new OfflineResourceManager(offlineResourceManagerConfig);
  }

  build() {
    NavDestination() {
      Web({ src: this.url, controller: this.controller })
        .javaScriptAccess(true)
        .onInterceptRequest((event) => {
          // 资源请求url
          let url: string = event.request.getRequestUrl();

          // 如果不是http/https请求,走原始逻辑
          if (!url.startsWith('http')) {
            return null;
          }

          // 资源类型:如https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.0/anime.min.js
          // 处理后type值为js
          let type: string;
          type = (url.split('.').pop() as string);
          // url匹配,是否为需要缓存的资源请求
          let matched: boolean = false;
          let cacheableResourceUrls: Array<string> = this.cacheableResourceUrls as Array<string>;
          for (let cacheableResourceUrl of cacheableResourceUrls) {
            if (new RegExp(cacheableResourceUrl, 'g').exec(url)) {
              matched = true;
              break;
            }
          }

          // 开发者可以增加缓存策略,例如:
          // 1、 网络优先还是缓存优先
          // 2、 增加缓存生效时间
          // 3、 优先使用缓存,保证页面快速加载,同时通过对比版本号等方法,发起请求更新缓存资源,后续重新加载页面时使用新的缓存

          if (matched) {
            // 查询缓存
            let cacheResource = (this.offlineResourceManager as OfflineResourceManager).fetchFromCache(url, type);
            // 构造请求响应
            let responseWeb: WebResourceResponse = new WebResourceResponse();
            const promise: Promise<String> = new Promise((resolve: Function, reject: Function) => {
              if (cacheResource) {
                // 返回缓存
                responseWeb.setResponseData(cacheResource as ResponseDataType);
                resolve('success');
              } else {
                // 发起请求
                let httpRequest: http.HttpRequest = http.createHttp();

                httpRequest.request(url, (err: BusinessError, data: http.HttpResponse) => {
                  if (data) {
                    // 数据正常返回
                    (this.offlineResourceManager as OfflineResourceManager).submitToCache(url,
                      data.result as ResponseDataType)
                    responseWeb.setResponseData(data.result as ResponseDataType);
                  } else {
                    // 请求出错,返回null
                    responseWeb.setResponseData(null);
                  }

                  responseWeb.setResponseMimeType(MimeType[type]);
                  responseWeb.setReasonMessage('OK');
                  responseWeb.setResponseCode(200);
                  // http响应返回后将promise设置成resolve状态
                  resolve('success');
                })
              }
            })
            promise.then(() => {
              responseWeb.setResponseIsReady(true);
            })
            responseWeb.setResponseIsReady(false);
            return responseWeb;
          }

          return null;
        })
    }
    .title(this.title)
  }
}