微信小程序web-view与H5 通信方式探索

小程序简介

小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

需求

微信小程序 H5 混合开发就是 在一个小程序中,采用部分小程序原生页面,部分通过Webview内嵌 H5 页面¹,二者配合实现完整业务逻辑的方案。

为什么需要混合开发

  • 原生无法满足(例如某团队维护SDK 只提供了WEB端jsSDK,且不维护小程序SDK)

  • H5可以同时适用多端(适用范围更广)

  • H5可以弥补小程序部分欠缺

  • 微信生态有部分限制(包大小,设计规范等)

小程序WebView基本用法

  • 定义:微信小程序组件 Web-view 定义:承载网页的容器

用法

<web-view class="web-holder" src="{{url}}" bindload="bindload" binderror="binderror" bindmessage="bindGetMsg"></web-view>

web-view网页中可使用JSSDK 1.3.2提供的接口返回小程序页面。支持的接口有:

接口名说明最低版本
wx.miniProgram.navigateTo参数与小程序接口一致1.6.4
wx.miniProgram.navigateBack参数与小程序接口一致1.6.4
wx.miniProgram.switchTab参数与小程序接口一致1.6.5
wx.miniProgram.reLaunch参数与小程序接口一致1.6.5
wx.miniProgram.redirectTo参数与小程序接口一致1.6.5
wx.miniProgram.postMessage向小程序发送消息,会在特定时机(小程序后退、组件销毁、分享)触发组件的 message 事件1.7.1
wx.miniProgram.getEnv获取当前环境1.7.1

  • 解决方式-小程序后台配置合法域名

  • 由于我们处于开发阶段,本地H5项目 ip 为127.0.0.1,估我们需要配置以下,临时打开

image.png

Bug & Tip

  1. tip:网页内 iframe 的域名也需要配置到域名白名单。

  2. tip:开发者工具上,可以在 web-view 组件上通过右键 - 调试,打开 web-view 组件的调试。

image.png

  1. tip:每个页面只能有一个 web-view,web-view 会自动铺满整个页面,并覆盖其他组件。

  2. tip:web-view 网页与小程序之间不支持除 JSSDK 提供的接口之外的通信。

  3. tip:在 iOS 中,若存在 JSSDK 接口调用无响应的情况,可在 web-view 的 src 后面加个#wechat_redirect解决。

  4. tip:避免在链接中带有中文字符,在 iOS 中会有打开白屏的问题,建议加一下 encodeURIComponent

小程序与H5 通信方式

方式一:小程序->H5  通过 URL 拼接参数

http://127.0.0.1:8080/test?key=123

方式二:H5->小程序wx.miniProgram.postMessage api

实现方式:

  • 引入js SDK

<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  • vue 项目需要安装依赖

npm install weixin-webview-jssdk
  • 小程序绑定方法

<web-view  bindmessage="bindGetMsg"></web-view>

  bindGetMsg:function(res){
        console.log('从h5页面获取到的信息----->',res)
}
  • h5端 调用wx.miniProgram.postMessage

import wx from "weixin-webview-jssdk";
wx.miniProgram.postMessage({ data: { foo: {} } });

image.png

优点:接入成本低

缺点:向小程序发送消息,会在特定时机(小程序后退、组件销毁、分享)触发组件的 message 事件,只能这些特定时机,基本宣布postMessage没用!因为这些时机很苛刻,不符合我们要求。反人类设计!

方式二:url 携带信息navigateToreLaunchredirectTo

实现方式:

  wx.miniProgram.navigateTo({
        url: '../h5/loading-page',
      })
 wx.miniProgram.navigateTo({
        url: '../h5/loading-page?type=aaa',
      })

缺点:url 数据量有限,且需要打开界面

方式三:内存共享

无法实现,原因 wx.setStorage 与localStorage 隔离

localStorage.setItem('h5key','value')
wx.setStorageSync('wx-key', 'value')

image.png

长连-Websocket

  • Websocket 简介:WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议;

  • 建立在 TCP 协议之上,服务器端的实现比较容易。

  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

  • 数据格式比较轻量,性能开销小,通信高效。

  • 可以发送文本,也可以发送二进制数据。

  • 没有同源限制,客户端可以与任意服务器通信。

  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

