一个网页除了可以运行在公共的浏览器上,也可以运行在 APP 端内的 WebView 组件上。由于这些 Hybrid Web 网页运行在一个相对封闭的环境里,所以 APP 本身可以向 WebView 中注入一些 JS 代码,对 Web 页面做定向增强(最典型的运用就是 JSBridge,提供了一道 Web <--> Native 通信的桥梁)。
在绝大多数情况下,业务开发并不需要感知这些 Native 注入的代码,但是在一些 性能优化/链路排查 的情况下,就需要感知这些 Native 注入代码的时机和运行情况了,从而更好的定位问题。
由于 Chrome/Safari 的 debug 调试工具基本上是为 纯 Web 服务的,而且这个需求很小众,所以这个能力支持的并不是很好。这个小需求网络上没什么总结性的文章,ChatGPT 回答的也差强人意,正好这段时间也做了一些相关的工作,所以顺势就记下来,帮助某个有缘人。
直接查看 Native 代码
如果你对 Native WebView 的封装代码很熟悉,或者有一定的 Native 经验,直接阅读源码是最快的方式。这里我说几个最常用的 JS 注入 API:
iOS
iOS 主要关注这 3 个 API:
addScriptMessageHandler[1]
- (void)addScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
通过这个方法可以给WKWebView环境中添加一个指定 name 的 JS 对象,前端可以调用该对象的 postMessage 方法,向客户端发送消息。前端类似于这样调用:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
addUserScript[2]
// WKUserContentController - (void)addUserScript:(WKUserScript *)userScript;
可以用这个函数注入 JS 脚本字符串到 WKWebView 中。
evaluateJavaScript[3]
// WKWebView - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *error))completionHandler;
这个函数也可以在 WKWebView 上下文中运行一段 JS 代码。
其实还有很多注入函数,但常用的就这 3 个,其它的函数就是和他们有些细微的差别,感兴趣的可以直接看官方文档。
另外还需注意的是 JS 代码注入的时机,页面加载前还是页面加载后注入代码,带来的影响可能是大不一样的。而且这个 API 也特别多,可参考文档:WKNavigationDelegate[4],重点关注 didStartProvisionalNavigation[5] 和 didFinishNavigation[6]。
图片
https://bbs.huaweicloud.com/blogs/331397
Android
Android 主要关注这 2 个 API:
addJavascriptInterface[7]
/** Instantiate the interface and set the context. */class WebAppInterface(private val mContext: Context) { // 通过 @JavascriptInterface 注解,向 WebView 暴露 showToast 方法 @JavascriptInterface fun showToast(toast: String) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show() }}val webView: WebView = findViewById(R.id.webview)// "Android" 将会暴露在 Webview 的 window 变量上webView.addJavascriptInterface(WebAppInterface(this), "Android")
然后前端直接调用即可:
<input type="button" value="Say hello" notallow="showAndroidToast('Hello Android!')" /><script type="text/javascript"> function showAndroidToast(toast) { window.Android.showToast(toast); }</script>
evaluateJavascript[8]
public void evaluateJavascript (String script, ValueCallback<String> resultCallback)
类似于 iOS,也是在 WebView 上下文中运行一段 JS 脚本。
同样的,Android 也要注意 JS 代码注入的时机。API 太多了,参考这个链接:WebViewClient#public-methods[9],重点关注 onPageStarted[10] 和 onPageFinished[11]。
有能力阅读 Native 源代码是最理想的情况,但是现实一般很残酷:
- 绝大部分前端同学不懂 Native 代码
- 现在还存留的高流量 APP,基本都迭代 5 年以上了,代码一层一层糊成