不能直接用JSON.stringify传参,因URL协议对未编码字符容忍度低,易致iOS WKWebView参数截断、中文乱码、特殊字符解析失败,且Android低版本有2048字符长度限制,未压缩JSON易超限;应改用encodeURIComponent整体编码或postMessage通信。

为什么不能直接用 JSON.stringify 传参
很多团队初期直接把对象塞进 JSON.stringify,再拼到 URL query 或 iframe src 里,结果在 iOS WKWebView 上遇到参数截断、中文乱码、特殊字符(如 &、=、#)导致解析失败。这不是 JS 的问题,而是 URL 协议本身对未编码字符的容忍度极低——window.location.href 或 iframe.src 被浏览器强制解析时,会提前拆分 query,把本该属于一个字段的值切碎。
更隐蔽的问题是:某些 Android 低版本 WebView 对 URL 长度限制严格(常卡在 2048 字符内),而未压缩的 JSON 很容易超限,且无法被 Native 端稳定捕获。
- 避免用
JSON.stringify(obj)直接拼接 URL - 必须对整个参数字符串做
encodeURIComponent,而不是只编码 key 或 value - 若参数含二进制或 Base64 数据,优先改用
postMessage+WKScriptMessageHandler(iOS)或evaluateJavascript(Android 4.4+)替代 URL scheme
iframe.src 场景下推荐的序列化流程
这是兼容性最广、仍被 Cordova / WebViewJavascriptBridge 沿用的方式,但必须控制好编码粒度和边界。
正确做法是:先构造干净的 query string,再整体编码,最后注入 iframe。不要分段拼接、不要信任后端返回的原始字符串。
立即学习“前端免费学习笔记(深入)”;
- 用
URLSearchParams构造参数(比手拼?a=1&b=2更安全):const params = new URLSearchParams();params.set('action', 'upload');params.set('fileId', 'abc123');params.set('meta', JSON.stringify({size: 102400, type: 'image/jpeg'}));const url = `https://__jsbridge__?${encodeURIComponent(params.toString())}`; - 注入前检查长度:
if (url.length > 2000) { /* 切换为 postMessage 方案 */ } - iFrame 创建后必须立即 append,且需在
document.documentElement下(避免被 Shadow DOM 或框架拦截)
Android addJavascriptInterface 下的参数传递陷阱
这个 API 看似省事,但 Android 4.2+ 要求方法必须加 @JavascriptInterface 注解,且参数类型受限——Java 层只接受 String、int、boolean 等基础类型,**不支持直接传入复杂对象或数组**。
所以 JS 侧不能写 Android.doSomething({a: 1, b: 'x'}),Native 会收不到或崩溃。
- JS 必须先序列化为字符串:
Android.doSomething(JSON.stringify(payload)) - Native Java 层再用 Gson/Fastjson 解析,而非依赖系统自动转换
- 注意:
addJavascriptInterface在 Android 4.2–4.4 存在远程代码执行漏洞,生产环境务必校验调用来源(如比对webView.getOriginalUrl())
iOS WKWebView 中绕过 URL 长度限制的务实方案
WKWebView 对 location.href 和 iframe.src 的单次消息长度限制比 UIWebView 更严,尤其在 iOS 15+ 上,超过 1900 字符易触发静默丢弃。这时候靠编码已无济于事。
真正有效的做法是切换通信通道,而不是优化序列化算法:
- 优先使用
webkit.messageHandlers.<name>.postMessage(),它接受任意可序列化对象(包括 ArrayBuffer),且无长度硬限制 - Native 端注册 handler 时,确保
configuration.userContentController.add(self, name: "bridge")已完成 - JS 调用示例:
try {window.webkit.messageHandlers.bridge.postMessage({action: 'sensorData', payload: new Float32Array([1.2, 3.4, 5.6])});} catch (e) { /* 降级到 iframe 方案 */ } - 注意:该 API 在 iOS 8+ 可用,但必须在页面
DOMContentLoaded后调用,否则window.webkit为 undefined
参数序列化不是独立环节,它必须和通信通道绑定设计。选错通道,再严谨的编码也救不了;选对通道,JSON.stringify 反而是最简最稳的选择。别在 URL scheme 上死磕大对象,那是拿 HTML 的短板去扛 Native 的需求。


