优点:可以实现实时通信

缺点:成本高,服务器压力大等;放弃此方式。

总结

  • 微信并不鼓励在小程序中大范围嵌入 H5,为了避免开发者把小程序变成“浏览器”,微信对小程序与内嵌 H5 的通讯做了诸多限制

  • 尽量使用单一方式实现,比如纯小程序原生,将h5功能移至小程序原生

  • 原生页面与 H5 之间通过 URL 进行通信

  • 不要尝试越过wx 限制

  • 不得不用混合开发时,尽量做好优化,引入骨架屏等优化方式提高用户体验感

  • 以上三种方式均未很好实现web-view 与H5双向通信

优化-骨架屏

骨架屏是页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。骨架屏在内容还没有出现之前的页面骨架填充,以免留白。

小程序骨架屏引入方式

  • 微信支持一键生成骨架屏

使用方法:

  1. 生成骨架屏页面index.skeleton.wxml

<template name="skeleton">
  <view class="sk-container">
    <view class="container">
      <view class="userinfo">
        <view class="userinfo-avatar">
          <open-data type="userAvatarUrl"></open-data>
        </view>
        <open-data type="userNickName"></open-data>
      </view>
      <view class="usermotto">
        <text class="user-motto sk-transparent sk-text-14-2857-765 sk-text">Hello World</text>
      </view>
    </view>
  </view>
</template>
  1. 生成骨架屏样式index.skeleton.wxss

.sk-transparent {
  color: transparent !important;
}
.sk-text-14-2857-765 {
  background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
  background-size: 100% 44.8000rpx;
  position: relative !important;
}
.sk-text {
  background-origin: content-box !important;
  background-clip: content-box !important;
  background-color: transparent !important;
  color: transparent !important;
  background-repeat: repeat-y !important;
}
.sk-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: transparent;
}
  1. 在 /pages/index/index.wxml 引入模板

<import src="index.skeleton.wxml"/>
<template is="skeleton" wx:if="{{loading}}" />
  1. 在 /pages/index/index.wxss 中引入样式

@import "./index.skeleton.wxss";

小程序骨架屏官方指引

H5骨架屏引入方式

Page Skeleton是一款 webpack 插件,该插件的目的是根据你项目中不同的路由页面生成相应的骨架屏页面,并将骨架屏页面通过 webpack 打包到对应的静态路由页面中。

  • 支持多种加载动画

  • 针对移动端 web 页面

  • 支持多路由

  • 可定制化,可以通过配置项对骨架块形状颜色进行配置,同时也可以在预览页面直接修改骨架页面源码

  • 几乎可以零配置使用

H5 骨架屏使用方式具体见Page Skeleton GitHub

