Created
October 28, 2025 06:10
-
-
Save vaebe/5b70c19824a3cbd8d6548dea9b9e9964 to your computer and use it in GitHub Desktop.
vue3 方调试代码
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export interface AntiDebugOptions { | |
| /** 是否禁用右键菜单 */ | |
| disableContextMenu?: boolean | |
| /** 是否拦截调试快捷键 */ | |
| disableShortcuts?: boolean | |
| /** 是否检测开发者工具 */ | |
| detectDevTools?: boolean | |
| /** 检测间隔(毫秒) */ | |
| checkInterval?: number | |
| /** 性能阈值(毫秒) */ | |
| performanceThreshold?: number | |
| /** 检测到调试时的回调 */ | |
| onDetected?: () => void | |
| } | |
| // 防止调试 | |
| export function useAntiDebug(options: AntiDebugOptions = {}) { | |
| const { | |
| disableContextMenu = true, | |
| disableShortcuts = true, | |
| detectDevTools = true, | |
| checkInterval: interval = 500, | |
| performanceThreshold = 100, | |
| onDetected, | |
| } = options | |
| let checkTimer: ReturnType<typeof setInterval> | undefined | |
| const isDevToolsOpen = ref(false) | |
| // 🚫 禁用右键菜单 | |
| const handleContextMenu = (e: MouseEvent) => { | |
| if (disableContextMenu) { | |
| e.preventDefault() | |
| } | |
| } | |
| // 🚫 拦截调试快捷键 | |
| const handleKeyDown = (e: KeyboardEvent) => { | |
| if (!disableShortcuts) | |
| return | |
| const key = e.key.toUpperCase() | |
| const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform) | |
| // Windows/Linux 快捷键 | |
| const isWindowsDebugKey | |
| = key === 'F12' | |
| || (e.ctrlKey && e.shiftKey && ['I', 'J', 'C'].includes(key)) | |
| || (e.ctrlKey && key === 'U') | |
| // macOS 快捷键 | |
| const isMacDebugKey | |
| = key === 'F12' | |
| || (e.metaKey && e.altKey && ['I', 'J', 'C'].includes(key)) | |
| || (e.metaKey && key === 'U') | |
| if ((isMac && isMacDebugKey) || (!isMac && isWindowsDebugKey)) { | |
| e.preventDefault() | |
| e.stopPropagation() | |
| return false | |
| } | |
| } | |
| // 🔍 多种方法检测开发者工具 | |
| const detectDevToolsOpen = () => { | |
| // 方法1: debugger 性能检测(最可靠) | |
| const start = performance.now() | |
| // eslint-disable-next-line no-debugger | |
| debugger | |
| const end = performance.now() | |
| if (end - start > performanceThreshold) { | |
| return true | |
| } | |
| // 方法2: console.log 检测(利用 getter 触发) | |
| // 只有开发者工具打开时,console.log 才会读取对象属性 | |
| let consoleOpened = false | |
| const detector = Object.create(null, { | |
| toString: { | |
| get() { | |
| consoleOpened = true | |
| return '' | |
| }, | |
| configurable: true, | |
| }, | |
| }) | |
| // 保存原始 console 方法 | |
| // eslint-disable-next-line no-console | |
| const originalLog = console.log | |
| // 临时覆盖 console.log 来静默执行 | |
| // eslint-disable-next-line no-console | |
| console.log = () => {} | |
| // eslint-disable-next-line no-console | |
| console.log(detector) | |
| // eslint-disable-next-line no-console | |
| console.log = originalLog | |
| if (consoleOpened) { | |
| return true | |
| } | |
| // 方法3: 检测 toString 调用次数 | |
| // 开发者工具会多次调用 toString 来格式化显示 | |
| let toStringCallCount = 0 | |
| const proxyObj = new Proxy({}, { | |
| get(target, prop) { | |
| if (prop === Symbol.toStringTag) { | |
| toStringCallCount++ | |
| } | |
| return target[prop as keyof typeof target] | |
| }, | |
| }) | |
| // eslint-disable-next-line no-console | |
| console.log = () => {} | |
| // eslint-disable-next-line no-console | |
| console.log(proxyObj) | |
| // eslint-disable-next-line no-console | |
| console.log = originalLog | |
| if (toStringCallCount > 1) { | |
| return true | |
| } | |
| return false | |
| } | |
| // 🚨 检测到调试时的处理 | |
| const handleDevToolsDetected = () => { | |
| isDevToolsOpen.value = true | |
| if (onDetected) { | |
| onDetected() | |
| } | |
| else { | |
| // 默认行为:清空页面 | |
| document.body.innerHTML = ` | |
| <div style=" | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 100vh; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| margin: 0; | |
| "> | |
| <h1 style="font-size: 3rem; margin-bottom: 1rem;">🚫</h1> | |
| <h2 style="font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem;"> | |
| 检测到调试模式 | |
| </h2> | |
| <p style="font-size: 1rem; opacity: 0.9;"> | |
| Developer Tools Detected | |
| </p> | |
| </div> | |
| ` | |
| // 清空控制台 | |
| // eslint-disable-next-line no-console | |
| console.clear() | |
| // 阻止进一步操作 | |
| if (checkTimer) { | |
| clearInterval(checkTimer) | |
| } | |
| } | |
| } | |
| // 🔄 定期检测 | |
| const runDetection = () => { | |
| if (!detectDevTools || isDevToolsOpen.value) | |
| return | |
| if (detectDevToolsOpen()) { | |
| handleDevToolsDetected() | |
| } | |
| } | |
| // 🎬 开始监控 | |
| const startMonitoring = () => { | |
| if (disableContextMenu) { | |
| window.addEventListener('contextmenu', handleContextMenu) | |
| } | |
| if (disableShortcuts) { | |
| window.addEventListener('keydown', handleKeyDown, true) | |
| } | |
| if (detectDevTools) { | |
| checkTimer = setInterval(runDetection, interval) | |
| // 立即执行一次检测 | |
| runDetection() | |
| } | |
| } | |
| // 🛑 停止监控 | |
| const stopMonitoring = () => { | |
| window.removeEventListener('contextmenu', handleContextMenu) | |
| window.removeEventListener('keydown', handleKeyDown, true) | |
| if (checkTimer) { | |
| clearInterval(checkTimer) | |
| checkTimer = undefined | |
| } | |
| } | |
| startMonitoring() | |
| onBeforeUnmount(stopMonitoring) | |
| return { | |
| isDevToolsOpen, | |
| startMonitoring, | |
| stopMonitoring, | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment