89 lines
3.1 KiB
JavaScript
89 lines
3.1 KiB
JavaScript
// frontend/composables/useAliveGuard.js
|
|
//
|
|
// 用途:UniApp iOS WebView 的 JS Framework 在原生异步回调回来时,如果页面已销毁,
|
|
// 调 n.fireEvent 会报 "undefined is not an object (evaluating 'n.fireEvent')"
|
|
// (warning 级别,不影响原生操作结果,但日志噪音大且难定位)
|
|
//
|
|
// 解法:在 setup() 里创建 alive 守卫,页面卸载/组件销毁时翻成 false;
|
|
// 所有原生异步回调(uni.* / plus.io.* / plus.nativeObj.* 的 success/fail/complete)
|
|
// 包一层 guard,守卫死亡后直接吞掉结果,不更新 state、不弹 toast、不继续后续链式调用。
|
|
//
|
|
// 注意:我们不取消原生操作本身 — 用户已经触发了上传/保存/文件读取,这些操作的实际副作用
|
|
// (网络请求发出、文件落盘)应该正常完成。只是 JS 这一侧不应当再触碰一个已死页面的 state。
|
|
//
|
|
// 用法:
|
|
// import { useAliveGuard } from '@/composables/useAliveGuard.js'
|
|
//
|
|
// const { isAlive, die, guard } = useAliveGuard()
|
|
//
|
|
// uni.uploadFile({
|
|
// url, filePath,
|
|
// success: guard((res) => { /* 仅 alive=true 时执行 */ }),
|
|
// fail: guard((err) => { /* 同上 */ }),
|
|
// complete: guard(() => { /* 同上 */ })
|
|
// })
|
|
//
|
|
// // plus.io 链式回调:
|
|
// plus.io.resolveLocalFileSystemURL(
|
|
// path,
|
|
// guard((entry) => { entry.file(guard((file) => { ... })) }),
|
|
// guard((err) => { ... })
|
|
// )
|
|
//
|
|
// // 主动检查:
|
|
// if (!isAlive()) return
|
|
//
|
|
// 自动绑定生命周期:
|
|
// - 页面上下文(setup 里当前实例是 page):绑定 onUnload(页面关闭时死)
|
|
// - 组件上下文(setup 里当前实例是 component):绑定 onBeforeUnmount(v-if=false / 父组件销毁时死)
|
|
// - 两者都不存在(罕见):返回的 alive 永远为 true(等同不守卫,留给调用方自行 die())
|
|
|
|
import { onBeforeUnmount } from 'vue'
|
|
import { onUnload } from '@dcloudio/uni-app'
|
|
|
|
/**
|
|
* @returns {{
|
|
* isAlive: () => boolean,
|
|
* die: () => void,
|
|
* guard: <T extends Function>(fn: T) => T
|
|
* }}
|
|
*/
|
|
export function useAliveGuard() {
|
|
let alive = true
|
|
let bound = false
|
|
|
|
// 优先 onUnload(页面级生命周期,从 @dcloudio/uni-app 导入)
|
|
// 页面里调 onUnload 是注册的页面 onUnload 钩子;组件里调可能抛 "onUnload is not a function"
|
|
try {
|
|
onUnload(() => { alive = false })
|
|
bound = true
|
|
} catch (_) {
|
|
/* 组件上下文,onUnload 不可用,下面兜底 */
|
|
}
|
|
|
|
// 兜底 onBeforeUnmount(Vue 组件级生命周期)
|
|
if (!bound) {
|
|
try {
|
|
onBeforeUnmount(() => { alive = false })
|
|
} catch (_) {
|
|
/* 都不在 setup 里调,保持 alive=true,调用方自行 die() */
|
|
}
|
|
}
|
|
|
|
return {
|
|
isAlive: () => alive,
|
|
/** 主动翻成 false(罕见用 — 大多数场景生命周期钩子已经处理) */
|
|
die: () => { alive = false },
|
|
/**
|
|
* 包装回调:页面/组件已死时 no-op,活着时透传执行
|
|
* 非函数参数原样返回,方便链式写:guard(maybeUndefined)
|
|
*/
|
|
guard(fn) {
|
|
if (typeof fn !== 'function') return fn
|
|
return function guardedCallback(...args) {
|
|
if (!alive) return undefined
|
|
return fn.apply(this, args)
|
|
}
|
|
}
|
|
}
|
|
} |