Skip to content

Instantly share code, notes, and snippets.

@vaebe
Created October 28, 2025 06:10
Show Gist options
  • Select an option

  • Save vaebe/5b70c19824a3cbd8d6548dea9b9e9964 to your computer and use it in GitHub Desktop.

Select an option

Save vaebe/5b70c19824a3cbd8d6548dea9b9e9964 to your computer and use it in GitHub Desktop.
vue3 方调试代码
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