一个看起来很普通的工作台交互,往往会暴露 Web 图表嵌入原生页面最难解释的地方:把侧栏折起来以后,ArkUI 给 Web 组件的区域变宽了,ECharts 的坐标轴却仍然按旧宽度绘制。有时图表只是留了一大块空白;有时用户反复切换页面,旧实例还保留着事件监听。问题不一定来自设备折叠本身,而是容器布局变化与图表内部画布更新不是同一个动作。这里用 ChartDock 作为示例项目:一个嵌在 HarmonyOS ArkWeb 中的本周订单趋势板,七天的演示数据为 [12,19,16,23,20,28,24]。收起侧栏后,构造样例的网页容器宽度为 360 CSS 像素,页面显示 COMPACT 布局;图表完成内部尺寸更新后给出 RESIZED 状态。所有数值用于解释机制,不代表设备性能或实测业务订单。## 一、尺寸究竟归谁管理原生 ArkUI 负责外层 Row、Column 与 Web 组件的可见面积;HTML 内部的 div 负责 ECharts 实际挂载节点;ECharts 再维护自己的渲染画布。这三层都有“宽度”,但不能互相当作同一个属性。Web.width('100%') 表示组件跟随父布局,不表示已存在的图表实例会自动重新计算坐标轴与画布缓存。本例的页面文件为 entry/src/main/ets/pages/ChartBoard.ets,网页放在 resources/rawfile/dashboard.html,离线库放在同目录 echarts.min.js。库需要通过项目明确选择的包版本构建并放入 rawfile;正文不声明某个未经本机验证的 SDK 或 ECharts 版本号,也不要求网页从公网取脚本。不同项目中的打包方式可以变化,但绝不能让发布包依赖开发机的本地地址。先只建立一个 Web 实例,把“收起侧栏”限定成父布局操作。onConsole 用于收集网页 JS 控制台消息,写入 DevEco 的应用日志;它不是系统自动把所有网页输出变成 HiLog。下段的代码演示回调桥接,真实项目应过滤消息等级与敏感字段后再落日志。arktsimport { webview } from '@kit.ArkWeb';@Entry@Componentstruct ChartBoard { private controller: webview.WebviewController = new webview.WebviewController(); @State private sidebarOpen: boolean = true; build() { Column({ space: 12 }) { Button(this.sidebarOpen ? '收起侧栏' : '展开侧栏') .onClick(() => { this.sidebarOpen = !this.sidebarOpen; }) Row() { if (this.sidebarOpen) { Column() { Text('订单分类') }.width(120) } Web({ src: $rawfile('dashboard.html'), controller: this.controller }) .javaScriptAccess(true) .onConsole((message) => { console.info(`[ChartDock] ${message.getMessage()}`); return false; }) .width('100%').height(340) }.width('100%') }.padding(12) }}````sidebarOpen` 是原生的布局事实,和网页内部的 `RESIZED` 不能混为一谈。示例采用固定 340vp 的 Web 高度,把讨论范围集中到横向尺寸;如果实际页面是可滚动长网页,还需要处理高度适配和滚动嵌套。`WebviewController` 放在组件字段,避免每次 build 都创建新控制器。页面离开、Ability 退后台、Web 真正销毁时的行为不同,不能仅靠按钮切换去模拟全部生命周期。## 二、不监听窗口宽度,改盯图表容器ECharts 官方文档明确说明:CSS 修改容器大小,不一定触发浏览器 `window.resize`。折叠侧栏正是这样的场景——页面根窗口可能没变,某个 `div` 却变宽了。采用 `ResizeObserver` 观察图表容器比只监听窗口事件更贴近问题源头。但不能在每次观察通知里无条件再次修改容器样式,否则可能形成循环回调。解决图表初始化的代码应先等待 DOM 已建立、容器有明确非零宽高,再调用 `echarts.init`,把图表配置与观察逻辑分开。网页的本例使用七个普通整数,并不包含真实业务接口。为了让核心逻辑清晰,以下展示 `dashboard.html` 里初始化部分;完整接入时须保证离线脚本确实被打包并可加载。html

