Cef 模式下,Vue 项目 @click 事件偶尔无效问题
Cef 模式:使用 C++ 创建的一个浏览器,有它自己的浏览器内核版本
在 Cef Web 壳子中开发时候,发现 Vue 项目 的 @click 偶尔就无效,一开始还以为电脑卡了,具体分析后发现不是这样的。
原因分析(vue2):
通过打断点分析,看到 @click 事件执行用到了下面代码的判断
if (
e.target === e.currentTarget ||
e.timeStamp >= attachedTimestamp || //产生问题的一行
e.timeStamp <= 0 ||
e.target.ownerDocument !== document
) {
return original.apply(this, arguments);
}
问题产生的原因应该就是 Cef 模式下手动事件时间戳发生了延迟(前几秒一直为 0)
因此我们要想办法让这个判断不利用时间戳执行下去
原因分析(vue3):
通过打断点分析,看到 @click 事件执行用到了下面代码的判断
function createInvoker(initialValue, instance) {
const invoker = (e) => {
const timeStamp = e.timeStamp || _getNow();
if (skipTimestampCheck || timeStamp >= invoker.attached - 1) {
callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */, [e]);
}
};
invoker.value = initialValue;
invoker.attached = getNow();
return invoker;
}
不需要看懂,只需要知道这里也是时间戳的问题
因此我们要想办法让这个判断不利用时间戳执行下去
解决问题:
Vue2
利用 e.target === e.currentTarget
这个判断条件进行验证:
e.currentTarget: 绑定事件的元素
e.target: 实际点击的元素(冒泡,最下面那个元素)
不难分析出,只需要让绑定 @click
的 dom 元素成为 e.target
即可。
使用 css 一个属性即可解决:
/* 绑定 @click 事件的 DOM 元素类名 */
.click-dom > * {
pointer-events: none;
}
Vue3
利用 skipTimestampCheck
这个判断条件进行验证:
想法设法让它变为 true
源代码中设置 skipTimestampCheck
的位置如下:
if (typeof window !== "undefined") {
// Determine what event timestamp the browser is using. Annoyingly, the
// timestamp can either be hi-res (relative to page load) or low-res
// (relative to UNIX epoch), so in order to compare time we have to use the
// same timestamp type when saving the flush timestamp.
if (_getNow() > document.createEvent("Event").timeStamp) {
// if the low-res timestamp which is bigger than the event timestamp
// (which is evaluated AFTER) it means the event is using a hi-res timestamp,
// and we need to use the hi-res version for event listeners as well.
_getNow = () => performance.now();
}
// #3485: Firefox <= 53 has incorrect Event.timeStamp implementation
// and does not fire microtasks in between event propagation, so safe to exclude.
const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i);
skipTimestampCheck = !!(ffMatch && Number(ffMatch[1]) <= 53);
}
所以,在我们的 Vue3 项目中,在 index.html 添加:
<!DOCTYPE html>
<html lang="en">
<head>
....
<script>
// 添加这一行!!!
Object.defineProperty(window.navigator, 'userAgent', {value:'firefox/53'});
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
综上,所有解决办法如下:
- 升级 Cef 版本,相关 issues :issues
- Vue2
/* 绑定 @click 事件的 DOM 元素类名 */
.click-dom > * {
pointer-events: none;
}
- Vue3
<!DOCTYPE html>
<html lang="en">
<head>
....
<script>
// 添加这一行!!!
Object.defineProperty(window.navigator, 'userAgent', {value:'firefox/53'});
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
如有错误或不解,欢迎评论区指正!