移动端 h5 实现 webview 退出时弹窗

在移动端开发时,一个很常见的情况是客户端开启一个 webview 来展示 h5 页面,端上会给这个 webview 提供一个返回按钮等功能,当点按返回没有历史记录时,就会退出当前 webview。PM 有时会有需求在退出 h5 应用时弹窗提示用户,在用户点击确认后,才真正退出,这里提供一个实现这个需求的思路。

难点

实现这个方案最大的难点,在于 h5 不能正确知道 webview 被关闭了。即便知道,也没有办法阻止 webview 关闭。

解决这个问题的一个最正常的方案,当然是让端上同学给我们加个端能力来调用,但是端开发需要周期,且需要发新版本才能支持,往往不能满足要求。那么还有什么其他方案呢?

解决方案

利用前端router,我们可以在每次页面打开时,先 replace 进入到一个 base 页面,之后再 push 进入到首页。这样在浏览器历史记录里第一个页面变成了 base。这样用户不断点返回,在退出 webview 之前,会先进入到 base 页面。只要进入 base 页面,我们就 push 到首页,从而使得一直有浏览记录,阻止了 webview 的退出。

这个解决方案目前仅在手机百度app中进行过测试,不保证兼容性,只是提供一个思路。

实现

简单实现代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import router from './router.js';

const basePath = Math.random().toString(36).substr(2, 12);
let isFirstPage = true;
let firstPageHash = null;

export function init(callback) {

router

// 这个页面作为 base 页面,是唯一入口
// 从其他 url 进入的也要先跳转到这个页面
.add(new RegExp(basePath), () => {
if (isFirstPage) {
setTimeout(() => {
isFirstPage = false;
router.push(firstPageHash);
}, 300);
}
else {
router.push(firstPageHash);
callback();
}
})

.add(() => {
if (isFirstPage) {
firstPageHash = location.hash;
router.replace('/' + basePath);
}
})
.listen();
}

这里为了简单我们使用了上次实现的简易 router

为了防止跟其他hash值冲突,这里我们随机生成了一个 basePath,任何页面进来,如果时首页,都先 replace 到 basePathrouter.replace('/' + basePath);

这里有个要注意的事项,就是如果我们是第一次加载页面,使用window.location.hash = path;方式修改hash,是不能留下历史记录的,猜测浏览器做了特殊处理。因此需要延迟一定时间再返回首页。

如果不是第一次打开时进入的 basePath,那么可以调用 init 时传入的回调函数,我们可以在这个回调函数里添加打开弹窗等逻辑。

如何退出?

别的端能力可以没有,但是关闭 webview 总归会有端能力吧 😓。

好吧,如果没有,我们可以再加一个参数,设置要退出的标记 shouldExit = 1basePath中判断shouldExit === 1时,不再执行push(),而是执行一次back()。我这里就直接调用关闭 webview 端能力了~

最后还是要强调一下这个方法只在我厂的 app 中使用过,其他 app 不保证可用👻。