跳出 React 视角, 利用 Head 标签脚本实现暗黑模式切换

起初我用 useEffectuseRef 管理暗黑模式切换, 后来换成执行更早的 useLayoutEffect, 希望能缓解 React 首次渲染时的白屏跳黑屏问题.

不过我始终没跳出 React 的框架去思考. 直到后来, 我才意识到, 其实完全可以在 <head> 标签中插入一段原生 <script>, 在 DOM 加载前设置好 document.documentElement.classList.add('dark').

;(function () {
  var darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
  var userPreferredTheme

  function setTheme(t) {
    window.__theme = t

    if (t === 'dark') {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
  }

  try {
    userPreferredTheme = localStorage.getItem('theme')
  } catch (_) {}

  if (!userPreferredTheme) {
    userPreferredTheme = darkModeQuery.matches ? 'dark' : 'light'
  }

  setTheme(userPreferredTheme)

  darkModeQuery.addEventListener('change', function (e) {
    if (!userPreferredTheme) {
      setTheme(e.matches ? 'dark' : 'light')
    }
  })

  window.__setPreferTheme = function (t) {
    userPreferredTheme = t
    setTheme(t)

    try {
      localStorage.setItem('theme', t)
    } catch (_) {}
  }
})()