xugenyuan

ref |> 文章详情页增加Web容器复用逻辑01

Signed-off-by: xugenyuan <xugenyuan@wondertek.com.cn>
1 export { WdWebComponent } from './src/main/ets/pages/WdWebComponent' 1 export { WdWebComponent } from './src/main/ets/pages/WdWebComponent'
2 2
3 export { WdWebLocalComponent } from './src/main/ets/pages/WdWebLocalComponent' 3 export { WdWebLocalComponent } from './src/main/ets/pages/WdWebLocalComponent'
  4 +
  5 +export { WebEvents, WebNodeController } from './src/main/ets/webPool/WebLocalPool'
  6 +export { WebPoolManager } from './src/main/ets/webPool/WebPoolManager'
  7 +export { WebArticleEventHandler } from './src/main/ets/webPool/WebArticleEventHandler'
@@ -9,18 +9,21 @@ import { window } from '@kit.ArkUI'; @@ -9,18 +9,21 @@ import { window } from '@kit.ArkUI';
9 import { NativeCallH5Type,NativeCallH5Event,eventParams } from './NativeCallH5Type'; 9 import { NativeCallH5Type,NativeCallH5Event,eventParams } from './NativeCallH5Type';
10 import { AudioSuspensionModel } from '../viewmodel/AudioSuspensionModel' 10 import { AudioSuspensionModel } from '../viewmodel/AudioSuspensionModel'
11 import { BusinessError } from '@kit.BasicServicesKit'; 11 import { BusinessError } from '@kit.BasicServicesKit';
  12 +import { WebPoolManager } from '../webPool/WebPoolManager';
  13 +import { WebEvents } from '../webPool/WebLocalPool';
