这是因为之前遇到一个线上问题引起的,这篇文章阅读的对象也比较适合 Android 和前端阅读。
我们说下 标题的 Document 对象,这个对 Android 开发者而言有些陌生,可以详见 MDN 里面的解释,简单的说就是它可以去代表我们要加载的这个网页对象。里面包含一些基本的信息和所谓生命周期。
Android WebView 提供了基于 WebViewClient 的页面周期的监听方法,常见的有:
- onPageStarted 当页面开始请求
- onPageFinished 页面完成加载
- onReceivedError 当加载出现错误的时候
更多方法可以详见 https://developer.android.com/reference/android/webkit/WebViewClient
在 JS 中,我们监听页面加载有两种常见的方法,一种是监听 window.onload
另外一个是 document
里的 readychange
来实现的。
其中 window.onload 定义是当页面完全加载时候触发,包括页面的上包含的图片和样式资源,脚本资源。
而 readychange 会监听三个状态的变化:
loading
开始加载documentinteractive
DOM 节点树生成完毕可进行操作complete
DOM 节点树生成完毕并且资源也已经加载完成
接下来我们做个试验:
我们实现一段监听代码:
<head>
...
<script>
window.startDate = Date.now()
</script>
<style type="text/css">
...
</style>
</head>
<body>
...
<script>
function writeLog(str) {
var el = document.createElement("P")
var now = Date.now()
el.innerText = str + ':' + now + ' (+' + (now - startDate) + ')'
var wrap = document.querySelector('#js-contents')
wrap.appendChild(el)
}
document.addEventListener('readystatechange', function() {
if (document.readyState == 'loading') {
writeLog("readystatechange-loading")
} else if (document.readyState == 'interactive') {
writeLog("readystatechange-interactive")
} else if (document.readyState == 'complete') {
writeLog("readystatechange-complete")
}
})
window.onload = function() {
writeLog("window onLoad")
}
</script>
我们会打印出我们比较关注的时间变化。
Android WebView 这边我们需要进行页面加载的测试,然后再 onPageFinished 执行一段 JS 代码。
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
pageLoaded = true
view.loadUrl("javascript:writeLog('onPageFinished')")
}
测试发现
onPageFinished 与 window.onload
/ readyState complete
几乎一致。
这个确认后,我们就再回到之前我们说遇到的 Bug 的问题。
由于客户端出于稳定考虑,实现 Loading 通常会基于 onPageFinished
来关闭,而我们客户端同时还需要监听页面的一些消息,实际上从上面知道 onPageFinished 和 window.load 几乎一致,因此页面的一些资源会影响到比如 img 或者 prefetch 。
由于我们是单页应用,但是这次打包出现了一些配置,会将部分脚本到 prefetch里面去进行预加载,然后 onPageFinished 会等到 prefetch 完成加载后才去执行。这样便造成了之前一直没碰到的,收到了 JS 发出的消息,但是迟迟客户端无法响应。随着单页应用增多,页面纯粹就是一些 JS / CSS , 但是在脚本资源加载的时候,我们要合理的去使用 prefetch / preload 这些标签。