本文同步发表于 微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
当 Web 组件加载的网页(如通过window.open())尝试打开新窗口时,系统并不会自动创建,而是会通知应用层,由开发者决定如何响应该请求(如在弹窗、新页面或新窗口中展示)。这是一个典型的 “通知-响应” 模型。
整个交互流程的角色与步骤如下:
网页侧:调用
window.open()等方法请求新窗口。ArkWeb 内核:拦截请求,检查
name参数对应的 Web 组件是否存在。应用侧:通过
onWindowNew()回调接收事件,创建新窗口(如CustomDialog)并建立关联。
二、API 与配置
为了启用并管理新窗口行为,需要使用一组特定的属性和回调接口,其作用和关系如下表:
| 接口/配置 | 所属组件 | 作用与说明 | 关键点 |
|---|---|---|---|
.multiWindowAccess(true) | Web 组件属性 | 总开关:允许网页请求打开新窗口。 | 必须设置为true,否则后续流程不会触发。 |
.allowWindowOpenMethod(true) | Web 组件属性 | 脚本开关:允许网页通过window.open()等 JavaScript 方式打开新窗口。 | 通常与multiWindowAccess配合使用。 |
.onWindowNew(event) | Web 组件事件回调 | 核心事件:当网页请求新窗口时触发。开发者需在此回调中创建新窗口并建立关联。 | 回调参数event中的handler是连接新窗口与内核的关键桥梁。 |
event.handler.setWebController(controller) | onWindowNew事件参数方法 | 建立关联:将你为新窗口创建的WebviewController实例告知 ArkWeb 内核,使内核能将新网页内容注入该控制器关联的 Web 组件。 | 必须调用,否则渲染进程会阻塞。若无新窗口,参数需设为null。 |
.onActivateContent() | Web 组件事件回调 | 激活通知:当网页尝试通过window.open(url, name)打开一个已存在的name窗口时,对应name的 Web 组件会收到此回调,提示应用将其展示到前台(如切换 Tab)。 | 此回调作用于目标窗口对应的 Web 组件,而非发起请求的源组件。 |
三、示例代码
// 1. 定义新窗口的UI组件(这里使用CustomDialog模拟弹窗窗口) @CustomDialog struct NewWebViewComp { controller?: CustomDialogController; // 为新窗口创建一个独立的控制器 webviewController1: webview.WebviewController = new webview.WebviewController(); build() { Column() { // 新窗口内的Web组件,初始无内容,将由内核注入 Web({ src: "", controller: this.webviewController1 }) .javaScriptAccess(true) .multiWindowAccess(false) // 此新窗口自身不允许再开窗口(根据需要设置) .onWindowExit(() => { /* 窗口关闭处理 */ }) .onActivateContent(() => { // 如果此窗口已被绑定过name,再次被请求打开时会触发这里 console.info("NewWebViewComp onActivateContent") }) } } } @Entry @Component struct WebComponent { // 主窗口的控制器 controller: webview.WebviewController = new webview.WebviewController(); // 用于控制新弹窗 dialogController: CustomDialogController | null = null; build() { Column() { // 2. 主Web组件:加载本地网页,并开启多窗口支持 Web({ src: $rawfile("window.html"), controller: this.controller }) .javaScriptAccess(true) .multiWindowAccess(true) // 总开关:允许打开新窗口 .allowWindowOpenMethod(true) // 允许JS的window.open() // 3. 核心:处理新窗口请求 .onWindowNew((event) => { // 关闭可能已存在的旧弹窗 if (this.dialogController) { this.dialogController.close() } // 为新窗口创建控制器 let popController: webview.Webview.WebviewController = new webview.WebviewController(); // 创建并打开一个自定义弹窗作为“新窗口” this.dialogController = new CustomDialogController({ builder: NewWebViewComp({ webviewController1: popController }), isModal: false // 设置为非模态,防止影响onActivateContent回调 }); this.dialogController.open(); // 4. 关键一步:将新窗口的控制器告知Web内核,建立绑定 event.handler.setWebController(popController); // 如果不调用setWebController或传入null,内核会认为应用未创建新窗口 }) } } }网页 (window.html) 代码:
<!DOCTYPE html> <html> <body> <!-- 点击按钮,触发 window.open() --> <input type="button" value="新窗口中打开网页" onclick="OpenNewWindow()"> <script type="text/javascript"> function OpenNewWindow() { // 打开一个空白页,并写入一些内容 let openedWindow = window.open("about:blank", "", "location=no,status=no,scrollbars=no"); openedWindow.document.write("<p><br><br>打开的窗口</p>"); openedWindow.focus(); } </script> </body> </html>四、原理
五、注意事项
setWebController必须调用:在onWindowNew回调中,无论是否创建新窗口,都必须调用event.handler.setWebController(...)。如果不调用,会导致 Web 内核的渲染进程阻塞等待。模态窗口与非模态窗口:使用
CustomDialogController时,如果isModal设为true(模态),在某些情况下可能会影响后续onActivateContent等回调的触发。内存与生命周期管理:
为新窗口创建的
WebviewController和CustomDialogController需要妥善管理(如在onWindowExit回调中关闭和释放),防止内存泄漏。在
onWindowExit回调中关闭弹窗。
onActivateContent的使用:这个回调是实现类似浏览器“标签页复用”功能的关键。当用户多次点击打开同一个“命名窗口”(name相同)时,可以将已存在的窗口激活并提到前台,而不是重复创建。OnWindowNewEvent.isUserTrigger参数:在OnWindowNewEvent回调函数中有一个isUserTrigger参数,可用于区分新窗口请求是由用户主动操作(如点击)触发,还是由网页脚本自动触发。有助于更精细的控制。