最近在项目中遇到一个webview中的返回问题,觉得比较有价值,在这里记录下。
问题所在
需求是在安卓客户端APP中用webview打开vue的单页应用,在vue应用中,路由自己管理,每次点击导航栏的返回按钮,都返回上级路由页面,当回到最初webview打开所在的页面时,再点击返回按钮,就调用客户端提供的 clientApi.goBack() 方法,会销毁掉这个webview,回到客户端app所在的页面。
在这个 vue 应用中,有 A、B、C、D、E,五个页面,彼此之间都可以相互跳转,每个页面都可能是客户端APP打开webview时所处的第一个页面。那么问题来了,假设某一次打开 webview 时,进入的是 C 页面,在应用内各种跳转,然后一级级地返回,返回到了 C 页面,如何判断该调用 clientApi.goBack() 了呢?
寻找解决方案
我尝试在 C 页面调用 router.back(), 没有任何反应,当然,这是JS的代码,销毁webview已经超出了它的权限。在 route 和 router 中都没有找到任何能判断当前页面就是打开 webview 时所在的页面的线索,又尝试在 window.history 中找找线索。
window.history 有三个属性:
lengthscrollRestorationstate
前面两个用不了,于是尝试下state.
发现每次路由变化之后,window.history.state 都是不同的值, 例如:
{
key: "icgo4i",
state: undefined
}
初始页面的 window.history.state 可能是 null, 也可能是一个如上的对象。尝试进行几次路由跳转,然后再返回,发现同一个页面,在不同的路由时机,其 window.history.state 的值是不同的,比如,当前在 A 页面,其 window.history.state 值为:
{
key: "icgo4i",
state: undefined
}
跳转到 B页面,由B页面跳转至 C 页面,再由 C 页面跳转至 A 页面,那么此时 A 页面的 window.history.state 的值为:
{
key: "0yfpsb",
state: undefined
}
与前一个 A 页面的值是不同的。
然后再由A页面一路调用 router.back() 或者window.history.back(),返回到最初的 A 页面,此时 window.history.state 值又变成了:
{
key: "icgo4i",
state: undefined
}
可以看到,与原来的值一样。
解决方案
问题终于有了解决方案,根据 window.history.state 的记忆功能,便可以判断当前页面是否是打开 webview 时所处的页面。步骤如下:
- h5 APP加载后,记录下当前页面的
window.history.state - 每次调用点击左上角返回按钮时,判断当前页面的
window.history.state与所记录的值是否一样 - 若一样,则调用
clientApi.goBack(), 否则调用router.back()
参考代码
vue版参考代码如下:
// store
state: {
initialHistoryStateStr: '',
}
mutations: {
setHistoryInitialStateStr(state: State, initialHistoryStateStr: string) {
state.initialHistoryStateStr = initialHistoryStateStr;
},
}
// common.js
if (store.state.initialHistoryStateStr === JSON.stringify(window.history.state)) {
clientApi.goBack();
} else {
router.back();
}
// App
created() {
this.$store.commit('setHistoryInitialStateStr', JSON.stringify(window.history.state));
window.handleClientGoBack = () => {
common.goBack();
};
clientApi.on('back', handleClientGoBack'); // 监听客户端物理返回按键点击
}
原生js版参考代码如下:
window.addEventListener('load', function() {
window.__initialHistoryStateStr = JSON.stringify(window.history.state);
}, false);
const common = {
back() {
const nowHistoryStateStr = JSON.stringify(window.history.state);
if (nowHistoryStateStr === window.__initialHistoryStateStr) {
clientApi.goBack();
} else {
window.history.back();
}
}
}
window.handleClientGoBack = () => {
common.goBack();
};
clientApi.on('goBack', 'handleClientGoBack'); // 监听客户端物理返回按键点击