blog.cover

Nuxt主题加载/切换闪烁解决方法

经验之道!

浏览468
评论0
2025年12月13日

闪烁问题

如果你用的是SSR,可能会遇到这种情况,在第一次进入网页或是刷新后,主题从默认主题闪烁一下变到当前主题(比如浅色→深色)。


出现闪烁问题是因为SSR环节,Nuxt一侧没有客户端环境,没有DOM也没有LocalStorage去检查并应用主题,导致主题相关的逻辑只能在浏览器运行时上边跑,而不是让服务端直接吐出来一个包含已应用主题的骨架。

所以,问题的关键在于如何把主题检查的逻辑跑在服务端。

这得分为两种主题实现来说(虽然说底层差不多的:

  • ThemeClass (.dark or .xxxx 动态添加类名切换主题)

  • 媒体查询 (window.matchMedia)

解决主题闪烁的方法

第一种 Head标签内联JS

先丢出来实现代码(这是网上用的最多的方法)

ThemeClass的版本↓

Javascript
      // IIFE 避免污染全局
(function () {
  function initTheme() {
    const _theme = localStorage.getItem('theme') || 'dark';
    document.documentElement.classList.toggle('dark', _theme === 'dark');
  }
  // 如果你用了
  if (_theme === 'dark') {
      document.documentElement.setAttribute('arco-theme', 'dark');
  } else {
      document.documentElement.removeAttribute('arco-theme');
  }
  initTheme()
})();
    

媒体查询的版本↓

Javascript
      // IIFE 避免污染全局
(function () {
  function initTheme() {
    const _theme =
      window.matchMedia &&
      window.matchMedia("(prefers-color-scheme: dark)").matches
        ? "dark"
        : "light";
    document.documentElement.classList.toggle('dark', _theme === 'dark');
  }
  // 如果你用了
  if (_theme === 'dark') {
      document.documentElement.setAttribute('arco-theme', 'dark');
  } else {
      document.documentElement.removeAttribute('arco-theme');
  }
  initTheme()
})();
    

这里有两种内联方式

Javascript
      // nuxt.config,js
app: {
    head: {
        script: [
            {
                type: 'text/javascript',
                children: "刚刚的脚本填这里"
            }
            // 或者单独放到一个文件里
            {
                src: "脚本文件路径"
            }
        ]
    }
}
    

第二种 useHead()

首先得讲讲useHead。useHead支持 去重、合并、优先级控制(避免重复 meta 标签), 并且它有多种行为模式:

场景运行环境行为
SSR(服务端渲染 / 水合前)Node.js在服务端收集 head 数据,注入到服务器渲染的骨架里
CSR(客户端渲染 / 水合后)浏览器在浏览器中执行,动态操作document.head
静态站点生成(SSG)Node.js(构建时)构建阶段运行,生成带正确 head 的静态 HTML 文件

最重要的是,在服务段渲染的时候它不依赖DOM,只对虚拟DOM操作,这意味着服务端可以通过useHead的方式来实现 documentElement.setAttribute('arco-theme', 'dark') 这种操作。水合后骨架上已经有对应的标签了,不会重复运行document.head。

那么主题变量的持久化如何解决呢?答案是Pinia Plugin Persistedstate,这个插件会把piniaStore的变量持久化到cookie里面,注意这个不要用多了。

Javascript
      // 主题控制
const theme = computed(()=> themeMode)

useHead({
    bodyAttrs: {
        'arco-theme': theme,
    },
    htmlAttrs: {
        class: theme,
    }
})

    

效果优劣

我用下来的感受是,useHead添加属性>内联JS。前者没有一丝浏览器运行时损失,是直接服务端添加到标签上的,意味着在水合前主题就已经挂载上去了。而后者内联JS依赖浏览器运行时,并且有时会失效,尤其是第一次加载页面的时候,首屏体验差。所以最后我博客用的是useHead+Pinia 这个方法。

Nuxt主题加载/切换闪烁解决方法
作者 NoelOrin
发布时间2025年12月13日
许可协议 CC BY-NC-SA 4.0