首页 / 资讯中心 / 文章详情

uni-app小程序WebView避坑指南:从业务域名配置到postMessage通信

uni-app小程序WebView避坑指南:从业务域名配置到postMessage通信 ★ FEATURED ARTICLE
做小程序开发的朋友早晚会撞上webview这个词。我最初接手uni-app项目时第一次遇到需要通过webview跳转到其他网址的需求以为就是在页面里塞一个组件、把src一填就完事结果被域名校验、postMessage时机、token同步这些问题折腾得不轻。后来踩的坑多了才明白这玩意儿虽然表面上是个网页容器但微信给它套了很多条条框框而且uni-app在编译到不同端时webview的行为差异还挺大。这篇就把我从配置到上线、从踩坑到填坑的完整过程整理出来给后面要碰这块的朋友省点时间。1. 为什么要用webview业务场景与选型思路1.1 需要webview的典型业务场景先说说大家什么情况下会用到webview。最常见的是活动页和营销页这类页面一般迭代快、生命周期短用H5开发成本低而且运营可以直接在后台改内容不用频繁发版审核。还有一种是内容型页面比如协议条款、帮助中心、隐私政策这些内容通常由运营维护塞进小程序里做原生页面性价比太低。另外就是老业务迁移公司之前已经有一套成熟的PC端或H5系统比如报表中心、后台管理、富文本编辑器的展示页让它重新用小程序原生写一遍成本太高直接webview套壳是最快的路子。我做的那次需求就是第三种情况。公司有一套内部的数据看板基于网页实现里面有大段的图表组件和复杂交互如果要在小程序里重新实现工作量至少以周为单位。最后定下来的方案就是小程序里留一个入口通过webview把整套看板加载进来原封不动地用。这个选择在技术上不是最优解但是业务上性价比极高。1.2 为什么不能直接跳转其他网址很多人第一次做这个需求时会问为什么不能直接用window.location.href或者小程序的跳转API把用户带到外部浏览器这里要讲清楚小程序的规则微信小程序是一个封闭的生态它不允许你在小程序内部像浏览器一样随意跳转到任意网址。小程序的跳转API比如uni.navigateTo只能跳内部页面根本不能跳外部URL。而web-view组件是微信官方提供的唯一一个可以在小程序内加载网页的合法通道但它同样不是你想加载谁就加载谁——必须先在公众平台配置业务域名通过校验之后才允许加载。这个设计的逻辑在于微信要保证用户在小程序里浏览的内容是可管可控的防止小程序变成纯粹的网页浏览器也防止有人通过外部网页绕过小程序的审核机制。所以只要是跳转到其他网址的需求业务域名的配置就是绕不过去的第一道门槛。搞清楚这个规则后面就不会做无用功。1.3 uni-app里用webview的额外考量如果你只是在微信小程序里用webview那直接写小程序的web-view标签就行。但项目用的是uni-app就意味着页面要兼顾多端——至少App端和H5端你得想好怎么处理。uni-app的web-view组件是一个条件编译的壳编译到微信小程序时它会被转换成小程序的web-view原生组件编译到App端时它对应的是App自带的webview能力而编译到H5端时这个组件的行为又不一样因为H5本身就跑在浏览器里再套一个webview语义上是重叠的。这个差异影响很大。同一套代码你能在小程序里正常加载一个网页但到了App端可能因为URL格式、Cookie策略、缓存机制不同而出现问题。我们在做跨端适配时往往需要根据条件编译写两套不同的处理逻辑。所以不要天真地认为uni-app写一次就万事大吉webview这个组件恰恰是跨端差异最明显的地方之一。2. 微信小程序的webview门槛先搞清规则再动手2.1 业务域名配置的具体流程小程序要用webview加载网页第一步是在微信公众平台配置业务域名。路径是【小程序后台】→【开发】→【开发设置】→【业务域名】。在这里你需要填写要加载的网页域名然后按照提示下载一个校验文件一个特定文件名、特定内容的txt文件把它放到你域名服务器的根目录下确保微信能通过https://你的域名/校验文件名访问到这个文件点击保存后微信会自动校验校验通过即配置成功。这里有几个细节值得注意。第一业务域名必须是备案过的域名而且必须支持HTTPS这是硬性条件不满足就不用往下继续了第二校验文件需要放在根目录不是放在子目录很多人第一次配的时候把文件放到某个路由对应的目录下结果一直校验失败第三配置生效后可能需要等一小段时间微信有缓存有时候校验通过了但真机上还提示非业务域名过几分钟再试就好了。业务域名不是你想配多少就配多少的微信有数量上限如果公司域名特别多要提前规划好配额。2.2 域名配置时的常见误区域名配置这个环节看着简单实操中经常出意外。我见过有人在测试环境用IP地址来配置微信根本不允许业务域名必须是域名形式还有人本地联调时直接把本地IP填进去也是不行的。小程序webview在正式环境下只能加载已配置的业务域名但在开发者工具里有一个不校验合法域名的开关选项很多人图省事一直开着这个选项做开发结果一上线全白屏就是因为忘了配置真实域名。另外要注意的是如果你配的业务域名是https://example.com那么你在webview里加载https://example.com/path是没问题的但加载https://sub.example.com/path却不被允许——子域名和主域名是分开算的想用哪个域名就要把哪个域名也加进业务域名列表里。这个细节特别容易踩因为我们开发环境、测试环境、生产环境往往用不同的子域名如果只在生产环境配了域名测试环境的webview就永远加载不出来。2.3 webview在uni-app不同端的映射关系既然用uni-app就得把webview在三个端的表现摸清楚。小程序端不用说了就是原生web-view组件占满整个页面层级非常高基本不受其他组件遮挡影响但也没法在它上面再覆盖自定义导航栏所以要处理导航栏标题得通过页面配置来做。App端uni-app的web-view是基于原生webview封装的行为上接近手机浏览器但它在页面上的布局方式和内嵌滚动行为、以及和原生代码的交互方式跟小程序端是完全不同的。H5端就更有意思uni-app编译到H5后组件本身不会渲染成一个独立的网页容器因为页面本身就在浏览器里跑你通常需要改用iframe方案或者直接用window.open来处理逻辑得单独写。我自己的项目里因为主要目标是微信小程序所以决策是小程序端走webview正式方案App端单独用条件编译写一套基于plus.webview的处理逻辑H5端暂不要求嵌入外部网页先用一个提示页顶着。这个取舍得在开发前就跟需求方对齐否则后期返工很麻烦。3. 完整实操一个能跑的webview页面3.1 页面骨架与src动态绑定先上一个基础版页面的代码大家感受一下整体结构。在小程序端的uni-app页面里webview的用法极其简单一个标签就够template view classwebview-page web-view :srcwebviewSrc/web-view /view /template script export default { data() { return { webviewSrc: } }, onLoad(options) { // 页面路径里带过来的参数 const url options.url ? decodeURIComponent(options.url) : this.webviewSrc url } } /script没错核心就一个src。但这里有一个很重要的点web-view组件在小程序原生端是覆盖式全屏的它会把页面里其他所有内容都压在下面也就是说你在同一页面里放一个自定义导航栏、放一个按钮这些内容在小程序端大概率是显示不出来的。所以如果你需要在小程序里显示自定义导航栏通常得用一个巧妙一点的方案页面本身只有一个web-view导航栏由网页内部自己来绘制或者通过页面级配置让微信使用默认导航栏。早期基础库版本里web-view还会盖住一切同层组件现在好多了但依然不建议在webview页面上堆叠太多原生元素。3.2 参数传递的三种方式与避坑记录在真实业务里你光把网页加载出来还不够往往要把小程序的用户信息、业务参数传给网页。我总结下来常用的有三种方式。第一种是URL拼接参数最简单直接。比如const baseUrl https://example.com/activity const token uni.getStorageSync(token) const userId 12345 this.webviewSrc ${baseUrl}?token${token}userId${userId}这种方式的问题在于URL长度有限如果参数多且长比如要传整个用户信息对象会直接把URL撑爆。而且URL里如果带了特殊字符一定要用encodeURIComponent处理否则参数会被截断或解析错误。我试过传一个带有号和符号的加密串没编码前在网页端收到的值完全错乱排查半天才发现是URL编码的锅。第二种是用wx.miniProgram往小程序侧存数据。这个方式实际上是从网页反向拿数据不是正向传参但在做免登录方案时很常用网页加载后先通过JS-SDK向小程序要token小程序侧再把token丢给网页。后面讲双向通信时细说。第三种是H5和小程序共用一个后端接口网页加载后自己通过业务参数向服务端换数据不依赖URL里传具体内容只传一个会话标识。这种最优雅、最安全适合生产环境使用。我的建议是上线项目优先用第三种前两种适合原型验收。3.3 登录态与token同步这是webview方案里大家碰到最多的问题。小程序里用户已经通过微信登录拿到了身份但H5页面自己有一套登录体系两边的session完全隔离经常出现的情况就是小程序里点进去一切正常网页里却提示未登录或直接跳登录页。为什么呢因为webview加载的网页它的Cookie和本地存储跟小程序是完全隔离的小程序登录了不代表网页也登录了。我常用的解决办法是URL令牌交换小程序侧把用户唯一标识或临时令牌通过URL传给网页网页在加载时取到token拿它调后端的换取会话接口拿到自己体系里的登录态。具体实现上小程序端这样拼URLconst token uni.getStorageSync(loginToken) this.webviewSrc https://example.com/page?token${encodeURIComponent(token)}网页侧在页面初始化时从window.location里取出token然后调用自己的登录接口完成会话建立。这个方案看着简单但要注意token不能太敏感因为它会出现在URL里而URL有可能被日志记录更稳妥的做法是让后端提供一个一次性的、短时效的票据票据换登录态换完作废。如果你要做App端情况又有不同App的webview没有微信JS-SDK不能通过wx.miniProgram通信需要走uni.postMessage或者原生桥接。这块需要单独处理一定要在项目排期里预留出时间别拿小程序的逻辑直接套App。3.4 加载进度与错误提示webview组件在微信小程序里是黑盒它自己不给你暴露加载进度也没有内置的加载失败页。这在实际体验里很致命如果用户网络不好或者域名挂了用户看到的就是一个白屏搞不清楚是加载中还是打不开。我的建议是至少做一个基础的兜底方案。可以利用页面的onShow和onHide配合一个定时器来模拟是否还在加载更通用一点的做法是在网页内部自己处理错误比如网页里监听window.onerror一旦出错就通过wx.miniProgram告诉小程序侧我加载失败了小程序侧再利用条件渲染在webview上方弹一个遮罩层提示用户。不过这里要再提一下webview的层级问题小程序端webview是原生组件普通view元素容易盖不住它。实践中可以用cover-view来做覆盖提示只有它能稳定覆盖在原生组件上。如果嫌麻烦也可以在路由层做文章——先进一个原生中间页中间页里先请求网页接口做预检确认页面可访问后再打开webview页这样至少能把打开就是白屏的情况拦截掉一部分。4. 与H5页面双向通信postMessage实战4.1 H5侧引入微信JS-SDKwebview要做双向通信靠的是微信给网页注入的wx.miniProgram对象。网页里需要引入微信官方JS-SDK也就是jweixin模块然后在网页代码里判断当前环境是不是在小程序内const ua window.navigator.userAgent.toLowerCase() const isWechatMiniProgram ua.indexOf(miniprogram) -1 if (isWechatMiniProgram) { const jweixin require(jweixin-module) // 按你自己的构建方式引入 // 网页向小程序发送消息 jweixin.miniProgram.postMessage({ data: { action: refresh, pageId: 123 } }) }这里有个很关键的点postMessage并不是你一发消息小程序端马上就能收到。微信限制了这个通信的触发时机——只有当webview页面发生特定行为时比如页面后退、分享、组件销毁、右上角菜单复制链接等消息才会被小程序接收。如果你在小程序页面里通过bindmessage绑定事件去接消息平时它可能一直是安静的退出去再回来才触发。这个设计是为了避免网页频繁往小程序侧刷消息但确实也坑了不少人。4.2 通信触发时机与限制我最早做的那个需求里H5页面里有个领取成功的按钮点击后通过postMessage通知小程序刷新列表。结果实测发现小程序侧半天收不到消息因为用户点击后并没有触发任何时机事件消息没被送达。折腾半天才查到官方文档里写的触发时机说明。按照微信的规则postMessage发送的数据会被暂存起来当以下时机出现时数据才被统一发送给小程序1webview页面返回上一页或内部跳转2webview被销毁3用户点击右上角菜单里的转发按钮4用户复制链接。这跟你直觉里的实时通信完全不沾边。所以如果业务是那种点击按钮小程序页面立即变更的强交互webview这套方案就不合适你需要考虑把交互逻辑搬到小程序原生页面来做或者退而求其次点击按钮后先在网页里提示操作成功等用户自然返回小程序页面时再在bindmessage回调里处理刷新。这不是bug是平台限制得在设计阶段就避开。4.3 一个实用的返回刷新案例下面给一个我在项目里实际用过的、符合平台限制的实操案例。场景是这样的小程序首页有一个webview入口用户在里面填写了一个表单点击提交成功后网页端通过postMessage通知小程序数据已更新返回后请刷新首页。由于postMessage在返回时才触发所以整体流程非常顺网页提交成功 → 用户点导航栏返回按钮 → 小程序webview页面收到message → 修改首页的某个刷新标识位 → 首页onShow时检测到标识位重新拉取数据。小程序webview页的代码逻辑大概是这样template web-view :srcwebviewSrc messagehandleMessage/web-view /template script export default { methods: { handleMessage(e) { // e.detail.data 是网页传过来的消息数组 const data e.detail.data if (data data.length 0) { const payload data[0] if (payload.action submitSuccess) { // 设置一个全局的标志 uni.setStorageSync(needRefresh, true) } } } } } /script回到首页时再做检查onShow() { const needRefresh uni.getStorageSync(needRefresh) if (needRefresh) { this.loadList() uni.removeStorageSync(needRefresh) } }这个方案实测下来很稳没有出现数据不刷新的问题。思路就是顺着微信的规则来设计交互而不是逆着它的规则硬来。5. 常见问题与排查技巧实录5.1 白屏与域名校验失败白屏是webview出现频率最高的问题而且原因有十几种。首先是域名校验失败页面加载时微信会先校验域名如果域名没配置或配置没生效页面会显示一个非业务域名的提示页。但注意提示页可能一闪而过然后变白屏所以你要在加载时仔细看页面顶部有没有出现过异常提示。第二种情况是域名配了但校验文件被删了或移动了位置微信访问不到这个时候建议直接在浏览器里访问https://你的业务域名/校验文件名看能不能打开。第三种是页面有重定向webview加载的URL会跳转到另一个未配置的域名这种情况微信也会拦截所以你在配置业务域名时不光要配入口页的域名重定向过程中涉及到的域名也要加进去。5.2 cookie与本地存储隔离我遇到过最头疼的问题是在微信里用手机浏览器直接访问H5页面一切正常放到小程序webview里登录状态却一直是失效的。排查到最后发现小程序的webview默认不共享微信浏览器的Cookie它有自己的存储空间。而且如果你在webview里登录过一次下次用户在其他地方登录了微信webview里的登录态可能还是旧的那个两边不同步就会产生我明明登录了为什么网页里还让我登录的诡异现象。解决办法要么是用URL令牌每次进来重新换取会话要么就在网页端设计静默跳转登录页拿code再换token的逻辑总之不能依赖Cookie做webview的会话管理。如果你在App端也用了uni-app的webview同样的道理Cookie策略在小程序和App的webview里可能还不一样都需要做适配。5.3 内部跳转被拦截有时webview加载的第一个页面是正常的但网页里有个链接用户一点或者网页通过JS触发了跳转结果跳不过去。原因还是老生常谈跳转的目标URL不在业务域名白名单里。一次跳转也逃不过校验。所以如果要在一个webview里连续浏览多个页面比如从一个列表页进到详情页这些页面涉及的域名都必须提前配置好否则就是第一个页面能打开第二个页面白屏或报错。还有一类情况是网页里用了window.open去开新窗口这在webview里通常是不生效的需要用window.location.href来跳转这个可以在网页代码里优化。5.4 App端和H5端的差异补充如果你只把小程序端调通就觉得完事大吉那就太小看跨端问题了。App端uni-app的webview跟小程序端的逻辑并不公用App端获取网页往App传的消息用的是plus.webview的evalJS或addEventListener根本没有message这回事。H5端更不用提webview组件压根不适用你得用iframe去实现类似的嵌入。这一点在需求评审时一定要说清楚否则就会出现小程序做完了App端还是不能访问的尴尬局面测试提一堆bug开发背一堆锅。这里我的建议是如果项目是原生小程序为主webview的核心逻辑按小程序来做App端用条件编译单独搞一套降级或适配方案比如干脆用系统浏览器打开外部网址H5端与App端同理可以用window.open或iframe嵌入就看产品上怎么定义行为了。5.5 关于调试工具的一些心得最后聊聊调试。开发者工具里有个不校验合法域名选项开发阶段可以打开但要记得上线前关掉。真机调试时webview打开的页面在小程序开发者工具里看到的网络请求有限有时候很难定位是请求失败还是页面渲染异常。我的习惯是先在真机浏览器的同环境里访问一遍URL确认网页本身没问题再去小程序里开webview。如果网页在浏览器正常、在webview里异常那基本可以断定是微信环境的问题重点查域名配置、HTTPS证书、UA判断这几个方向。如果怀疑是UA判断把页面内容搞没了可以暂时在网页里把小程序环境判断的逻辑注释掉强制按普通浏览器渲染排查。这个小技巧救过我很多次。因为很多H5页面会根据环境做差异化渲染比如在小程序环境里不发某些请求、不显示某些组件万一判断逻辑写错了就会出现网页单独访问正常、webview里残缺不全的奇怪现象。先排除页面自身逻辑问题再回头查小程序配置排查效率会高很多。最后再补充一点个人习惯凡是涉及webview的需求我都会在开发前要求产品方把目标域名和业务域名清单整理成文档口头说的就是这几个页面到后面一定会变成还有一个域名忘了加。文档在手配置的时候照着填就行不会漏。webview本身不是复杂技术真正的复杂度都在它周围那些看不见的规则里把这些规则提前摸透这个页面就能平稳上线。
阅读完成 · 觉得有帮助?
咨询建站