WebLocalPool.ets 6.89 KB
import { UIContext } from '@ohos.arkui.UIContext';
import { NodeController, BuilderNode, FrameNode } from '@ohos.arkui.node';
import { BusinessError } from '@ohos.base';
import { WebNetErrorList } from '@ohos.web.netErrorList';
import { Logger } from 'wdKit';

// @Builder中为动态组件的具体组件内容
// WebViewPoolData为入参封装类
class WebViewPoolData {
  target: string = ''
  webOption: WebOptions = { } as WebOptions
  webEvents?: WebEvents = undefined
  webHeight: Length = 0

  constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
    this.webOption = webOption
    this.webEvents = webEvent
    this.target = target
    this.webHeight = webHeight
  }
}

const TAG = 'WebViewPool';

// 影响web组件加载的网络错误码
class webLoadFail {
  static ERR_TIMED_OUT = WebNetErrorList.ERR_TIMED_OUT;
  static ERR_NETWORK_CHANGED = WebNetErrorList.ERR_NETWORK_CHANGED;
}

// 不需要重试刷新的网络错误码
class excludeRetryLoad {
  static ERR_FAILED = WebNetErrorList.ERR_FAILED;
}

function isErrorCode(code: string) {
  return Object.keys(webLoadFail).includes(code);
}

@Component
export struct DynamicWebView {
  target: string = '';
  private webOption: WebOptions = { } as WebOptions
  private webEvents?: WebEvents = undefined
  @Prop webHeight: Length

  aboutToAppear(): void {

  }

  build() {
    Row() {
      Web(this.webOption)
        .backgroundColor(Color.White)
        .domStorageAccess(true)
        .databaseAccess(true)
        .javaScriptAccess(true)
        .imageAccess(true)
        .mixedMode(MixedMode.All)
        .onlineImageAccess(true)
        .nestedScroll({
          scrollForward: NestedScrollMode.SELF_FIRST,
          scrollBackward: NestedScrollMode.PARENT_FIRST
        })
        .width('100%')
        .height('100%')
        .onPageBegin((event) => {
          this.webEvents?.onPageBegin(event)
        })
        .onPageEnd((event) => {
          this.webEvents?.onPageEnd(event)
        })
        .onLoadIntercept((event) => {
          if (!this.webEvents) {
            return false
          }
          return this.webEvents?.onLoadIntercept(event);
        })
    }.width('100%')
    .height(this.webHeight)
  }
}

@Builder
export function webBuilder(data: WebViewPoolData) {
  DynamicWebView({
    webOption: data.webOption,
    target: data.target,
    webEvents: data.webEvents,
    webHeight: data.webHeight
  })
}

export class WebNodeController extends NodeController {
  private rootNode: BuilderNode<[WebViewPoolData]> | null = null;
  public webOption: WebOptions = { } as WebOptions
  public webEvents?: WebEvents = undefined
  private webHeight: Length = 0
  private target: string = '';
  public webNodeInTree:boolean = false;

  constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
    super();
    this.webOption = webOption
    this.webEvents = webEvent
    this.target = target
    this.webHeight = webHeight
  }

  aboutToAppear(): void {
    this.webNodeInTree = true;
    Logger.debug(TAG, `WebNodeController aboutToAppear target: ${this.target} ==> url:${this.webOption.src}`);
  }

  aboutToDisappear(): void {
    try {
      this.webNodeInTree = false;
      // 下树时给commonWebView加载空白页面
      // if (!getNWeb(this.target)) {
      //   this.webOption.controller?.loadData(
      //     "<html><body bgcolor=\"#18181A\"></body></html>",
      //     "text/html",
      //     "UTF-8"
      //   );
      // }
      Logger.debug(TAG, `WebNodeController aboutToDisappear target: ${this.target} ==> url:${this.webOption.src}`);
    } catch (error) {
      Logger.error(TAG,`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
    }
  }

  makeNode(context: UIContext): FrameNode | null {
    if (!context) {
      Logger.error(TAG, " uicontext is undifined : " + (context === undefined));
    }
    if (this.rootNode != null) {
      // 返回FrameNode节点
      return this.rootNode.getFrameNode();
    }
    // 返回null控制动态组件脱离绑定节点
    return null;
  }

  // 此函数为自定义函数,可作为初始化函数使用
  // 通过UIContext初始化BuilderNode,再通过BuilderNode中的build接口初始化@Builder中的内容
  initWeb(uiContext: UIContext) {
    if (this.rootNode != null) {
      return;
    }
    this.rootNode = new BuilderNode(uiContext);
    this.rootNode.build(wrapBuilder<[WebViewPoolData]>(webBuilder),
      new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
  }

  prepareForReuse() {
    this.webEvents?.onPrepareForReuse()
  }

  destoryWeb() {
    this.rootNode?.dispose()
    this.rootNode = null
  }

  updateWebHeight(webHeight: Length) {
    try {
      if (this.rootNode !== null) {
        this.webHeight = webHeight
        this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
      }
    } catch (err) {
      Logger.warn(TAG, `commonWebView update error : ${err}`);
    }
  }

  updateWebEvents(webEvent: WebEvents) {
    try {
      if (this.rootNode !== null) {
        this.webEvents = webEvent
        this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
      }
    } catch (err) {
      Logger.warn(TAG, `commonWebView update error : ${err}`);
    }
  }




}

export interface WebEvents {
  onPrepareForReuse(): void
  onPageBegin(event: OnPageBeginEvent): void;
  onPageEnd(event: OnPageEndEvent): void;
  onLoadIntercept(event: OnLoadInterceptEvent): boolean;
}

// 创建Map保存所需要的NodeController
const nodeMap: Map<string, WebNodeControllerInfo> = new Map();

// NodeController内容和创建时间戳
class WebNodeControllerInfo {
  webNodeController?: WebNodeController;
  createTime: number = 0;

  constructor(webNodeController: WebNodeController, createTime: number) {
    this.webNodeController = webNodeController;
    this.createTime = createTime;
  }
}

// 自定义获取NodeController接口
export const getPoolWebNodeController = (target: string): WebNodeController | undefined => {
  return nodeMap.get(target)?.webNodeController;
}

// 自定义获取WebviewController接口
export const getPoolWebController = (target: string): WebviewController | undefined => {
  return nodeMap.get(target)?.webNodeController?.webOption.controller as WebviewController;
}

// 初始化需要UIContext 需在Ability获取
export const createReuseableWeb = (uiContext: UIContext, webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) => {
  // 创建NodeController
  let baseNode = new WebNodeController(webOption, target, webHeight, webEvent);
  // 初始化自定义Web组件
  baseNode.initWeb(uiContext);
  nodeMap.set(target, new WebNodeControllerInfo(baseNode, Date.now()));
}

export const destoryReuseableWeb = (target: string) => {
  if (nodeMap.get(target)) {
    getPoolWebNodeController(target)?.destoryWeb()
    nodeMap.delete(target)
  }
}