Nuxt3 SSR环境下检测prefers-color-scheme并设置data-mode(解决window未定义)
在Nuxt3 SSR中服务端设置html的data-mode属性(基于系统主题偏好)
完全可以实现需求:将系统主题偏好传递给服务端,在返回HTML前为根节点设置data-mode属性,避免客户端切换导致的闪烁问题,同时解决SSR时window/localStorage未定义的错误。
核心思路
- 服务端优先通过浏览器请求头获取用户的系统主题偏好(现代浏览器支持
Sec-CH-Prefers-Color-Scheme头); - 后续请求通过Cookie存储用户的主题选择(包括系统主题或自定义主题),确保服务端能稳定获取;
- 利用Nuxt3的服务端API(
useRequestHeaders/useCookie/useHtmlAttrs)在服务端渲染阶段直接设置html的属性; - 客户端挂载后同步主题到
localStorage并监听系统主题变化,同时更新Cookie保证后续请求一致性。
具体实现代码(app.vue中直接处理)
<script setup> import { useRequestHeaders, useHtmlAttrs, useCookie } from '#imports' // 服务端获取主题:Cookie > 请求头 > 默认浅色 const headers = useRequestHeaders(['sec-ch-prefers-color-scheme']) const themeCookie = useCookie('theme') const theme = themeCookie.value || headers['sec-ch-prefers-color-scheme'] || 'light' // 服务端设置html的data-mode属性 useHtmlAttrs({ 'data-mode': theme }) // 客户端挂载后处理:同步到localStorage + 监听系统主题变化 onMounted(() => { // 首次加载同步主题到localStorage if (!localStorage.getItem('theme')) { localStorage.setItem('theme', theme) } // 监听系统主题切换,同步更新本地存储、DOM属性和Cookie const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const handleThemeChange = (e) => { const newTheme = e.matches ? 'dark' : 'light' localStorage.setItem('theme', newTheme) document.documentElement.setAttribute('data-mode', newTheme) document.cookie = `theme=${newTheme}; path=/; max-age=31536000` } mediaQuery.addEventListener('change', handleThemeChange) onUnmounted(() => mediaQuery.removeEventListener('change', handleThemeChange)) }) </script>
备选方案:用Server Middleware统一处理
如果需要全局统一管理主题逻辑,可以创建服务端中间件:
- 在
server/middleware/theme.ts中添加:
export default defineEventHandler((event) => { const themeCookie = getCookie(event, 'theme') const themeHeader = getHeader(event, 'sec-ch-prefers-color-scheme') const theme = themeCookie || themeHeader || 'light' // 将主题存入请求上下文,供页面组件使用 event.context.theme = theme // 首次请求设置Cookie,后续请求复用 if (!themeCookie) { setCookie(event, 'theme', theme, { path: '/', maxAge: 31536000 // 1年有效期 }) } })
- 在
app.vue中获取上下文主题并设置属性:
<script setup> import { useHtmlAttrs, useRequestEvent } from '#imports' const event = useRequestEvent() const theme = event?.context.theme || 'light' useHtmlAttrs({ 'data-mode': theme }) // 客户端挂载后的处理同上 </script>
关键注意事项
- 避免在服务端代码中直接使用
window/localStorage等客户端API,这些仅在浏览器环境可用; Sec-CH-Prefers-Color-Scheme是浏览器主动发送的客户端提示头,现代浏览器(Chrome 81+、Firefox 95+等)均支持,老旧浏览器会 fallback 到默认主题;- 用Cookie存储主题能保证服务端每次请求都能获取到正确的主题值,确保SSR渲染的页面与客户端状态一致。
内容的提问来源于stack exchange,提问作者sadeq shahmoradi
相关产品推荐
相关产品推荐

