You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt3 SSR环境下检测prefers-color-scheme并设置data-mode(解决window未定义)

在Nuxt3 SSR中服务端设置html的data-mode属性(基于系统主题偏好)

完全可以实现需求:将系统主题偏好传递给服务端,在返回HTML前为根节点设置data-mode属性,避免客户端切换导致的闪烁问题,同时解决SSR时window/localStorage未定义的错误。

核心思路

  1. 服务端优先通过浏览器请求头获取用户的系统主题偏好(现代浏览器支持Sec-CH-Prefers-Color-Scheme头);
  2. 后续请求通过Cookie存储用户的主题选择(包括系统主题或自定义主题),确保服务端能稳定获取;
  3. 利用Nuxt3的服务端API(useRequestHeaders/useCookie/useHtmlAttrs)在服务端渲染阶段直接设置html的属性;
  4. 客户端挂载后同步主题到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统一处理

如果需要全局统一管理主题逻辑,可以创建服务端中间件:

  1. 在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年有效期
    })
  }
})
  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:55:14