You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
31 lines
893 B
31 lines
893 B
import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'
|
|
|
|
/**
|
|
* 响应式地跟踪 CSS 媒体查询匹配状态。
|
|
*
|
|
* SSR 安全:服务端渲染期间始终返回 `false`,避免 hydration mismatch;
|
|
* 客户端 `onMounted` 之后才订阅 `matchMedia`。
|
|
*
|
|
* @param query - 合法 CSS media query 字符串
|
|
* @returns Ref<boolean> - 当前是否匹配
|
|
*/
|
|
export function useMediaQuery(query: string): Ref<boolean> {
|
|
const matches = ref(false)
|
|
let mql: MediaQueryList | null = null
|
|
const handler = (e: MediaQueryListEvent) => {
|
|
matches.value = e.matches
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (typeof window === 'undefined' || !window.matchMedia) return
|
|
mql = window.matchMedia(query)
|
|
matches.value = mql.matches
|
|
mql.addEventListener('change', handler)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
mql?.removeEventListener('change', handler)
|
|
})
|
|
|
|
return matches
|
|
}
|
|
|