webview里的返回事件处理

5,016 阅读2分钟

最近在项目中遇到一个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已经超出了它的权限。在 routerouter 中都没有找到任何能判断当前页面就是打开 webview 时所在的页面的线索,又尝试在 window.history 中找找线索。

window.history 有三个属性:

  • length
  • scrollRestoration
  • state

前面两个用不了,于是尝试下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');  // 监听客户端物理返回按键点击