很多业务的复杂页面(营销活动、长文档、可视化报表、旧系统)已经用 H5 写好,强行用小程序原生重写成本极高。微信的 web-view 组件允许在小程序页面里直接内嵌网页,为「原生壳 + H5 内容」的混合架构提供了通道。但 web-view 不是简单的 iframe:它有域名白名单、通信桥、分享与支付等能力的边界。本文从能力边界讲起,给出 JSBridge 通信、免登录、协同与安全加固的完整实战方案。
一、web-view 的能力与限制
1.1 能力边界
| 能力 | 支持情况 | 说明 |
|---|---|---|
| 内嵌 H5 页面 | 支持 | 需配置业务域名 |
| H5 与小程序双向通信 | 支持 | 通过 postMessage / wx.miniProgram |
| 微信支付 | 部分支持 | H5 中可调起原生支付需特定方案 |
| 分享给好友 | 支持 | 需通过小程序侧配置分享 |
| 跳转小程序其他页面 | 支持 | 通过 wx.miniProgram.navigateTo |
| 打开摄像头/相册 | 不支持 | H5 无法直接调用原生能力 |
| 定位 | 需授权 | 通过 H5 标准接口 + 用户授权 |
1.2 关键限制
- 业务域名白名单:web-view 加载的域名必须在微信公众平台「业务域名」中配置,且需要下载并放置校验文件,域名不能使用 IP 和端口。
- 每个小程序页面只能有一个 web-view,且 web-view 会覆盖同页面其他原生组件。
- 基础库限制:web-view 组件从基础库 1.6.4 开始支持,老基础库需做兼容判断。
- 个人主体小程序不能使用 web-view,仅企业/组织类目开放。
一句话:web-view 是「受控的浏览器」,能力以微信的域名白名单和接口授权为前提。
二、业务域名与页面加载
2.1 配置业务域名
在微信公众平台「开发管理 - 开发设置 - 业务域名」中配置,下载校验文件放到域名根目录:
配置流程:
1. 准备 HTTPS 域名(需有效证书,且非 IP、非端口)
2. 下载微信提供的校验文件 domain-check.txt
3. 将校验文件放到域名根目录,如 https://h5.example.com/domain-check.txt
4. 在公众平台添加域名并验证
5. 开发者工具中勾选「不校验合法域名」以便本地联调
2.2 在 WXML 中加载 H5
<!-- pages/h5/h5.wxml:内嵌 web-view -->
<web-view
src="https://h5.example.com/activity/2026"
bindmessage="onH5Message"
bindload="onH5Load"
binderror="onH5Error"
/>
// pages/h5/h5.js
Page({
onH5Load(e) {
console.log('web-view 加载成功', e);
},
onH5Error(e) {
// 域名未配置、证书异常、页面 404 都会触发
wx.showToast({ title: '页面加载失败', icon: 'none' });
}
});
2.3 参数透传
向 H5 传参可以通过 URL query,也可以借助小程序的全局数据。注意避免在 URL 里放敏感信息:
// 拼接 URL 参数
const token = wx.getStorageSync('access_token');
Page({
data: {
h5Src: ''
},
onLoad(options) {
const target = options.target || 'activity/2026';
// 只传业务标识,登录态通过 JSBridge 传递
this.setData({
h5Src: 'https://h5.example.com/' + target + '?channel=miniprogram'
});
}
});
三、JSBridge 通信机制
3.1 通信模型
H5 与小程序通过微信注入的 wx.miniProgram 对象通信,消息是单向异步的,需要约定消息协议:
H5 → 小程序:wx.miniProgram.postMessage(数据)
↓ 小程序侧通过 bindmessage 事件接收
小程序 → H5:web-view 的 src 变更 / wx.miniProgram 无法直接推消息
注意:postMessage 的消息只在特定时机(页面返回、分享、组件销毁等)才会触达小程序,不是实时通道。需要实时通信时应由 H5 主动轮询自己的后端。
3.2 H5 侧注入代码
// H5 侧:判断是否运行在小程序中
function isInWechatMiniProgram() {
return !!(
window.__wxjs_environment === 'miniprogram' ||
window.wx?.miniProgram
);
}
// H5 向小程序发送消息
function sendToMiniProgram(payload) {
if (!isInWechatMiniProgram()) return;
window.wx.miniProgram.postMessage({
type: 'H5_EVENT',
data: payload
});
}
3.3 小程序侧接收消息
// pages/h5/h5.js 接收 H5 消息
Page({
onH5Message(e) {
const detail = e.detail || {};
const data = Array.isArray(detail.data) ? detail.data[0] : detail.data;
if (!data) return;
switch (data.type) {
case 'H5_EVENT':
// 例如 H5 上报「点击领券」,小程序侧拉起对应逻辑
this.handleH5Event(data.data);
break;
default:
break;
}
},
handleH5Event(payload) {
wx.showToast({ title: '收到 H5 事件', icon: 'none' });
}
});
一句话:JSBridge 是消息总线,两端都要按统一的 type/data 协议编解码,才不会变成「鸡同鸭讲」。
四、登录态与免登方案
4.1 免登的难点
H5 页面运行在 web-view 中,无法直接使用小程序的 wx.login 换取 code。免登的常见做法是:小程序侧先登录拿到 token,再通过 JSBridge 或 URL 传给 H5,H5 用该 token 请求自己的后端。
4.2 令牌传递方案对比
| 方案 | 做法 | 安全性 | 适用 |
|---|---|---|---|
| URL 参数 | token 放在 query | 低,会进日志 | 内部低敏页面 |
| postMessage | H5 加载完成后主动索取 | 中,需防重放 | 常规业务 |
| 服务端会话 | H5 后端用 code 换 openid | 高 | 对安全性要求高 |
4.3 基于签名票据的免登
推荐做法:小程序侧向自己的服务端换取一张短期签名票据,H5 拿到票据后向 H5 后端换取 H5 侧会话,避免在 URL 中暴露长期 token:
// 小程序侧:获取票据并通过 postMessage 传给 H5
async function fetchTicket() {
const res = await wx.request({
url: 'https://api.example.com/auth/ticket',
method: 'POST',
data: { openid: getOpenid() }
});
return res.data.ticket; // 例如 JWT,有效期 5 分钟
}
// H5 侧:加载完成后向小程序索取票据
window.onload = function () {
if (isInWechatMiniProgram()) {
window.wx.miniProgram.postMessage({
type: 'H5_NEED_TICKET',
data: { page: location.pathname }
});
}
};
4.4 登录态失效处理
双方都要处理 token 过期:H5 请求返回 401 时,应提示用户并通过 JSBridge 通知小程序重新登录:
// H5 请求拦截器中的 401 处理
function handleUnauthorized() {
// 通知小程序侧刷新登录态
if (isInWechatMiniProgram()) {
window.wx.miniProgram.postMessage({
type: 'H5_AUTH_EXPIRED'
});
} else {
window.location.href = '/login?redirect=' + encodeURIComponent(location.href);
}
}
五、与小程序组件的协同
5.1 什么场景用 H5,什么场景用原生
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 营销活动页 | H5 | 改版频繁、运营可自行维护 |
| 复杂表单/长流程 | 原生 | 键盘、滚动、交互体验更好 |
| 图表/可视化报表 | H5 | 生态丰富、可复用前端资源 |
| 支付/授权关键链路 | 原生 | 依赖小程序能力、合规更稳 |
| 老旧系统迁移 | H5 | 复用存量资产、成本低 |
5.2 混合导航与分享
H5 页面没有小程序原生的导航栏,需要小程序侧配置导航与分享:
// pages/h5/h5.js:配置标题、分享、返回
Page({
onLoad(options) {
wx.setNavigationBarTitle({
title: decodeURIComponent(options.title || '活动详情')
});
},
onShareAppMessage() {
return {
title: '快来参与活动',
path: '/pages/h5/h5?target=activity/2026&title=' + encodeURIComponent('活动详情')
};
}
});
5.3 原生能力兜底
H5 无法直接调起的原生能力(定位、保存相册、订阅消息),可以通过 JSBridge 让小程序侧代为执行:
// 小程序侧接收 H5 请求并调用原生能力
Page({
onH5Message(e) {
const data = e.detail?.data?.[0];
if (data.type === 'H5_SAVE_IMAGE') {
wx.saveImageToPhotosAlbum({
filePath: data.data.url,
success: () => {
// 结果通过 web-view 变更 src 或下一条消息回传
this.sendToH5({ type: 'SAVE_IMAGE_RESULT', ok: true });
}
});
}
}
});
六、性能与安全加固
6.1 性能优化
| 问题 | 优化手段 |
|---|---|
| 首屏慢 | H5 走 CDN、资源预加载、骨架屏 |
| 加载白屏 | 小程序侧加 loading 提示,监听 bindload |
| 与原生组件层级冲突 | 避免在 web-view 同页堆叠原生组件 |
| 内存占用高 | 控制 H5 页面数量,及时关闭 web-view 页面 |
6.2 内容安全
web-view 页面同样受微信内容安全规范约束,需做好:
- 业务域名校验:H5 页面禁止加载非白名单域名的资源(JS/图片),防止被注入。
- 敏感信息防护:URL 与 postMessage 均不传递明文手机号、身份证等。
- 防重放与防篡改:签名票据设置有效期并绑定 openid 与场景。
- H5 内容审核:营销活动页需符合平台内容规范,禁止诱导分享、虚假信息。
6.3 请求域名与 WebView 白名单区分
| 白名单类型 | 用途 | 位置 |
|---|---|---|
| 业务域名 | web-view 内嵌的 H5 域名 | 公众平台「业务域名」 |
| request 合法域名 | 小程序 wx.request 的请求域名 | 公众平台「request 合法域名」 |
| 服务器域名 | 上传/下载文件域名 | 公众平台「uploadFile/downloadFile」 |
三者互相独立,H5 内部的接口请求走的是 H5 自己后端的域名,不受小程序 request 白名单限制,但 H5 域名本身必须在业务域名内。
一句话:安全的核心是「最小暴露」——票据短时效、消息不传敏感数据、域名严格白名单。
七、总结
web-view 是小程序补齐复杂业务场景的「万金油」,但也是一把需要约束的刀:域名白名单、JSBridge 协议、免登令牌、能力边界四件事必须一开始就设计清楚。混合开发的原则是「各取所长」——高频交互与关键链路用原生保障体验,低频复杂页面用 H5 保证迭代速度,两边通过统一的消息协议协同。配合小程序登录鉴权打通免登体系,结合安全合规守住内容与隐私底线,再借助性能优化让 H5 页面在小程序容器里也能流畅运行,混合架构才能真正成为业务的助推器而不是隐患。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。