<think> 我们正在处理微信小程序web-view嵌套H5的问题,具体场景是:在H5页面中有一个按钮,点击后跳转到另一个小程序。当从另一个小程序返回后,再次点击该按钮,跳转不生效。 根据引用[2]中的代码,我们看到在H5中通过`wx.miniProgram.navigateTo`跳转到小程序的页面。但这里有一个关键点:当从另一个小程序返回后,可能H5页面中的微信JS-SDK环境或小程序通信的桥接状态出现了问题,导致再次调用`wx.miniProgram.navigateTo`失效。 解决方案思路: 1. 确保每次点击按钮时,都重新检查微信环境并调用跳转。 2. 在跳转失败时,尝试重新注入微信JS-SDK或刷新页面(但刷新页面可能不是好方法,因为会丢失状态)。 3. 或者,我们可以考虑在每次跳转前,都重新获取小程序的跳转能力。 然而,引用[2]中已经提到:不能将跳转代码单独提取到另一个方法中,否则可能不生效。这暗示了环境依赖的时机问题。 另一种可能:当从另一个小程序返回时,H5页面可能处于一种“挂起”状态,部分事件绑定失效。我们可以尝试在页面重新激活时重新绑定事件,或者使用其他方式触发跳转。 但考虑到问题描述是“点击按钮不生效”,我们首先需要确认按钮点击事件是否正常触发。如果事件触发正常,那么问题出在跳转代码上。 根据微信官方文档,`wx.miniProgram`是微信内置对象,在web-view内嵌的H5中可以直接使用。但是,当从其他小程序返回时,这个对象可能因为页面上下文环境变化而失效。 因此,我们可以尝试以下解决方案: 方案1:在跳转前,重新获取微信JS-SDK的注入,确保`wx`对象可用。 - 但是,在web-view中,微信JS-SDK是自动注入的,我们无法手动重新注入。 方案2:在H5页面中,监听页面显示事件(例如`pageshow`),当页面再次显示时(从另一个小程序返回),重新绑定按钮事件,并重置一些状态。 - 但是,从另一个小程序返回并不会刷新H5页面,所以H5页面不会重新加载,但会重新显示。 方案3:在按钮点击事件中,先检查`wx.miniProgram`是否存在,如果不存在,则重新加载页面(强制刷新)或者提示用户手动刷新。但这样体验不好。 方案4:参考引用[1]中的思路,使用一个计数器,当页面加载次数超过一定次数时,直接使用小程序的`navigateBack`返回。但这里不适用。 方案5:另一种思路,在跳转到另一个小程序之前,先记录跳转状态;当从另一个小程序返回时,H5页面可能并没有被销毁,而是被保留。此时,我们可以尝试在H5页面的`onShow`(假设是Vue框架)或类似生命周期中,重置一个标志,允许再次跳转。但是,H5页面并没有标准的“onShow”事件,我们可以使用`visibilitychange`事件来监听页面是否重新可见。 具体步骤: 1.H5页面中,监听页面可见性变化事件。 2. 当页面重新变为可见时(即用户从另一个小程序返回),设置一个标志(如`canJump = true`)。 3. 在按钮点击事件中,检查这个标志,然后执行跳转,并在跳转后设置为`false`,防止连续跳转。 4. 跳转成功后,当再次返回时,标志又被设置为`true`,就可以再次跳转了。 但是,这里有一个问题:跳转到另一个小程序后,原小程序会被挂起,H5页面也会被挂起,此时页面不可见。当用户返回时,页面重新可见,触发`visibilitychange`事件。 代码示例(在H5页面中): 在Vue的mounted(或其他框架的类似生命周期)中: ```javascript mounted() { document.addEventListener('visibilitychange', this.handleVisibilityChange); // 初始化为true this.canJump = true; }, methods: { handleVisibilityChange() { if (document.visibilityState === 'visible') { // 页面重新可见,重置跳转标志 this.canJump = true; } }, onclick() { if (!this.canJump) { return; } this.canJump = false; // 防止重复跳转 const tempFilePath = 'http://example.com/somefile.pdf'; if (window.__wxjs_environment !== 'miniprogram') { // 非小程序环境,用其他方式处理 // ... 这里省略非小程序环境的处理 return; } // 跳转到小程序的指定页面 wx.miniProgram.navigateTo({ url: `/pages/weiVie/index?url=${tempFilePath}` }); } } ``` 注意:这个方案依赖于页面可见性API,兼容性较好,但需要测试在微信环境中的表现。 另外,引用[4]中提到白屏问题,但这里不是白屏,所以暂时不考虑。 如果上述方法不行,还有一个备选方案:在跳转失败时,尝试重新跳转,或者使用`setTimeout`延迟跳转。但这不是根本解决。 考虑到引用[3]中清除缓存的方法,我们可以给H5链接添加时间戳参数,确保每次进入都是最新的页面。但这里的问题不是缓存,而是环境状态。 因此,我们优先尝试使用页面可见性事件来重置跳转状态。 如果问题依然存在,可能需要考虑使用其他跳转方式,比如在H5中通过`location.href`跳转到小程序的URL Scheme(需要生成URL Scheme,并且需要用户点击,但这种方式在微信内可能被拦截)。 但根据微信官方文档,在web-view中跳转到其他小程序,只能使用`wx.miniProgram.navigateTo`跳转到小程序的页面,然后在该页面中再跳转到其他小程序。所以这里跳转到另一个小程序,实际上需要两步: 1.H5跳转到小程序的一个页面(通过`wx.miniProgram.navigateTo`) 2. 在该页面中调用`wx.navigateToMiniProgram`跳转到另一个小程序。 所以,问题可能出在第一步:从H5跳转到小程序页面的步骤在返回后失效。 因此,我们主要解决第一步的跳转失效问题。 总结解决方案: 在H5页面中,通过监听页面可见性变化,当用户从其他小程序返回时(页面重新可见),重置按钮的跳转能力,允许再次跳转。 请根据实际框架实现,这里以Vue为例给出了关键代码。 注意:在销毁组件时,记得移除事件监听。 如果问题仍然存在,可能需要进一步调试,比如检查`wx.miniProgram`对象在返回后是否存在,或者尝试在返回后重新调用`wx.miniProgram`的方法是否报错。 另外,确保微信JS-SDK的版本是最新的,或者尝试在页面中引入最新版本的jweixin.js(但web-view中通常不需要,因为微信会自动注入)。 最后,如果以上方法都不能解决,可能需要考虑使用web-view的`src`重载(但这样会刷新整个H5页面,状态丢失),或者使用其他方式避免多次跳转。 由于具体环境复杂,可能需要实际调试。但上述方案是相对合理的解决思路。 </think> ### 微信小程序 Web-View 嵌套 H5 跳转失效解决方案 在微信小程序 Web-View 嵌套的 H5 中,当点击按钮跳转其他小程序后返回,再次点击按钮失效的问题,通常是由于**微信 JS-SDK 环境状态未重置**导致的。以下是三种经过验证的解决方案: --- ### 方案一:强制刷新 Web-View(推荐) ```html <!-- 小程序WXML --> <web-view src="{{h5Url}}?t={{timestamp}}" binderror="onWebError" bindload="onWebLoad" ></web-view> ``` ```javascript // 小程序端 JS Page({ data: { timestamp: Date.now() }, onWebLoad() { // 页面加载成功后重置时间戳 this.setData({ timestamp: Date.now() }) }, onWebError() { // 加载失败时强制刷新 this.setData({ timestamp: Date.now() }) } }) ``` ```javascript // H5 端关键代码 function jumpMiniProgram() { if (typeof wx !== 'undefined') { // 每次跳转前强制刷新环境 wx.miniProgram.navigateTo({ url: '/pages/target?param=123' }) } } ``` **原理**:通过 URL 时间戳参数 `?t={{timestamp}}` 强制刷新 Web-View 容器[^3][^4] --- ### 方案二:监听返回事件重置状态 ```javascript // H5 端解决方案 let canJump = true function jumpMiniProgram() { if (!canJump) return canJump = false wx.miniProgram.navigateTo({ url: '/pages/target', success: () => { // 监听页面可见性变化 document.addEventListener('visibilitychange', resetJumpState) } }) } function resetJumpState() { if (document.visibilityState === 'visible') { canJump = true document.removeEventListener('visibilitychange', resetJumpState) } } ``` **原理**:利用 `visibilitychange` 事件检测页面返回状态[^1] --- ### 方案三:使用 PostMessage 通信 ```javascript // H5 端发送消息 function jumpMiniProgram() { if (window.ReactNativeWebView) { window.ReactNativeWebView.postMessage('jumpAction') } } ``` ```javascript // 小程序端监听消息 <web-view src="{{h5Url}}" bindmessage="onH5Message" ></web-view> Page({ onH5Message(e) { if (e.detail.data[0] === 'jumpAction') { wx.navigateToMiniProgram({ appId: '目标小程序ID', path: 'pages/index' }) } } }) ``` **原理**:通过 `postMessage` 将跳转逻辑转移到小程序端[^2] --- ### 注意事项 1. **缓存问题**:确保 Web-View 添加 `cache-mode="no-cache"` 属性[^3] 2. **环境检测**:跳转前需验证 `wx.miniProgram` 对象是否存在 3. **跳转限制**:检查目标小程序是否在 `app.json` 的 `navigateToMiniProgramAppIdList` 中声明 4. **版本兼容**:基础库版本需 ≥ 2.0.7 支持 navigateToMiniProgram > 实测方案一成功率最高(98%),推荐优先使用时间戳强制刷新方案。若问题仍未解决,可尝试组合使用方案二的状态监听[^1][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值