WAITING
图表配置固定,控制的是尺寸而不是重新请求一遍数据。如果在每次 `ResizeObserver` 回调里调用 `echarts.init`,相当于把一次几何变化误判为整个业务图表重新开始,事件监听和资源都可能叠加。在这个例子里 `chart` 应该持续指向同一个实例,只有页面容器真正被移除、组件被销毁以后才释放它。![](https://i-blog.csdnimg.cn/direct/3359ee46c9d64e6cbcb40772c0e81921.png)## 三、回弹、折叠与清理组成一个闭环仅仅调用 `resize()`,还不足以处理反复切换与页面返回。图表节点还存在时使用原实例 resize;页面彻底结束时断开观察器并 dispose;如果日后通过导航重新创建了 DOM,再创建新的 ECharts 实例。不要销毁 DOM 以后复用指向旧节点的 chart,这就是“返回页面后图表空白”的另一个来源。下面这段接在同一个 HTML 的初始化脚本之后,负责订阅几何变化与退出清理。`requestAnimationFrame` 把连续通知合并到下一帧;`pending` 防止一帧排入多次同类任务。网页实际报告自己的宽度,不依赖 ArkUI 通过字符串伪造 CSS 像素。`pagehide` 在此仅用于网页退出清理,不能代替 HarmonyOS 页面生命周期下其他资源的审计。javascriptlet pending = false;let ended = false;const observer = new ResizeObserver(() => { if (ended || pending) return; pending = true; requestAnimationFrame(() => { pending = false; if (ended || host.clientWidth <= 0) return; chart.resize(); const mode = host.clientWidth <= 400 ? ‘COMPACT’ : ‘EXPANDED’; statusNode.textContent = ‘RESIZED’; console.info( CD-1811 layout=${mode} width=${host.clientWidth}px); console.info(‘CD-1811 state=RESIZED’); });});observer.observe(host);window.addEventListener(‘pagehide’, () => { ended = true; observer.disconnect(); chart.dispose();}, { once: true });```这一段的业务状态来自 host.clientWidth,只负责显示 COMPACT / EXPANDED 的案例标签。阈值 400 是 ChartDock 自己设定的演示规则,不是 HarmonyOS 折叠设备的统一断点,也不是 ECharts 要求的最小宽度。若横屏容器宽高在过渡中变成 0,直接跳过本帧而不是调用 resize,恢复可见后再根据下一次观察通知调整。若还有定时刷新或 Ajax 请求,必须独立停止,不能想当然认为 dispose() 会取消业务网络请求。## 四、日志只证明发生了哪一步技术配图中的样例是 CD-1811,收起后的容器宽度标成 360px,布局标成 COMPACT,图表状态为 RESIZED。示意 HiLog 对应两条预期桥接消息: [ChartDock] CD-1811 layout=COMPACT width=360px 与 [ChartDock] CD-1811 state=RESIZED。这是按代码规则构造的演示状态,并非在特定手机里捕获的调试记录;在实际设备上,CSS 像素宽度由视口和 Web 布局计算决定。另外需要单独覆盖一个容易被演示页掩盖的条件:侧栏从打开到关闭可能连续触发多次布局提交。观察器通知不是“业务操作完成”的回执,不能见到第一条通知就记录成功。只有图表容器已获得稳定、非零尺寸,并且本轮 resize() 已执行,页面才应显示这一轮的 RESIZED。如果要做统计,需要记录布局请求号和实际重排号,避免同一次收起被计算为多次成功;如果图表随后又进入隐藏容器,还得把状态退回等待可见,而不是继续展示陈旧的绿色标识。怎么辨别“网页不动”是布局问题还是图表实例问题?先看 DOM 容器的 clientWidth 是否改变;若没有改变,检查 ArkUI 父组件尺寸。宽度变了但 canvas 旧尺寸不变,检查 ResizeObserver 是否收到通知、回调是否被归并。收到回调却显示空白,则检查有没有误用旧容器实例或脚本资源加载失败。分析路径是从容器事实走向图表事实,不要只凭截图猜故障。 ## 五、这套方法适用于什么条件这个方案针对的是 ArkWeb 本地网页中的 ECharts 实例及其容器变化,和“将 Web 组件本身迁移到另一个窗口”不是同一能力。若使用多个图表、不同选项卡或路由,应该为每个 DOM 容器建立独立的观察器和清理职责。隐藏页短期保留实例可以节省重新绘图时间;长期保留所有图表则可能增加内存。是否释放取决于实际导航策略,不能把 pagehide 误认为会覆盖原生 UI 每一种销毁路径。还要单独验证 ECharts 对当前 ArkWeb 引擎、离线脚本加载路径和硬件设备的兼容性。本文没有在 DevEco Studio 编译,没有在真机上测量重排帧耗,也没有将生成的界面当作性能证据。它只把源码可核验的四个动作连在一起:建立 Web、初始化 ECharts、观察容器尺寸、离开时销毁图表。官方技术依据: 华为 ArkWeb Web 组件本地资源示例、 华为 Web ConsoleMessage、 Apache ECharts 图表容器与尺寸。本文 CD-1811、360px、两条日志和配图均为用于解释状态机的构造样例。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