
微信小程序使用 web-view 功能,实现内嵌 H5 项目
本文记录了一个 UniApp OA 项目接入微信小程序 web-view 的完整排查过程。项目原本只适配 App 和 H5,直接编译到小程序会出现大量样式问题,因此改用小程序 web-view 内嵌 H5。实现过程中遇到两个核心问题:小程序 NavBar 没有返回按钮,以及 H5 内调用 wx.navigateTo 找不到小程序页面。最终通过小程序中转页承载二次 web-view,并使用 uni.webView.navigateTo 避免 UniApp 内置 SDK 与微信 JSSDK 冲突,完成 H5 与小程序之间的跳转。
最近有个需求:把一个已经上线的 UniApp OA 项目接到微信小程序里。
这个 OA 项目原来主要跑在 App 和 H5 端,并没有认真适配过微信小程序。直接编译到小程序后,样式问题不少,很多页面都要重新调。考虑到项目已经在生产环境使用,如果把所有页面都按小程序再适配一遍,成本会比较高。
最后采用的方案是:小程序里用 web-view 直接内嵌 H5 页面。小程序只负责外壳和少量页面跳转,主要业务仍然走 H5。
这个方案本身不复杂,但实际接入时踩了两个坑。
问题一:web-view 页面没有返回按钮
一开始,我在 H5 里按原来的方式跳转:
uni.navigateTo({
url: url,
animationType: "slide-in-right",
});页面确实跳了,但小程序顶部 NavBar 没有返回上级页面的箭头。

后来发现,问题不在样式,也不是 web-view 不能返回,而是这个页面在小程序路由栈里的位置不对。
小程序顶部返回按钮出现的前提是:当前页面不是一级页面,而是通过小程序路由跳转进入的页面。也就是说,如果首页直接放一个 web-view,它本身就是一级页面,自然不会显示返回箭头。
解决思路:加一个小程序中转页
最终的路由设计是这样的:
- 小程序首页使用
web-view加载 H5 首页。 - H5 内部需要跳转时,不再只走 H5 自己的路由。
- H5 调用小程序跳转,进入一个小程序中转页。
- 中转页再次使用
web-view加载目标 H5 URL。
这样目标页面就不是小程序一级页面,而是由 navigateTo 进入的二级页面,NavBar 上会出现返回按钮。
在微信小程序里新增一个 pages/web/web 页面,专门作为中转页:

这个页面只做一件事:从 query 里读取 H5 URL,然后用 web-view 打开它。
问题二:H5 里调用 wx.navigateTo 找不到页面
一开始我在 H5 项目里全局引入微信 JSSDK:
<script type="text/javascript" src="/static/js/jweixin-1.3.2.js"></script>然后尝试跳到小程序中转页:
export const clickNavigateTo = (url: string = "/pages/index/index"): void => {
wx.navigateTo({ url: "/pages/web/web", animationType: "slide-in-right" });
};结果报错:找不到页面。

这个错误看起来很奇怪,因为小程序里确实已经创建了 pages/web/web。
真正的问题是:当前项目是 UniApp H5,不是普通 Vue H5。UniApp 自己也有一套 SDK,页面里的 wx.navigateTo 并不是我以为的微信 JSSDK 方法,而是被 UniApp 环境影响了。它会去 UniApp 的 pages 目录里找路由,而不是去微信小程序的 pages 里找路由,于是就出现了“页面不存在”。
UniApp 官方文档里也提到了 web-view 内部跳转冲突的问题:

解决方向是使用 uni.webView.navigateTo。

问题三:uni.webView 没有成功挂载
按文档改成 uni.webView.navigateTo 后,又遇到一个新问题:控制台里拿不到 webView 方法。
我打印了 uni 实例,发现里面没有 webView。再去看 uni.webview 的源码,源码里明明有这个方法。说明问题不是 API 不存在,而是导入后的对象和当前运行环境里的 uni 冲突了。
最后的处理方式是修改引入的 uni.webview 源码,避免它和 UniApp 自带的 SDK 命名冲突。

修改后再次打印,就可以看到 webView 方法了。

最终跳转代码
H5 侧可以封装一个方法:如果当前在微信小程序环境里,就走小程序中转页;否则继续走原来的 H5 路由。
export const isWxMiniprogram = (): boolean =>
window.__wxjs_environment === "miniprogram";
export const clickNavigateTo = (url: string = "/pages/index/index"): void => {
if (isWxMiniprogram()) {
uniCustom.webView.navigateTo({
url: `/pages/web/web?url=${encodeURIComponent(url)}`,
});
} else {
uni.navigateTo({
url,
animationType: "slide-in-right",
});
}
};这里有两个细节:
- 传给中转页的 H5 地址要用
encodeURIComponent处理。 - 小程序端的
/pages/web/web必须提前在小程序项目里配置好。
总结
这次问题的核心不是 web-view 难用,而是 H5、UniApp、小程序三套路由体系混在一起后,很容易误判当前调用的是哪一个环境里的 API。
最后稳定下来的方案是:
- 小程序首页承载 H5 首页。
- H5 需要进入新页面时,调用小程序中转页。
- 中转页再用
web-view打开目标 H5 URL。 - H5 里不要直接混用
wx.navigateTo,优先使用处理过冲突的uni.webView.navigateTo。
这样既保留了 H5 现有项目,又能让小程序返回按钮和页面栈行为符合用户预期。