在鸿蒙应用开发中,WebView 依然是绕不开的能力 。
无论是 H5 活动页、配置化页面,还是业务兜底方案,Web 都扮演着重要角色。
鸿蒙官方虽然提供了 WebView 能力,但在实际项目中,直接使用原生能力往往不够顺手,例如:
- JS 交互方式分散、不统一
- Web 生命周期与页面路由难以协同
- 参数传递、标题管理、返回逻辑重复
- Web 参数配置容易踩坑(尤其是 mixedMode)
因此,本文基于官方组件 @pura/harmony-web,结合实际业务场景,进行了二次封装与工程化改造,并总结了一些关键设计点与注意事项。
原始组件地址:
https://ohpm.openharmony.cn/#/cn/detail/@pura%2Fharmony-web
一、整体设计目标
本次封装的目标并不是"再造 WebView",而是解决真实业务中的常见问题:
- 统一 Web 页面路由承载方式
- 统一 JS ↔ Native 交互模型
- 控制 Web 参数配置的"自由度",避免误用
- 让 Web 页面像普通 ArkUI 页面一样可控、可维护
最终希望做到的是:
Web 页面只是一个特殊的页面,而不是一个"特殊系统"
二、封装基础:基于 harmony-web 的二次开发
博主本次并未直接使用底层 webview.WebviewController + 原生组件拼装,而是:
- 以 @pura/harmony-web 为基础
- 在其之上做二次封装和能力收敛
- 对外仅暴露必要参数和交互接口
这样做的好处是:
- 避免直接操作大量 Web 细节
- 保证项目内 Web 行为的一致性
- 后续替换或升级 Web 内核成本更低
三、Web 与 JS 交互:多参数调用的正确姿势
在 Web 与 Native 的交互中,一个非常常见的需求是:
👉 从 Native 主动调用 JS 方法,并传递多个参数
正确的做法
如果 JS 方法需要多个参数,推荐通过字符串拼接 + 标准 JS 调用形式:
const trainJsCmd = `getToken('${token}', '${apiVersion}')`
this.runJavaScript(trainJsCmd)
.then((data) => {
// JS 执行成功回调
})
.catch((error: Error) => {
// JS 执行失败处理
})
这样设计的原因
- WebView 本质是执行 JS 字符串
- 多参数场景下,显式构造函数调用最直观
- 避免 JSON 序列化 / 反序列化带来的兼容性问题
⚠️ 实践经验:
不要幻想 WebView 能帮你"智能解析参数",一切都要明确、可控。
四、mixedMode:一个非常容易被滥用的配置
在 WebView 配置中,mixedMode 是一个高风险参数。
很多开发在遇到资源加载异常时,会下意识设置:
mixedMode: MixedMode.All
强烈建议:请勿乱用 mixedMode
原因很简单:
- mixedMode 会放开 HTTPS 页面加载 HTTP 资源
- 存在安全风险
- 在部分系统版本上,会引发资源加载异常或不可预期行为
✅ 正确做法是:
- 优先保证 H5 资源本身的协议一致性
- 只有在明确知道后果的前提下才调整 mixedMode
- 不在基础封装层默认开启
基础组件的职责是"兜底",不是"纵容错误配置"
五、页面自适应:缩放到适配屏幕的关键配置
在实际项目中,H5 页面经常出现:
- 字体过大 / 过小
- 页面无法完整适配屏幕
- 看起来像"放大版网页"
在 ArkWeb 中,如果希望页面自适应屏幕比例,需要注意两个关键配置:
this.options.initialScale = 0
this.options.textZoomRatio = 0
这两个参数的意义
initialScale = 0:让 WebView 自行计算初始缩放比例textZoomRatio = 0:关闭额外的文本缩放干预
这是一个组合配置 ,缺一不可。
只设置其中一个,往往效果不完整。
六、核心页面结构设计
下面是本次封装后的 Web 页面核心结构(已做工程化整合):
关键设计点说明
- 使用
@Route承载 Web 页面,纳入统一路由体系 - Web 页面与普通 ArkUI 页面拥有一致的:
-
- 返回逻辑
- 标题管理
- 生命周期控制
- JS 对象通过
arkJsObject注入 - WebClient 统一处理标题、加载事件
核心代码结构(节选)
@Route({ name: RouterConst.PAGE_WEB_VIEW })
@ComponentV2
export struct WebViewPage {
private webviewVM: WebViewPageVM = new WebViewPageVM()
private webviewController: WebviewController = new webview.WebviewController()
@Local webUrl: string = ""
@Local options: ArkWebOptions = new ArkWebOptions()
@Local webClient: MyWebClient = new MyWebClient()
@Local private jsObject: WebJsObj = new WebJsObj()
aboutToAppear(): void {
// 读取路由参数
// Web 配置
this.options.fileAccess = true
this.options.databaseAccess = true
this.options.allowWindowOpenMethod = true
this.options.initialScale = 0
this.options.textZoomRatio = 0
}
build() {
ComNavDestination({
baseVM: this.webviewVM,
onBackPress: () => this.onBackController()
}) {
Column() {
ArkWeb({
controller: this.webviewController,
src: this.webUrl,
options: this.options,
webClient: this.webClient,
arkJsObject: this.jsObject
})
}
}
}
}
七、返回逻辑:Web 优先,页面兜底
在返回逻辑上,采用了一个非常明确的原则:
能 Web 内返回,就不直接退出页面
private onBackController(): boolean {
if (this.webviewController?.accessBackward()) {
this.webviewController.backward()
return true
}
return false
}
这样可以保证:
- H5 内部页面栈完整
- 用户体验符合直觉
- 不会误触返回直接退出页面
八、经验总结与注意事项
1️⃣ Web 封装不是功能堆砌
基础 Web 组件一定要:
- 克制参数
- 限制能力
- 默认安全
2️⃣ JS 交互要"笨一点"
- 明确字符串调用
- 明确参数顺序
- 明确回调结果
3️⃣ Web 配置越少越稳定
- mixedMode 不要默认开
- 缩放、缓存等行为统一管理
- 不允许业务层随意修改底层配置
九、总结
通过基于 @pura/harmony-web 的二次封装,我们可以:
- 将 Web 页面真正纳入 ArkUI 工程体系
- 提供稳定、可控、可维护的 Web 能力
- 避免项目后期 Web 页面失控
WebView 并不复杂,
复杂的是缺乏约束的使用方式。
当 Web 被当成"普通页面"来设计时,
很多问题,反而自然就消失了。