ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue3 H5嵌入微信小程序web-view的navigateTo跳转实践

Vue3 H5嵌入微信小程序web-view的navigateTo跳转实践 做 Vue3 项目时如果 H5 页面要嵌进微信小程序的 web-view 里大概率会遇到一个需求点按钮后从网页跳到小程序原生页面。这个动作靠的就是 wx.miniProgram.navigateTo()。我最初接手这个需求时踩了不少坑尤其是“跳转指定小程序”这几个字的理解——不少同学以为它是用来跳转到另一个小程序的实际上它在网页端只能跳回当前小程序的指定页面。这篇文章把我的使用经验完整写出来从环境配置、Vue3 组合式封装、跳转参数到常见问题排查尽量让第一次做的人不用再翻几十个帖子。适合谁看正在用 Vite Vue3 开发 H5需要嵌到微信小程序 web-view 里的前端同学或者在小程序与 H5 混合开发中负责路由跳转的人。即使你是刚接触微信生态只要照着步骤来也能跑通。1. 先搞清楚wx.miniProgram 到底是从哪来的1.1 谁在调用谁H5 与小程序的边界先聊清楚运行环境。微信小程序的 web-view 组件可以承载一个网页这个网页本质上还是跑在浏览器里的 H5但不能直接调用小程序的 API比如 wx.request、wx.login 这些在小程序运行环境里的方法网页里根本没有。微信做了一层桥接在小程序的 web-view 中加载网页时微信会向页面注入一个全局对象 wx里面带 miniProgram 子对象。这个对象的出现时机比网页的任意脚本都要早所以你在 Vue3 的 onMounted 里访问 wx 通常已经存在了。需要区分的是只有在小程序 web-view 中打开的网页才有完整的 wx.miniProgram 能力。如果你把同一个 Vue3 页面直接放到微信聊天窗口、公众号文章里打开可能也能访问到 wx但 miniProgram 相关的跳转方法不可用或者根本没有这个方法。所以第一步不是写跳转而是先判断当前环境避免在普通浏览器里执行 wx.miniProgram.navigateTo 报错。1.2 navigateTo 与其他路由方法的边界wx.miniProgram 提供的方法并不只有 navigateTo在小程序端等价的路由能力几乎都开放给了网页端。常见的有这几个方法对应小程序 API主要用途关键限制navigateTowx.navigateTo跳转到普通页面保留当前页面不能跳 tabBar 页面redirectTowx.redirectTo关闭当前页面跳转到新页面不能跳 tabBar 页面switchTabwx.switchTab跳转到 tabBar 页面并关闭其他非 tabBar 页面只能跳 tabBar 页面不能带参数reLaunchwx.reLaunch关闭所有页面打开某个页面可跳 tabBar 也可普通页但不能带复杂参数navigateBackwx.navigateBack返回上一个页面不能指定返回页只能按层级从表格能看出来很多报错是因为混淆了 navigateTo 和 switchTab。如果你的目标页面在 app.json 的 tabBar 列表里用 navigateTo 会直接失败。这个点我在后面会专门展开。2. Vue3 项目中接入前的环境准备2.1 引入微信 JSSDK 的两种方式虽然 wx.miniProgram 是注入对象但官方推荐通过 JSSDK 来统一处理。最简单的方式是直接在 index.html 里加 script 标签script srchttps://res.wx.qq.com/open/js/jweixin-1.6.0.js/script加载这个脚本之后全局会挂一个 wx 对象。注意这里加载的是“微信 JS-SDK”不是为了分享、支付才需要的那个 jweixinweb-view 页面里同样可以用它来拿到 miniProgram 对象。如果你的 Vue3 项目是用 Vite 构建、代码里不想依赖全局变量也可以用 npm 包npm install weixin-js-sdk然后在需要的地方引入import wx from weixin-js-sdk;有同学会问到底是 script 引入好还是 npm 包好我的建议是看项目环境。管理后台这类不常变化的页面script 引入省得处理打包体积但如果你的 Vue3 项目本身就用了 module bundlernpm 包更符合编码习惯而且后续做 TS 封装更方便。两者在使用 wx.miniProgram 的写法上没有任何区别。2.2 先判断是不是小程序环境不管用什么方式引入跳转前都应该做一次环境判断。原因很简单这个 Vue3 项目可能既要在浏览器里跑又要在小程序 web-view 里跑。在浏览器里直接调 wx.miniProgram.navigateTo十有八九会报 TypeError: Cannot read properties of undefined。现在常用的判断方式是看 userAgentexport function isWxMiniProgram(): boolean { const ua navigator.userAgent; return ua.includes(miniProgram); }微信开发者工具的 web-view 模拟环境中UA 里通常也包含 miniProgram 字段所以开发阶段能模拟出来。真机上判断也比较稳定。需要注意的是如果你在小程序里开了“调试模式”UA 可能还带着其他标识但 miniProgram 这个关键词不会丢。2.3 小程序端必须配置业务域名这块很多人会漏但漏掉的直接后果就是 web-view 白屏。在小程序后台的“开发管理-开发设置-业务域名”里需要把承载 Vue3 页面的域名加入白名单并且下载校验文件放到域名根目录。只有配置成功web-view 才能正常加载你的 H5。还需要注意只有企业、个体工商户等非个人主体类型的小程序才支持 web-view 组件个人主体小程序即使配置了域名web-view 也无法使用。如果你的项目是个人小程序老实换方案别在 H5 跳转这条路死磕。2.4 封装一个 Vue3 组合式函数直接在每个页面里重复写 wx.miniProgram.xxx 会显得很乱。既然项目用的是 Vue3组合式函数是最合适的封装方式。我习惯建一个useMiniProgram.tsimport { ref } from vue; const isMiniProgram ref(false); export function useMiniProgram() { function checkEnv() { return typeof wx ! undefined wx.miniProgram; } function navigateTo(url: string) { if (!checkEnv()) { console.warn(当前不在微信小程序 web-view 环境中); return; } wx.miniProgram.navigateTo({ url, fail: (err: unknown) console.error(navigateTo fail:, err), }); } function switchTab(url: string) { if (!checkEnv()) return; wx.miniProgram.switchTab({ url }); } function reLaunch(url: string) { if (!checkEnv()) return; wx.miniProgram.reLaunch({ url }); } function navigateBack(delta 1) { if (!checkEnv()) return; wx.miniProgram.navigateBack({ delta }); } return { isMiniProgram, navigateTo, switchTab, reLaunch, navigateBack, }; }这个封装看起来简单但在实际项目中能省下很多事情。页面里只需要const { navigateTo } useMiniProgram()就可以调用遇到环境不对还能统一打日志比散落在各组件里好维护。3. 核心实现用 navigateTo 跳转到指定页面3.1 先澄清navigateTo 不能直接跳另一个小程序标题里“跳转指定小程序”容易让人误以为能从一个 Vue3 网页直接跳到另一个微信小程序。官方并没有给 web-view 页面开放这个能力wx.miniProgram.navigateTo 的 url 只能指向当前小程序内的页面。想跳到另一个小程序常规做法是先用 navigateTo 跳到当前小程序的一个中转原生页面再由这个原生页面调用 wx.navigateToMiniProgram 去打开目标小程序。这个方案后面第 5 章详细讲。所以如果你要跳的页面就在当前小程序里可以直接看下一节如果确实要跨小程序请直接翻到 5.1那里给了一套完整可用的中转方案。3.2 正确的 URL 格式和参数传递先说基本写法。假设小程序里有一个商品详情页路径是 pages/goods/detail你想从 H5 跳过去并且带上商品 idconst id 10086; const url /pages/goods/detail?id${encodeURIComponent(String(id))}fromh5; wx.miniProgram.navigateTo({ url, success: () console.log(跳转成功), fail: (err) console.error(跳转失败, err), });这里有几个关键点都是我实际踩过的第一url 必须以/开头。我一开始写成pages/goods/detail小程序端直接报navigateTo:fail url not in app.json。这个报错非常误导人因为页面路径明明存在只是少了前导斜杠。第二页面路径不需要带.html后缀也不需要写?之前的 hash 风格直接用小程序的app.json里配置的路径。第三参数尽量用 encodeURIComponent 编码。如果参数里有中文、特殊符号、空格没有编码会让小程序端解析出来的值缺失或乱码。如果项目里参数很多可以用URLSearchParams或者自己写一个序列化函数别图省事拼字符串。第四navigateTo 最多只能打开十层页面。如果用户在小程序原生的页面里跳来跳去超过十层再调用 navigateTo 会失败这时需要考虑 reLaunch 或者 navigateBack 调整路由栈。3.3 tabBar 页面和普通页面的跳转差异小程序把页面分成两种普通页面和 tabBar 页面。tabBar 页面就是底部有标签栏的那些比如首页、分类、我的。web-view 里跳 tabBar 页面不能用 navigateTo必须用 switchTabwx.miniProgram.switchTab({ url: /pages/index/index, });switchTab 比较特殊它不允许带 query 参数。如果你希望 tabBar 页面能接收到 H5 传过去的参数只能先把参数存到小程序端的全局 storage 或请求参数里再通过其他方式读取。我的做法一般是在 H5 跳转前先把数据通过 wx.miniProgram.postMessage 发给小程序容器小程序端在 web-view 的 bindmessage 事件里接收并缓存然后 switchTab 过去之后再从缓存里取。这里顺便提一下 postMessage 的触发时机。H5 里调用 wx.miniProgram.postMessage 并不会立刻把消息发给小程序而是要等 web-view 页面发生特定事件比如用户分享、页面卸载、后退或者组件销毁时才会触发。所以别指望 postMessage 传完消息马上就能在小程序端收到需要踩准时机。3.4 从原生页面返回 H5 时怎么处理状态navigateTo 是保留 H5 所在的 web-view 页面跳到原生页面后用户可以返回。返回后 H5 的 Vue3 页面会重新出现在 web-view 里但页面的状态不会自动刷新。我遇到过一个场景H5 里提交表单后跳到原生订单列表用户处理完订单返回H5 上的状态还停留在“已提交”实际上订单已经被改过了。要解决这个问题可以在 H5 里监听 visibilitychange 或者在小程序端通过 wx.miniProgram.postMessage 回传刷新信号。也可以用更简单的方式小程序原生页在处理完成后 navigateBack并带一个参数回来但 web-view 承载的 H5 并不能直接读取路由参数它只能感知到页面重新可见。所以在 H5 的 onShow 或者 visibilitychange 回调里重新拉数据是更靠谱的方案。4. 实操过程中最常见的几个坑4.1 一个真实场景登录后跳转原生订单页我把场景完整走一遍。假设你的 Vue3 页面嵌在小程序 web-view 里用户在小程序里打开这个页面H5 内部通过微信手机号快速登录逻辑拿到了用户标识登录成功之后要跳转到小程序原生的订单列表页。第一步在 Vue3 的登录接口返回后保存用户 token 到 H5 的 localStorage 或内存变量。第二步确认当前页面是在小程序环境里。第三步调用 navigateTo 跳转function goOrder() { const url /pages/order/list?userId encodeURIComponent(userId); wx.miniProgram.navigateTo({ url, success() { console.log(跳转订单页成功); }, fail(err) { console.error(跳转订单页失败, err); }, }); }这里不建议把完整的登录 token 直接拼到 url 里。web-view 的页面 URL 是有可能被外部拿到并重放的如果把 token 带上相当于把你的登录凭证就此暴露。更稳妥的做法是H5 先通过 postMessage 把 token 传给小程序容器小程序原生订单页再从自己的存储里读取跳转时只传 userId 等非敏感信息。如果确实需要鉴权最好用短期有效的 code 换 token。如果小程序端在 bindmessage 里接收 token 时发现还没收到可以加一个重试机制否则会出现用户到了订单页但接口报未登录的情况。4.2 报错与现象速查表直接整理一张表遇到问题先对着查报错或现象常见原因解决办法wx is not defined没有引入 JSSDK或不在微信环境在 index.html 引入 jweixin 脚本并确认 UAnavigateTo:fail url not in app.jsonurl 少了前导斜杠或路径与 app.json 不一致改成/pages/xxx/xxx格式跳转 tabBar 页面没反应用了 navigateTo改用 switchTabweb-view 白屏业务域名未配置、校验文件缺失、个人主体小程序后台配置域名并放置校验文件开发者工具正常真机跳转失败真机 web-view 域名、缓存、版本差异用真机调试面板看完整报错信息跳转成功但参数没收到参数未编码或中文/特殊符号被截断用 encodeURIComponent 包一层返回 H5 后页面数据还是旧的Vue3 页面没有监听重新可见监听 visibilitychange 重新拉数据4.3 Vue3 TypeScript 的类型声明问题使用 weixin-js-sdk 时最容易卡住的是 TypeScript 报错找不到模块 weixin-js-sdk 或 wx 没有类型声明。npm 包自带类型往往比较老甚至没有 full type。项目里可以加一个声明文件declare module weixin-js-sdk { interface MiniProgram { navigateTo(options: { url: string; success?: () void; fail?: (err: unknown) void }): void; switchTab(options: { url: string; success?: () void; fail?: (err: unknown) void }): void; reLaunch(options: { url: string; success?: () void; fail?: (err: unknown) void }): void; navigateBack(options: { delta?: number }): void; postMessage(options: { data: Recordstring, unknown }): void; getEnv(callback: (res: { miniprogram: boolean }) void): void; } const wx: { miniProgram: MiniProgram; }; export default wx; }如果不想维护类型也可以用 any 直接过关但长期维护还是建议搞一份声明。Vue3 项目里组合式函数配类型后调用方写起来会顺手很多不会出现“跑起来没问题、一 build 就报错”的窘境。5. 扩展方案跨小程序跳转和 uni-app 适配5.1 想要跳到另一个小程序中转页怎么设计前面说过wx.miniProgram.navigateTo 只能跳当前小程序内的页面。如果你的业务确实需要从 H5 跳转并打开另一个小程序我的方案是这样的在当前小程序里建一个不可见的纯中转页面例如pages/bridge/index。H5 先 navigateTo 到这个中转页并带上目标小程序的 appId、path 和业务参数const targetAppId wx1234567890abcdef; const targetPath /pages/home/index; const bridgeUrl /pages/bridge/index?appId${encodeURIComponent(targetAppId)}path${encodeURIComponent(targetPath)}; wx.miniProgram.navigateTo({ url: bridgeUrl, });中转页在小程序端使用原生代码在 onLoad 里拿到参数后调用 wx.navigateToMiniProgramwx.navigateToMiniProgram({ appId: options.appId, path: decodeURIComponent(options.path), extraData: { from: h5 }, success() {}, fail(err) { console.error(err); }, });注意 wx.navigateToMiniProgram 必须由用户点击行为触发中转页里最好不要自动执行而是放一个“继续前往”按钮让用户再点一下。否则在某些版本上会被拦截或者出现 unavailable 的报错。5.2 在 uni-app 项目里怎么处理如果你不是纯 Vue3 项目而是用 uni-app 开发 H5并打算打包后放进微信小程序 web-view处理方式基本一致。uni-app 的 H5 端同样可以通过 wx.miniProgram 跳转只不过在 uni-app 里建议用条件编译来隔离逻辑// #ifdef H5 import wx from weixin-js-sdk; // #endif export function switchToMiniProgram(url: string) { // #ifdef H5 wx.miniProgram.navigateTo({ url }); // #endif }这里要特别提醒uni-app 在小程序端运行时也会有 wx 对象但那套 wx 是小程序自己的全局 API和 web-view 里的 wx.miniProgram 不是一回事。条件编译一定要写对不然在小程序端执行 wx.miniProgram 会因为 wx.miniProgram 为 undefined 而报错。打包时如果遇到体积超过 2MB 的问题一般是把 JSSDK 动态引入或者在 H5 端使用 script 标签避免所有平台都打进去。5.3 安全与合规注意点最后讲几个容易被忽略的安全点。web-view 承载的 H5 页面其实是在微信的受限容器里运行的微信对业务域名、页面内容都有要求不要尝试在 H5 里做任何绕过限制的操作。跳转参数不要携带手机号、身份证号、完整 token 这类敏感数据。如果业务需要回传登录用户信息优先走 H5 与小程序端约定的加密协议或者用短期令牌。另外web-view 页面的 URL 不能随便跳转到未被配置的域名否则会被拦截。不要把业务域名配成 http 明文生产环境必须使用 https。这些规定看起来繁琐但对用户数据和平台安全都是必要的。合规的事情建议在项目启动时就规划好不然等联调阶段才发现返工成本很高。我个人在实际调试中的经验是先把 wx.miniProgram.getEnv 的返回值打出来看确认当前环境确实是小程序。然后再试跳转。如果在开发者工具里一切正常但真机不行多半是域名配置或缓存问题清一下微信缓存再试。先把环境判断、域名配置、路径写法这三件事做对后面的跳转基本就顺了。这个功能本身不复杂但细节确实多希望这篇能帮你少走几步弯路。
返回列表