uni-app 中 Web-view 与 Vue 页面的通讯机制详解
一、Web-view 简介
Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面:
- 支持加载本地 HTML 文件
- 支持加载远程 HTML 页面
- 实现 Web 与原生的双向通讯
- 可用于嵌入第三方网页或 H5 应用
二、Web-view 加载 HTML 的方式
1. 加载本地 HTML 文件
<template>
<view>
<web-view :src="localHtmlPath"></web-view>
</view>
</template>
<script setup>
import { ref, onLoad } from 'vue';
const localHtmlPath = ref('');
onLoad(() => {
// 本地 HTML 文件路径
localHtmlPath.value = '/static/html/index.html';
});
</script>
2. 加载远程 HTML 页面
<template>
<view>
<web-view :src="remoteUrl"></web-view>
</view>
</template>
<script setup>
import { ref, onLoad } from 'vue';
const remoteUrl = ref('');
onLoad(() => {
// 远程 HTML 页面 URL
remoteUrl.value = 'https://www.example.com';
});
</script>
三、Web-view 与 Vue 页面的通讯方式
1. H5 页面向 uni-app 发送消息
在 H5 页面中:
// H5 页面 (index.html)
function sendMessageToUniApp() {
// 方式一:使用 postMessage (推荐)
window.uni.postMessage({
type: 'getData',
data: {
name: '张三',
age: 25
}
});
// 方式二:使用自定义 JSBridge (兼容旧版本)
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.uni) {
// iOS 平台
window.webkit.messageHandlers.uni.postMessage({
type: 'getData',
data: {
name: '张三',
age: 25
}
});
} else if (window.uni) {
// Android 平台
window.uni.postMessage(JSON.stringify({
type: 'getData',
data: {
name: '张三',
age: 25
}
}));
}
}
在 uni-app 页面中接收消息:
<template>
<view>
<web-view :src="htmlPath" @message="handleMessage"></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const htmlPath = ref('/static/html/index.html');
const handleMessage = (e) => {
const data = e.detail.data[0];
console.log('收到 H5 消息:', data);
// 根据消息类型处理
if (data.type === 'getData') {
// 处理数据
console.log('用户信息:', data.data);
}
};
</script>
2. uni-app 向 H5 页面发送消息
在 uni-app 页面中:
<template>
<view>
<button @click="sendToH5">向 H5 发送消息</button>
<web-view ref="webviewRef" :src="htmlPath"></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const webviewRef = ref(null);
const htmlPath = ref('/static/html/index.html');
const sendToH5 = () => {
// 向 H5 发送消息
webviewRef.value.postMessage({
type: 'setData',
data: {
title: '来自 uni-app 的数据',
content: '这是一条从 uni-app 发送到 H5 的消息'
}
});
};
</script>
在 H5 页面中接收消息:
// H5 页面 (index.html)
// 监听 uni-app 发送的消息
window.addEventListener('message', (e) => {
const data = e.data;
console.log('收到 uni-app 消息:', data);
// 根据消息类型处理
if (data.type === 'setData') {
// 更新页面内容
document.getElementById('title').innerText = data.data.title;
document.getElementById('content').innerText = data.data.content;
}
});
四、在 H5 页面中调用 uni-app API
1. 使用 postMessage 间接调用
在 H5 页面中:
// H5 页面 (index.html)
function callUniAppAPI() {
// 请求调用 uni-app API
window.uni.postMessage({
type: 'callAPI',
apiName: 'getLocation',
params: {
type: 'wgs84'
}
});
}
在 uni-app 页面中:
<template>
<view>
<web-view :src="htmlPath" @message="handleMessage"></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const htmlPath = ref('/static/html/index.html');
const webviewRef = ref(null);
const handleMessage = (e) => {
const data = e.detail.data[0];
// 处理 API 调用请求
if (data.type === 'callAPI') {
const { apiName, params } = data;
// 根据 API 名称调用相应的 uni-app API
if (apiName === 'getLocation') {
uni.getLocation({
...params,
success: (res) => {
// 将结果返回给 H5 页面
webviewRef.value.postMessage({
type: 'apiResult',
apiName,
success: true,
result: res
});
},
fail: (err) => {
// 将错误返回给 H5 页面
webviewRef.value.postMessage({
type: 'apiResult',
apiName,
success: false,
error: err
});
}
});
}
// 可以添加更多 API 的处理
}
};
</script>
在 H5 页面中接收 API 结果:
// H5 页面 (index.html)
window.addEventListener('message', (e) => {
const data = e.data;
// 处理 API 调用结果
if (data.type === 'apiResult') {
if (data.success) {
console.log('API 调用成功:', data.result);
// 更新页面显示
document.getElementById('location').innerText = `纬度: ${data.result.latitude}, 经度: ${data.result.longitude}`;
} else {
console.error('API 调用失败:', data.error);
// 显示错误信息
document.getElementById('location').innerText = '获取位置失败';
}
}
});
2. 使用 JSBridge 直接调用
在 uni-app 页面中注入 JSBridge:
<template>
<view>
<web-view ref="webviewRef" :src="htmlPath" @message="handleMessage"></web-view>
</view>
</template>
<script setup>
import { ref, onReady } from 'vue';
const webviewRef = ref(null);
const htmlPath = ref('/static/html/index.html');
onReady(() => {
// 注入 JSBridge
const jsStr = `
window.uniAPI = {
getLocation: function(callback) {
window.uni.postMessage({
type: 'callAPI',
apiName: 'getLocation',
callbackId: 'getLocation_' + Date.now()
});
// 存储回调函数
window.uniAPI._callbacks['getLocation_' + Date.now()] = callback;
},
_callbacks: {}
};
`;
// 执行 JS 代码注入 JSBridge
webviewRef.value.evalJs(jsStr);
});
const handleMessage = (e) => {
const data = e.detail.data[0];
// 处理 API 调用结果
if (data.type === 'apiResult') {
const callback = window.uniAPI._callbacks[data.callbackId];
if (callback) {
callback(data.success ? data.result : null, data.success ? null : data.error);
delete window.uniAPI._callbacks[data.callbackId];
}
}
};
</script>
在 H5 页面中使用 JSBridge:
// H5 页面 (index.html)
function getLocation() {
// 使用 JSBridge 调用 uni-app API
window.uniAPI.getLocation((result, error) => {
if (result) {
console.log('获取位置成功:', result);
document.getElementById('location').innerText = `纬度: ${result.latitude}, 经度: ${result.longitude}`;
} else {
console.error('获取位置失败:', error);
document.getElementById('location').innerText = '获取位置失败';
}
});
}
五、注意事项与最佳实践
跨域问题
- 加载远程 HTML 时需确保域名在白名单中
- 在
manifest.json
中配置h5
→domains
性能优化
- 避免频繁通讯
- 使用批量数据传输
- 对数据进行压缩处理
安全考虑
- 验证消息来源
- 对敏感操作进行权限控制
- 避免在 URL 中传递敏感信息
兼容性处理
- 处理不同平台的差异
- 提供降级方案
- 进行充分测试
调试技巧
- 使用 HBuilderX 的调试工具
- 在 H5 页面中添加日志输出
- 使用浏览器开发者工具调试
六、常见问题解决方案
消息接收不及时
- 确保 web-view 组件已加载完成
- 使用定时器重试机制
API 调用失败
- 检查 API 名称和参数是否正确
- 确认 API 权限是否已获取
- 处理 API 调用超时情况
内存泄漏
- 及时清理不再使用的回调函数
- 避免创建过多的临时对象
- 合理管理消息队列
性能问题
- 减少跨边界通讯次数
- 批量处理数据
- 对大数据进行分片传输
通过以上方法,您可以在 uni-app 中实现 Web-view 与 Vue 页面的高效通讯,为用户提供更丰富的交互体验。