微信 / WebView 环境兼容
场景
H5 页面需要在微信浏览器、App 内嵌 WebView 等环境中运行,遇到各种特有的兼容问题。
常见问题与方案
1. 环境检测
env-detect.ts
function getEnv() {
const ua = navigator.userAgent.toLowerCase();
return {
isWechat: /micromessenger/i.test(ua),
isAlipay: /alipayclient/i.test(ua),
isIOS: /iphone|ipad/i.test(ua),
isAndroid: /android/i.test(ua),
isApp: /myapp/i.test(ua), // 自有 App 的 UA 标识
isWebView:
/(; wv\))|webview|wv/i.test(ua) ||
typeof (window as any).ReactNativeWebView !== 'undefined',
};
}
2. 微信浏览器常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 页面缓存不更新 | 微信强缓存 | URL 加版本号/时间戳 |
| 视频自动播放失败 | 需要微信 SDK 接入 | WeixinJSBridgeReady 事件 |
| 分享标题/图片不对 | 需要微信 JS-SDK | wx.updateAppMessageShareData |
| SPA 路由分享 URL 不变 | iOS 微信取的是入口 URL | 后端做分享签名时用入口 URL |
| 下拉出现"此网页由..." | 系统行为 | overscroll-behavior: none |
微信视频自动播放
// 微信中自动播放视频
document.addEventListener('WeixinJSBridgeReady', () => {
const video = document.querySelector<HTMLVideoElement>('#myVideo');
video?.play();
});
3. JSBridge 通信
jsbridge.ts
// 通用 JSBridge 封装
function callNative(method: string, params?: Record<string, unknown>): Promise<unknown> {
return new Promise((resolve, reject) => {
const callbackName = `_cb_${Date.now()}_${Math.random().toString(36).slice(2)}`;
// 注册回调
(window as any)[callbackName] = (result: unknown) => {
resolve(result);
delete (window as any)[callbackName];
};
// 不同环境的调用方式
if ((window as any).webkit?.messageHandlers?.[method]) {
// iOS WKWebView
(window as any).webkit.messageHandlers[method].postMessage({
...params,
callback: callbackName,
});
} else if ((window as any).AndroidBridge?.[method]) {
// Android
(window as any).AndroidBridge[method](JSON.stringify(params), callbackName);
} else {
// URL Scheme 兜底
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = `myapp://${method}?params=${encodeURIComponent(JSON.stringify(params))}&callback=${callbackName}`;
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 100);
setTimeout(() => reject(new Error('Bridge timeout')), 5000);
}
});
}
4. WebView 白屏排查
常见面试问题
Q1: H5 与原生 App 通信有哪些方式?
答案:
| 方式 | 原理 | 特点 |
|---|---|---|
| URL Scheme | iframe.src = 'myapp://...' | 兼容性好、有长度限制 |
| 注入全局对象 | App 注入 window.Bridge | 直接调用、需要 App 配合 |
| postMessage | WKWebView 的 messageHandlers | iOS 推荐方式 |
| prompt/console 拦截 | 重写 prompt 等方法 | hack 方式、不推荐 |
Q2: 微信 H5 页面的 SPA 路由分享问题怎么解决?
答案:
iOS 微信浏览器会将页面入口 URL(第一次打开的 URL)作为分享 URL,而不是当前路由的 URL。
解决方案:
- 后端签名时使用入口 URL:iOS 用
window.entryUrl(进入时记录)签名,Android 用location.href - 分享时动态设置:通过
wx.updateAppMessageShareData设置正确的分享链接 - 使用 Hash 模式路由:Hash 变化不影响微信识别的 URL