HarmonyOS鸿蒙Next应用中如何使用WebView加载网页?
HarmonyOS鸿蒙Next应用中如何使用WebView加载网页?
在HarmonyOS应用中如何使用WebView加载网页?
如何实现Web与原生的交互、拦截网络请求和处理页面事件?
4 回复
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebPage {
controller: webview.WebviewController = new webview.WebviewController();
@State webUrl: string = 'https://example.com'; // 替换为目标网址
build() {
Column() {
Web({
src: this.webUrl,
controller: this.controller
})
.width('100%')
.height('100%')
}
}
}
web加载页面的大概是类似上述这种代码,具体的实践可以参考ArkWeb简介中使用Web组件加载页面这个章节的内容,其他跟web相关的可以参考ArkWeb简介中其他对应的章节。
更多关于HarmonyOS鸿蒙Next应用中如何使用WebView加载网页?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
解决方案
1. 基础WebView使用
import web_webview from '@ohos.web.webview'
@Entry
@Component
struct BasicWebView {
controller: web_webview.WebviewController = new web_webview.WebviewController()
build() {
Column() {
// 导航栏
Row() {
Button('后退')
.enabled(this.controller.accessBackward())
.onClick(() => {
this.controller.backward()
})
Button('前进')
.enabled(this.controller.accessForward())
.onClick(() => {
this.controller.forward()
})
Button('刷新')
.onClick(() => {
this.controller.refresh()
})
}
.width('100%')
.padding(12)
.justifyContent(FlexAlign.SpaceAround)
// WebView
Web({
src: 'https://www.example.com',
controller: this.controller
})
.width('100%')
.layoutWeight(1)
.onPageBegin((event) => {
console.log('页面开始加载:', event?.url)
})
.onPageEnd((event) => {
console.log('页面加载完成:', event?.url)
})
.onProgressChange((event) => {
console.log('加载进度:', event?.newProgress)
})
.onErrorReceive((event) => {
console.error('加载错误:', event?.error.getErrorInfo())
})
}
}
}
2. 加载本地HTML
@Entry
@Component
struct LocalWebView {
controller: web_webview.WebviewController = new web_webview.WebviewController()
@State htmlContent: string = `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: sans-serif;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.container {
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 12px;
}
button {
background: white;
color: #667eea;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-size: 16px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>本地HTML页面</h1>
<p>这是通过loadData加载的本地HTML内容</p>
<button onclick="handleClick()">点击我</button>
</div>
<script>
function handleClick() {
alert('按钮被点击了!');
}
</script>
</body>
</html>
`
build() {
Column() {
Web({
src: '',
controller: this.controller
})
.width('100%')
.height('100%')
.onControllerAttached(() => {
this.controller.loadData(this.htmlContent, 'text/html', 'UTF-8')
})
}
}
}
3. Web与Native交互
// 定义交互对象
class WebBridge {
// 供Web调用的方法
[@JavaScriptProxy](/user/JavaScriptProxy)
showToast(message: string) {
console.log('Web调用Native:', message)
// 可以调用原生Toast等
}
[@JavaScriptProxy](/user/JavaScriptProxy)
getUserInfo(): string {
return JSON.stringify({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
})
}
[@JavaScriptProxy](/user/JavaScriptProxy)
openNativePage(page: string) {
console.log('打开原生页面:', page)
// 可以调用路由跳转
}
}
@Entry
@Component
struct WebNativeInteraction {
controller: web_webview.WebviewController = new web_webview.WebviewController()
bridge: WebBridge = new WebBridge()
build() {
Column() {
Button('调用Web方法')
.onClick(() => {
// Native调用Web方法
this.controller.runJavaScript('webFunction("来自Native的消息")')
})
.margin(16)
Web({
src: '',
controller: this.controller
})
.width('100%')
.layoutWeight(1)
.javaScriptAccess(true) // 启用JavaScript
.javaScriptProxy({
object: this.bridge,
name: 'nativeBridge', // Web中通过window.nativeBridge访问
methodList: ['showToast', 'getUserInfo', 'openNativePage'],
controller: this.controller
})
.onControllerAttached(() => {
this.loadInteractivePage()
})
}
}
private loadInteractivePage() {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { padding: 20px; font-family: sans-serif; }
button {
width: 100%;
padding: 12px;
margin: 8px 0;
background: #1890ff;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
}
#result {
margin-top: 20px;
padding: 12px;
background: #f5f5f5;
border-radius: 6px;
}
</style>
</head>
<body>
<h2>Web与Native交互</h2>
<button onclick="callShowToast()">调用Native显示Toast</button>
<button onclick="callGetUserInfo()">获取用户信息</button>
<button onclick="callOpenPage()">打开原生页面</button>
<div id="result"></div>
<script>
// Web调用Native方法
function callShowToast() {
window.nativeBridge.showToast('来自Web的消息');
}
function callGetUserInfo() {
const userInfo = window.nativeBridge.getUserInfo();
document.getElementById('result').innerText =
'用户信息: ' + userInfo;
}
function callOpenPage() {
window.nativeBridge.openNativePage('DetailPage');
}
// 供Native调用的方法
function webFunction(message) {
document.getElementById('result').innerText =
'Native消息: ' + message;
}
</script>
</body>
</html>
`
this.controller.loadData(html, 'text/html', 'UTF-8')
}
}
4. 拦截网络请求
@Entry
@Component
struct WebViewWithInterceptor {
controller: web_webview.WebviewController = new web_webview.WebviewController()
build() {
Web({
src: 'https://www.example.com',
controller: this.controller
})
.width('100%')
.height('100%')
.onInterceptRequest((event) => {
// 拦截请求
const url = event?.request.getRequestUrl()
console.log('拦截请求:', url)
// 可以修改请求或返回本地资源
if (url?.includes('custom-resource')) {
// 返回本地资源
return {
data: '<html><body>自定义内容</body></html>',
encoding: 'utf-8',
mimeType: 'text/html',
reason: 'OK',
statusCode: 200
}
}
return null // 不拦截,继续请求
})
.onUrlLoadIntercept((event) => {
// URL加载拦截
const url = event?.data.toString()
console.log('URL加载:', url)
// 拦截特定协议
if (url?.startsWith('myapp://')) {
console.log('拦截自定义协议')
// 处理自定义协议
return true // 返回true表示拦截
}
return false // 不拦截
})
}
}
5. 完整WebView示例
import web_webview from '@ohos.web.webview'
@Entry
@Component
struct CompleteWebView {
controller: web_webview.WebviewController = new web_webview.WebviewController()
@State pageTitle: string = ''
@State pageUrl: string = ''
@State progress: number = 0
@State canGoBack: boolean = false
@State canGoForward: boolean = false
@State isLoading: boolean = false
build() {
Column() {
// 顶部工具栏
Row() {
Text(this.pageTitle || '加载中...')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
if (this.isLoading) {
LoadingProgress()
.width(20)
.height(20)
}
}
.width('100%')
.padding(12)
.backgroundColor('#f5f5f5')
// 地址栏
Row() {
Text(this.pageUrl)
.fontSize(14)
.fontColor('#666666')
.layoutWeight(1)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.padding({ left: 12, right: 12, top: 4, bottom: 4 })
.backgroundColor('#ffffff')
// 进度条
if (this.progress > 0 && this.progress < 100) {
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width('100%')
.color('#1890ff')
.height(2)
}
// WebView
Web({
src: 'https://www.harmonyos.com',
controller: this.controller
})
.width('100%')
.layoutWeight(1)
.javaScriptAccess(true)
.domStorageAccess(true)
.onPageBegin((event) => {
this.isLoading = true
this.pageUrl = event?.url || ''
})
.onPageEnd((event) => {
this.isLoading = false
this.pageTitle = this.controller.getTitle()
this.updateNavigationState()
})
.onProgressChange((event) => {
this.progress = event?.newProgress || 0
})
.onTitleReceive((event) => {
this.pageTitle = event?.title || ''
})
// 底部导航栏
Row() {
Button() {
Image($r('sys.media.ohos_ic_public_arrow_left'))
.width(24)
.height(24)
}
.type(ButtonType.Normal)
.backgroundColor(Color.Transparent)
.enabled(this.canGoBack)
.opacity(this.canGoBack ? 1 : 0.3)
.onClick(() => {
this.controller.backward()
})
Button() {
Image($r('sys.media.ohos_ic_public_arrow_right'))
.width(24)
.height(24)
}
.type(ButtonType.Normal)
.backgroundColor(Color.Transparent)
.enabled(this.canGoForward)
.opacity(this.canGoForward ? 1 : 0.3)
.onClick(() => {
this.controller.forward()
})
Button() {
Image($r('sys.media.ohos_ic_public_refresh'))
.width(24)
.height(24)
}
.type(ButtonType.Normal)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.controller.refresh()
})
Button() {
Image($r('sys.media.ohos_ic_public_cancel'))
.width(24)
.height(24)
}
.type(ButtonType.Normal)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.controller.stop()
})
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.SpaceAround)
.backgroundColor('#f5f5f5')
}
}
private updateNavigationState() {
this.canGoBack = this.controller.accessBackward()
this.canGoForward = this.controller.accessForward()
}
}
关键要点
- WebviewController: 控制WebView的加载、导航等行为
- JavaScript交互: 通过@JavaScriptProxy实现双向通信
- 请求拦截: onInterceptRequest可拦截和修改网络请求
- 生命周期: onPageBegin、onPageEnd、onProgressChange等事件
- 安全性: 谨慎开启javaScriptAccess,验证来源
最佳实践
- 性能优化: 启用缓存、压缩传输数据
- 错误处理: 监听onErrorReceive处理加载失败
- 内存管理: 及时清理不用的WebView
- 安全通信: 验证Web调用来源,过滤敏感数据
- 用户体验: 显示加载进度,提供刷新重试
在HarmonyOS Next应用中,使用WebView加载网页需通过ArkUI框架实现。首先导入Web组件模块,然后在UI中声明Web组件并设置初始加载的网页地址。可通过loadUrl方法动态加载指定URL,同时支持配置JavaScript启用、文件访问等参数以控制网页行为。
在HarmonyOS Next应用中,使用WebView加载网页主要涉及@ohos.web.webview模块。以下是核心实现方法:
1. 加载网页
在ArkUI(声明式开发范式)中,使用Web组件:
import webview from '@ohos.web.webview';
@Entry
@Component
struct WebPage {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
// 加载在线网页
Web({ src: 'https://developer.harmonyos.com/', controller: this.controller })
.width('100%')
.height('100%')
// 或加载本地HTML文件
// Web({ src: $rawfile('index.html'), controller: this.controller })
}
}
}
2. 实现Web与原生交互
2.1 JavaScript调用原生方法
注册JavaScript接口:
// 原生侧注册对象
this.controller.registerJavaScriptProxy({
// 定义供JS调用的方法
showToast: (msg: string) => {
prompt.showToast({ message: msg });
},
getDeviceInfo: () => {
return { model: 'HarmonyOS Device' };
}
}, 'nativeBridge'); // 注入到window.nativeBridge
// 在Web页面JavaScript中调用:
// window.nativeBridge.showToast('Hello from JS');
// const info = window.nativeBridge.getDeviceInfo();
2.2 原生调用JavaScript方法
// 执行JS代码
this.controller.runJavaScript('alert("来自原生的调用")');
// 调用JS函数并获取返回值
this.controller.runJavaScript('getUserToken()', (err, result) => {
if (!err) {
console.log('JS返回值:', result);
}
});
3. 拦截网络请求
通过onInterceptRequest回调拦截和修改请求:
this.controller.on('onInterceptRequest', (event) => {
// 拦截特定URL
if (event.request.url.indexOf('blocked-domain.com') !== -1) {
// 阻止请求
return { intercept: true };
}
// 修改请求头
event.request.header = {
...event.request.header,
'Custom-Header': 'HarmonyOS'
};
// 允许请求继续
return { intercept: false };
});
4. 处理页面事件
4.1 页面加载事件
this.controller.on('pageBegin', (event) => {
console.log('开始加载:', event.url);
});
this.controller.on('pageEnd', (event) => {
console.log('加载完成:', event.url);
});
this.controller.on('pageError', (error) => {
console.error('加载失败:', error);
});
4.2 其他常用事件
// 进度变化
this.controller.on('progressChange', (progress) => {
console.log('加载进度:', progress);
});
// 标题变化
this.controller.on('titleReceive', (title) => {
console.log('页面标题:', title);
});
// JS对话框
this.controller.on('alert', (event) => {
prompt.showDialog({ message: event.message });
event.result.handleConfirm(); // 确认对话框
});
5. 完整示例
import webview from '@ohos.web.webview';
@Entry
@Component
struct FullWebViewExample {
controller: webview.WebviewController = new webview.WebviewController();
aboutToAppear() {
// 注册JS接口
this.controller.registerJavaScriptProxy({
nativeMethod: (data: string) => {
console.log('收到JS数据:', data);
}
}, 'harmonyBridge');
// 设置事件监听
this.controller.on('pageEnd', () => {
// 页面加载完成后注入CSS
this.controller.runJavaScript(`
document.body.style.backgroundColor = '#f5f5f5';
`);
});
// 拦截请求
this.controller.on('onInterceptRequest', (event) => {
console.log('请求URL:', event.request.url);
return { intercept: false };
});
}
build() {
Column() {
Web({ src: 'https://example.com', controller: this.controller })
.width('100%')
.height('100%')
.onPageEnd(() => {
console.log('页面加载完成回调');
})
}
}
}
关键配置
在module.json5中配置网络权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
通过以上方法,可以实现在HarmonyOS Next应用中完整的WebView功能,包括网页加载、双向通信、请求拦截和事件处理。注意Web组件需要合理管理生命周期,避免内存泄漏。

