必须用微任务,因为路由变化时DOM尚未更新完成,直接聚焦会失败;微任务确保在DOM更新完毕但浏览器未重绘前执行焦点设置,此时main或首个可聚焦元素已渲染且可见。

在单页应用(SPA)中,路由切换后自动将焦点重置到页面主区域(如 main 或首个可聚焦元素),是提升无障碍体验(a11y)的关键实践。由于路由变化通常不触发完整页面刷新,需手动控制焦点。使用微任务(microtask)能确保焦点逻辑在 DOM 更新完成后、浏览器绘制前执行,避免因 DOM 尚未就绪导致 focus() 失效。
为什么必须用微任务?
Vue Router、React Router 等库的导航钩子(如 onBeforeRouteUpdate、useEffect)或组件挂载时机,往往发生在 DOM 更新开始前或过程中。此时调用 element.focus() 可能失败(返回 false),尤其当目标元素尚未被渲染或未脱离 display: none 状态。微任务(如 Promise.resolve().then() 或 queueMicrotask())能将其延迟到当前宏任务末尾、DOM 渲染前的下一个微任务阶段——此时新路由对应的组件已挂载、DOM 已更新完毕,但浏览器尚未重绘,是设置焦点的黄金时机。
通用实现方式(兼容 Vue/React/Svelte)
核心思路:监听路由变化 → 在微任务中查找并聚焦目标元素。
- 优先聚焦语义化主内容区:
document.querySelector('main') || document.querySelector('[role="main"]') - 若无
main,退而求其次聚焦首个可聚焦元素:document.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])') - 聚焦前确保元素已渲染且可见(检查
offsetParent !== null和getComputedStyle(el).display !== 'none') - 使用
queueMicrotask()(推荐)或Promise.resolve().then()包裹聚焦逻辑
Vue Router 示例(组合式 API)
在布局组件或路由守卫中:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
立即学习“Java免费学习笔记(深入)”;
import { onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute()
const focusMainContent = () => {
queueMicrotask(() => {
const main = document.querySelector('main') || document.querySelector('[role="main"]')
if (main && main.offsetParent !== null) {
main.setAttribute('tabindex', '-1')
main.focus()
return
}
// 退选首个可聚焦元素
const focusable = document.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
if (focusable && focusable.offsetParent !== null) {
focusable.focus()
}
})
}
// 路由变更时触发
watch(() => route.path, focusMainContent, { immediate: true })
return {}
}
}
React Router v6 示例
封装自定义 Hook:
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
export function useFocusOnRouteChange() {
const location = useLocation()
useEffect(() => {
const focusMain = () => {
queueMicrotask(() => {
const main = document.querySelector('main') || document.querySelector('[role="main"]')
if (main && main.offsetParent !== null) {
main.tabIndex = -1
main.focus()
return
}
const firstFocusable = document.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
if (firstFocusable && firstFocusable.offsetParent !== null) {
firstFocusable.focus()
}
})
}
focusMain()
}, [location])
}
// 在 Layout 组件中使用
function Layout({ children }) {
useFocusOnRouteChange()
return <div>{children}</div>
}
不复杂但容易忽略 —— 微任务不是锦上添花,而是让焦点重置真正生效的必要调度机制。


