12 14
13 const TAG = 'WdWebLocalComponent'; 15 const TAG = 'WdWebLocalComponent';
14 16
15 @Component 17 @Component
16 export struct WdWebLocalComponent { 18 export struct WdWebLocalComponent {
  19 + webPoolTargetId: string = ''
17 webviewControl: BridgeWebViewControl = new BridgeWebViewControl() 20 webviewControl: BridgeWebViewControl = new BridgeWebViewControl()
18 onWebPrepared: () => void = () => { 21 onWebPrepared: () => void = () => {
19 } 22 }
20 @Prop backVisibility: boolean = false 23 @Prop backVisibility: boolean = false
21 @Prop webResource: Resource = {} as Resource 24 @Prop webResource: Resource = {} as Resource
22 @Prop @Watch('onReloadStateChanged') reload: number = 0 25 @Prop @Watch('onReloadStateChanged') reload: number = 0
23 - @State webHeight: string | number = '100%' 26 + @State webHeight: Length = '100%'
24 @Link isPageEnd: boolean 27 @Link isPageEnd: boolean
25 @State videoUrl: string = '' 28 @State videoUrl: string = ''
26 @State positionWidth: number = 0 29 @State positionWidth: number = 0
@@ -75,6 +78,18 @@ export struct WdWebLocalComponent { @@ -75,6 +78,18 @@ export struct WdWebLocalComponent {
75 } 78 }
76 79
77 aboutToAppear(): void { 80 aboutToAppear(): void {
  81 +
  82 + let eventHandler = WebPoolManager.sharedInstance().getWebEventHandler(this.webPoolTargetId)
  83 + if (!eventHandler) {
  84 + Logger.error(TAG, '当前target不应拿不到nodeController');
  85 + } else {
  86 + eventHandler.pageEndBlock = this.onPageEnd.bind(this)
  87 + if (eventHandler.pageLoadEnd) {
  88 + this.onPageEnd()
  89 + }
  90 + eventHandler.currentPageOperateBlock = this.currentPageOperate.bind(this)
  91 + }
  92 +
78 EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_STATUS, (status) => { 93 EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_STATUS, (status) => {
79 Logger.debug(TAG, "接收音频播放状态 = " + status); 94 Logger.debug(TAG, "接收音频播放状态 = " + status);
80 if (!this.isPause) { 95 if (!this.isPause) {
@@ -101,50 +116,7 @@ export struct WdWebLocalComponent { @@ -101,50 +116,7 @@ export struct WdWebLocalComponent {
101 116
102 Row() { 117 Row() {
103 RelativeContainer() { 118 RelativeContainer() {
104 - Web({  
105 - src: this.webResource,  
106 - controller: this.webviewControl,  
107 - renderMode: RenderMode.SYNC_RENDER  
108 - })// Web({ src: this.webResource, controller: this.webviewControl })  
109 - .backgroundColor(Color.White)  
110 - .domStorageAccess(true)  
111 - .databaseAccess(true)  
112 - .javaScriptAccess(true)  
113 - .imageAccess(true)  
114 - .mixedMode(MixedMode.All)  
115 - .onlineImageAccess(true)  
116 - .cacheMode(this.mode)  
117 - // .enableNativeEmbedMode(true)  
118 - // .layoutMode(WebLayoutMode.FIT_CONTENT)  
119 - .nestedScroll({  
120 - scrollForward: NestedScrollMode.SELF_FIRST,  
121 - scrollBackward: NestedScrollMode.PARENT_FIRST  
122 - })  
123 - .width('100%')  
124 - .height(this.webHeight)  
125 - .onPageBegin((event) => {  
126 - this.onPageBegin(event?.url);  
127 - })  
128 - .onPageEnd((event) => {  
129 - this.onPageEnd(event?.url)  
130 - })  
131 - .onLoadIntercept((event) => {  
132 - let url: string = event.data.getRequestUrl().toString()  
133 - url = url.replace("%(?![0-9a-fA-F]{2})", "%25")  
134 - .replace("\\+", "%2B");  
135 - url = decodeURIComponent(url)  
136 - Logger.debug(TAG, 'Web onLoadIntercept url: ' + url);  
137 - if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {  
138 - this.webviewControl.handlerReturnData(url)  
139 - return true  
140 - }  
141 - if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) {  
142 - // this.webviewControl.flushMessageQueue()  
143 - this.webviewControl.receiveDataFromH5(url)  
144 - return true  
145 - }  
146 - return this.onLoadIntercept(event.data.getRequestUrl().toString());  
147 - }) 119 + NodeContainer(WebPoolManager.sharedInstance().getWebNodeController(this.webPoolTargetId))
148 .id('web') 120 .id('web')
149 .alignRules({ 121 .alignRules({
150 top: { anchor: "__container__", align: VerticalAlign.Top }, 122 top: { anchor: "__container__", align: VerticalAlign.Top },
@@ -171,20 +143,6 @@ export struct WdWebLocalComponent { @@ -171,20 +143,6 @@ export struct WdWebLocalComponent {
171 } 143 }
172 } 144 }
173 145
174 - private registerHandlers(): void {  
175 - // 注册h5调用js相关  
176 - for (let i = 0; i < H5CallNativeType.JsCallTypeList.length; i++) {  
177 - let handleName = H5CallNativeType.JsCallTypeList[i];  
178 - let handle = (data: Message, f: Callback) => {  
179 - Logger.debug('registerHandlers handlerName: ' + JSON.stringify(data.data))  
180 - this.setCurrentPageOperate8(data)  
181 - this.setCurrentPageOperate9(data)  
182 - this.defaultPerformJSCallNative(data, f)  
183 - };  
184 - this.webviewControl.registerHandler(handleName, { handle: handle });  
185 - }  
186 - }  
187 -  
188 //webview 高度设置 146 //webview 高度设置
189 private setCurrentPageOperate8: (data: Message) => void = (data) => { 147 private setCurrentPageOperate8: (data: Message) => void = (data) => {
190 148
@@ -198,6 +156,8 @@ export struct WdWebLocalComponent { @@ -198,6 +156,8 @@ export struct WdWebLocalComponent {
198 { 156 {
199 this.webHeight = Number(data?.data?.webViewHeight) || '100%' 157 this.webHeight = Number(data?.data?.webViewHeight) || '100%'
200 } 158 }
  159 +
  160 + WebPoolManager.sharedInstance().getWebNodeController(this.webPoolTargetId)?.updateWebHeight(this.webHeight)
201 } 161 }
202 } 162 }
203 // 暂停音频悬浮窗 163 // 暂停音频悬浮窗
@@ -228,29 +188,18 @@ export struct WdWebLocalComponent { @@ -228,29 +188,18 @@ export struct WdWebLocalComponent {
228 /** 188 /**
229 * 默认【CallNative】逻辑处理 189 * 默认【CallNative】逻辑处理
230 */ 190 */
231 - private defaultPerformJSCallNative: (data: Message, f: Callback) => void = (data: Message, f: Callback) => { 191 + private currentPageOperate: (data: Message, f: Callback) => void = (data: Message, f: Callback) => {
  192 + this.setCurrentPageOperate8(data)
  193 + this.setCurrentPageOperate9(data)
232 performJSCallNative(data, f) 194 performJSCallNative(data, f)
233 } 195 }
234 196
235 - onPageBegin: (url?: string) => void = () => {  
236 - Logger.debug(TAG, 'onPageBegin');  
237 - this.registerHandlers();  
238 - //有时序问题 必须延时执行  
239 - setTimeout(() => {  
240 - BridgeUtil.webViewLoadLocalJs(getContext(this), this.webviewControl)  
241 - }, 200)  
242 - }  
243 onPageEnd: (url?: string) => void = () => { 197 onPageEnd: (url?: string) => void = () => {
244 Logger.debug(TAG, 'onPageEnd'); 198 Logger.debug(TAG, 'onPageEnd');
245 this.onWebPrepared() 199 this.onWebPrepared()
246 this.isPageEnd = true 200 this.isPageEnd = true
247 } 201 }
248 202
249 - onLoadIntercept: (url?: string) => boolean = () => {  
250 - Logger.debug(TAG, 'onLoadIntercept return false');  
251 - return false  
252 - }  
253 -  
254 onReloadStateChanged() { 203 onReloadStateChanged() {
255 Logger.info(TAG, `onReloadStateChanged:::refresh, this.reload: ${this.reload}`); 204 Logger.info(TAG, `onReloadStateChanged:::refresh, this.reload: ${this.reload}`);
256 if (this.reload > 0) { 205 if (this.reload > 0) {
  1 +import { BridgeUtil, BridgeWebViewControl, Callback } from "wdJsBridge";
  2 +import { Message } from "wdJsBridge/src/main/ets/bean/Message";
  3 +import { Logger } from "wdKit";
  4 +import { H5CallNativeType } from "../pages/H5CallNativeType";
  5 +import { WebEvents } from "./WebLocalPool";
  6 +
  7 +const TAG = 'WebArticleEventHandler'
  8 +
  9 +export class WebArticleEventHandler implements WebEvents {
  10 + webviewControl?: BridgeWebViewControl
  11 +
  12 + pageLoadEnd: boolean = false
  13 + pageEndBlock?:(event: OnPageEndEvent) => void
  14 +
  15 + currentPageOperateBlock?: (data: Message, f: Callback) => void
  16 +
  17 + constructor(webviewControl: BridgeWebViewControl) {
  18 + this.webviewControl = webviewControl
  19 + }
  20 +
  21 +
  22 + //MARK: ----
  23 + onPrepareForReuse(): void {
  24 +
  25 + }
  26 +
  27 + onPageBegin(event: OnPageBeginEvent): void {
  28 + Logger.debug(TAG, 'onPageBegin');
  29 + this.registerHandlers();
  30 + //有时序问题 必须延时执行
  31 + setTimeout(() => {
  32 + BridgeUtil.webViewLoadLocalJs(getContext(this), this.webviewControl!)
  33 + }, 200)
  34 + }
  35 +
  36 + onPageEnd(event: OnPageEndEvent): void {
  37 + Logger.debug(TAG, 'onPageEnd');
  38 + this.pageLoadEnd = true
  39 + if (this.pageEndBlock) {
  40 + this.pageEndBlock(event)
  41 + }
  42 + }
  43 +
  44 + onLoadIntercept(event: OnLoadInterceptEvent): boolean {
  45 + let url: string = event.data.getRequestUrl().toString()
  46 + url = url.replace("%(?![0-9a-fA-F]{2})", "%25")
  47 + .replace("\\+", "%2B");
  48 + url = decodeURIComponent(url)
  49 + Logger.debug(TAG, 'Web onLoadIntercept url: ' + url);
  50 + if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {
  51 + this.webviewControl?.handlerReturnData(url)
  52 + return true
  53 + }
  54 + if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) {
  55 + // this.webviewControl.flushMessageQueue()
  56 + this.webviewControl?.receiveDataFromH5(url)
  57 + return true
  58 + }
  59 + return false;
  60 + }
  61 +
  62 + private registerHandlers(): void {
  63 + // 注册h5调用js相关
  64 + for (let i = 0; i < H5CallNativeType.JsCallTypeList.length; i++) {
  65 + let handleName = H5CallNativeType.JsCallTypeList[i];
  66 + let handle = (data: Message, f: Callback) => {
  67 + Logger.debug('registerHandlers handlerName: ' + JSON.stringify(data.data))
  68 + if (this.currentPageOperateBlock) {
  69 + this.currentPageOperateBlock(data, f)
  70 + }
  71 + };
  72 + this.webviewControl?.registerHandler(handleName, { handle: handle });
  73 + }
  74 + }
  75 +
  76 +}
  1 +import { UIContext } from '@ohos.arkui.UIContext';
  2 +import { NodeController, BuilderNode, FrameNode } from '@ohos.arkui.node';
  3 +import { BusinessError } from '@ohos.base';
  4 +import { WebNetErrorList } from '@ohos.web.netErrorList';
  5 +import { Logger } from 'wdKit';
  6 +
  7 +// @Builder中为动态组件的具体组件内容
  8 +// WebViewPoolData为入参封装类
  9 +class WebViewPoolData {
  10 + target: string = ''
  11 + webOption: WebOptions = { } as WebOptions
  12 + webEvents?: WebEvents = undefined
  13 + webHeight: Length = 0
  14 +
  15 + constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
  16 + this.webOption = webOption
  17 + this.webEvents = webEvent
  18 + this.target = target
  19 + this.webHeight = webHeight
  20 + }
  21 +}
  22 +
  23 +const TAG = 'WebViewPool';
  24 +
  25 +// 影响web组件加载的网络错误码
  26 +class webLoadFail {
  27 + static ERR_TIMED_OUT = WebNetErrorList.ERR_TIMED_OUT;
  28 + static ERR_NETWORK_CHANGED = WebNetErrorList.ERR_NETWORK_CHANGED;
  29 +}
  30 +
  31 +// 不需要重试刷新的网络错误码
  32 +class excludeRetryLoad {
  33 + static ERR_FAILED = WebNetErrorList.ERR_FAILED;
  34 +}
  35 +
  36 +function isErrorCode(code: string) {
  37 + return Object.keys(webLoadFail).includes(code);
  38 +}
  39 +
  40 +@Component
  41 +export struct DynamicWebView {
  42 + target: string = '';
  43 + private webOption: WebOptions = { } as WebOptions
  44 + private webEvents?: WebEvents = undefined
  45 + @Prop webHeight: Length
  46 +
  47 + aboutToAppear(): void {
  48 +
  49 + }
  50 +
  51 + build() {
  52 + Row() {
  53 + Web(this.webOption)
  54 + .backgroundColor(Color.White)
  55 + .domStorageAccess(true)
  56 + .databaseAccess(true)
  57 + .javaScriptAccess(true)
  58 + .imageAccess(true)
  59 + .mixedMode(MixedMode.All)
  60 + .onlineImageAccess(true)
  61 + .nestedScroll({
  62 + scrollForward: NestedScrollMode.SELF_FIRST,
  63 + scrollBackward: NestedScrollMode.PARENT_FIRST
  64 + })
  65 + .width('100%')
  66 + .height('100%')
  67 + .onPageBegin((event) => {
  68 + this.webEvents?.onPageBegin(event)
  69 + })
  70 + .onPageEnd((event) => {
  71 + this.webEvents?.onPageEnd(event)
  72 + })
  73 + .onLoadIntercept((event) => {
  74 + if (!this.webEvents) {
  75 + return false
  76 + }
  77 + return this.webEvents?.onLoadIntercept(event);
  78 + })
  79 + }.width('100%')
  80 + .height(this.webHeight)
  81 + }
  82 +}
  83 +
  84 +@Builder
  85 +export function webBuilder(data: WebViewPoolData) {
  86 + DynamicWebView({
  87 + webOption: data.webOption,
  88 + target: data.target,
  89 + webEvents: data.webEvents,
  90 + webHeight: data.webHeight
  91 + })
  92 +}
  93 +
  94 +export class WebNodeController extends NodeController {
  95 + private rootNode: BuilderNode<[WebViewPoolData]> | null = null;
  96 + public webOption: WebOptions = { } as WebOptions
  97 + public webEvents?: WebEvents = undefined
  98 + private webHeight: Length = 0
  99 + private target: string = '';
  100 + public webNodeInTree:boolean = false;
  101 +
  102 + constructor(webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) {
  103 + super();
  104 + this.webOption = webOption
  105 + this.webEvents = webEvent
  106 + this.target = target
  107 + this.webHeight = webHeight
  108 + }
  109 +
  110 + aboutToAppear(): void {
  111 + this.webNodeInTree = true;
  112 + Logger.debug(TAG, `WebNodeController aboutToAppear target: ${this.target} ==> url:${this.webOption.src}`);
  113 + }
  114 +
  115 + aboutToDisappear(): void {
  116 + try {
  117 + this.webNodeInTree = false;
  118 + // 下树时给commonWebView加载空白页面
  119 + // if (!getNWeb(this.target)) {
  120 + // this.webOption.controller?.loadData(
  121 + // "<html><body bgcolor=\"#18181A\"></body></html>",
  122 + // "text/html",
  123 + // "UTF-8"
  124 + // );
  125 + // }
  126 + Logger.debug(TAG, `WebNodeController aboutToDisappear target: ${this.target} ==> url:${this.webOption.src}`);
  127 + } catch (error) {
  128 + Logger.error(TAG,`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
  129 + }
  130 + }
  131 +
  132 + makeNode(context: UIContext): FrameNode | null {
  133 + if (!context) {
  134 + Logger.error(TAG, " uicontext is undifined : " + (context === undefined));
  135 + }
  136 + if (this.rootNode != null) {
  137 + // 返回FrameNode节点
  138 + return this.rootNode.getFrameNode();
  139 + }
  140 + // 返回null控制动态组件脱离绑定节点
  141 + return null;
  142 + }
  143 +
  144 + // 此函数为自定义函数,可作为初始化函数使用
  145 + // 通过UIContext初始化BuilderNode,再通过BuilderNode中的build接口初始化@Builder中的内容
  146 + initWeb(uiContext: UIContext) {
  147 + if (this.rootNode != null) {
  148 + return;
  149 + }
  150 + this.rootNode = new BuilderNode(uiContext);
  151 + this.rootNode.build(wrapBuilder<[WebViewPoolData]>(webBuilder),
  152 + new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
  153 + }
  154 +
  155 + prepareForReuse() {
  156 + this.webEvents?.onPrepareForReuse()
  157 + }
  158 +
  159 + destoryWeb() {
  160 + this.rootNode?.dispose()
  161 + this.rootNode = null
  162 + }
  163 +
  164 + updateWebHeight(webHeight: Length) {
  165 + try {
  166 + if (this.rootNode !== null) {
  167 + this.webHeight = webHeight
  168 + this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
  169 + }
  170 + } catch (err) {
  171 + Logger.warn(TAG, `commonWebView update error : ${err}`);
  172 + }
  173 + }
  174 +
  175 + updateWebEvents(webEvent: WebEvents) {
  176 + try {
  177 + if (this.rootNode !== null) {
  178 + this.webEvents = webEvent
  179 + this.rootNode.update(new WebViewPoolData(this.webOption, this.target, this.webHeight, this.webEvents));
  180 + }
  181 + } catch (err) {
  182 + Logger.warn(TAG, `commonWebView update error : ${err}`);
  183 + }
  184 + }
  185 +
  186 +
  187 +
  188 +
  189 +}
  190 +
  191 +export interface WebEvents {
  192 + onPrepareForReuse(): void
  193 + onPageBegin(event: OnPageBeginEvent): void;
  194 + onPageEnd(event: OnPageEndEvent): void;
  195 + onLoadIntercept(event: OnLoadInterceptEvent): boolean;
  196 +}
  197 +
  198 +// 创建Map保存所需要的NodeController
  199 +const nodeMap: Map<string, WebNodeControllerInfo> = new Map();
  200 +
  201 +// NodeController内容和创建时间戳
  202 +class WebNodeControllerInfo {
  203 + webNodeController?: WebNodeController;
  204 + createTime: number = 0;
  205 +
  206 + constructor(webNodeController: WebNodeController, createTime: number) {
  207 + this.webNodeController = webNodeController;
  208 + this.createTime = createTime;
  209 + }
  210 +}
  211 +
  212 +// 自定义获取NodeController接口
  213 +export const getPoolWebNodeController = (target: string): WebNodeController | undefined => {
  214 + return nodeMap.get(target)?.webNodeController;
  215 +}
  216 +
  217 +// 自定义获取WebviewController接口
  218 +export const getPoolWebController = (target: string): WebviewController | undefined => {
  219 + return nodeMap.get(target)?.webNodeController?.webOption.controller as WebviewController;
  220 +}
  221 +
  222 +// 初始化需要UIContext 需在Ability获取
  223 +export const createReuseableWeb = (uiContext: UIContext, webOption: WebOptions, target: string, webHeight: Length, webEvent?:WebEvents) => {
  224 + // 创建NodeController
  225 + let baseNode = new WebNodeController(webOption, target, webHeight, webEvent);
  226 + // 初始化自定义Web组件
  227 + baseNode.initWeb(uiContext);
  228 + nodeMap.set(target, new WebNodeControllerInfo(baseNode, Date.now()));
  229 +}
  230 +
  231 +export const destoryReuseableWeb = (target: string) => {
  232 + if (nodeMap.get(target)) {
  233 + getPoolWebNodeController(target)?.destoryWeb()
  234 + nodeMap.delete(target)
  235 + }
  236 +}
  1 +import { BridgeWebViewControl } from "wdJsBridge"
  2 +import { AppUtils } from "wdKit"
  3 +import { createReuseableWeb, destoryReuseableWeb, getPoolWebNodeController, WebNodeController } from "./WebLocalPool"
  4 +import { util } from "@kit.ArkTS"
  5 +import { WebArticleEventHandler } from "./WebArticleEventHandler"
  6 +
  7 +interface ArticleWebInterface {
  8 + uiContext: UIContext,
  9 + webSrc: string | Resource,
  10 + webController: BridgeWebViewControl
  11 + webArticleEventHandler: WebArticleEventHandler
  12 +}
  13 +
  14 +export class WebPoolManager {
  15 +
  16 + private webTargets: string[] = []
  17 +
  18 + private constructor() {
  19 + }
  20 + private static manager: WebPoolManager
  21 + static sharedInstance() : WebPoolManager {
  22 + if (!WebPoolManager.manager) {
  23 + WebPoolManager.manager = new WebPoolManager()
  24 + }
  25 + return WebPoolManager.manager
  26 + }
  27 +
  28 + createArticleWeb(param: ArticleWebInterface, taking?: boolean) : string {
  29 + let target = util.generateRandomUUID()
  30 + createReuseableWeb(param.uiContext,
  31 + {src: param.webSrc, controller: param.webController},
  32 + target,
  33 + 800,
  34 + param.webArticleEventHandler
  35 + )
  36 + if (taking != true) {
  37 + this.webTargets.push(target)
  38 + }
  39 + return target
  40 + }
  41 +
  42 + // 取一个可用的target,不存在则根据参数创建
  43 + // 当使用完成,需要调用recycleTarget()
  44 + takeAvaiableArticleWebTarget(param: ArticleWebInterface): string {
  45 + if (this.webTargets.length) {
  46 + return this.webTargets.slice(0, 1).pop()!
  47 + }
  48 + return this.createArticleWeb(param, true)
  49 + }
  50 +
  51 + // 回收target
  52 + recycleTarget(target: string) {
  53 + let nodeController = getPoolWebNodeController(target)
  54 + if (!nodeController) {
  55 + let index = this.webTargets.indexOf(target)
  56 + if (index >= 0) {
  57 + this.webTargets.splice(index, 1)
  58 + }
  59 + return
  60 + }
  61 +
  62 + nodeController.prepareForReuse()
  63 + this.webTargets.push(target)
  64 + }
  65 +
  66 + // 撤底销毁
  67 + destoryTarget(target: string) {
  68 + destoryReuseableWeb(target)
  69 + let index = this.webTargets.indexOf(target)
  70 + if (index >= 0) {
  71 + this.webTargets.splice(index, 1)
  72 + }
  73 + }
  74 +
  75 + getWebNodeController(target: string) : WebNodeController | undefined {
  76 + return getPoolWebNodeController(target)
  77 + }
  78 + getWebController(target: string) : BridgeWebViewControl | undefined {
  79 + return getPoolWebNodeController(target)?.webOption.controller as BridgeWebViewControl
  80 + }
  81 + getWebEventHandler(target: string) : WebArticleEventHandler | undefined {
  82 + return getPoolWebNodeController(target)?.webEvents as WebArticleEventHandler
  83 + }
  84 +
  85 +
  86 +}
@@ -8,7 +8,7 @@ import { @@ -8,7 +8,7 @@ import {
8 } from 'wdBean'; 8 } from 'wdBean';
9 import { Logger, SPHelper, NetworkUtil, DisplayUtils } from 'wdKit'; 9 import { Logger, SPHelper, NetworkUtil, DisplayUtils } from 'wdKit';
10 import { SpConstants } from 'wdConstant'; 10 import { SpConstants } from 'wdConstant';
11 -import { WdWebLocalComponent } from 'wdWebComponent'; 11 +import { WdWebLocalComponent, WebArticleEventHandler, WebPoolManager } from 'wdWebComponent';
12 import { NativeCallH5Type } from 'wdWebComponent/src/main/ets/pages/NativeCallH5Type'; 12 import { NativeCallH5Type } from 'wdWebComponent/src/main/ets/pages/NativeCallH5Type';
13 import { BridgeWebViewControl } from 'wdJsBridge/Index'; 13 import { BridgeWebViewControl } from 'wdJsBridge/Index';
14 14
@@ -19,6 +19,7 @@ export struct ImageAndTextWebComponent { @@ -19,6 +19,7 @@ export struct ImageAndTextWebComponent {
19 @State reload: number = 0; 19 @State reload: number = 0;
20 @Link isPageEnd: boolean 20 @Link isPageEnd: boolean
21 @Prop @Watch('onDetailDataUpdated') contentDetailData: ContentDetailDTO [] = [] as ContentDetailDTO [] 21 @Prop @Watch('onDetailDataUpdated') contentDetailData: ContentDetailDTO [] = [] as ContentDetailDTO []
  22 + webPoolTargetId: string = ''
22 webviewControl: BridgeWebViewControl = new BridgeWebViewControl() 23 webviewControl: BridgeWebViewControl = new BridgeWebViewControl()
23 private h5ReceiveAppData: H5ReceiveDetailBean = { dataSource: '2' } as H5ReceiveDetailBean 24 private h5ReceiveAppData: H5ReceiveDetailBean = { dataSource: '2' } as H5ReceiveDetailBean
24 private webPrepared = false; 25 private webPrepared = false;
@@ -95,11 +96,20 @@ export struct ImageAndTextWebComponent { @@ -95,11 +96,20 @@ export struct ImageAndTextWebComponent {
95 96
96 aboutToAppear(): void { 97 aboutToAppear(): void {
97 Logger.debug(TAG, 'H5模板加载控件 aboutToAppear'); 98 Logger.debug(TAG, 'H5模板加载控件 aboutToAppear');
  99 +
  100 + this.webPoolTargetId = WebPoolManager.sharedInstance().takeAvaiableArticleWebTarget({
  101 + uiContext: this.getUIContext(),
  102 + webSrc: $rawfile('apph5/index.html'),
  103 + webController: this.webviewControl,
  104 + webArticleEventHandler: new WebArticleEventHandler(this.webviewControl)
  105 + })
  106 + this.webviewControl = WebPoolManager.sharedInstance().getWebController(this.webPoolTargetId)!
98 } 107 }
99 108
100 build() { 109 build() {
101 Column() { 110 Column() {
102 WdWebLocalComponent({ 111 WdWebLocalComponent({
  112 + webPoolTargetId: this.webPoolTargetId,
103 webviewControl: this.webviewControl, 113 webviewControl: this.webviewControl,
104 reload:this.reload, 114 reload:this.reload,
105 webResource: $rawfile('apph5/index.html'), 115 webResource: $rawfile('apph5/index.html'),