next-themes两行代码就能解决主题切换!

next-themes是在next.js应用中解决主题色切换的方案,目前在 github 已经有4.4k star了。

它的用法也很简单,毕竟他的口号就是两行代码完美解决暗色模式,那么现在我们就来看看吧

本文是在 @next14 的环境,即 app router

如果是 page router,请去官网自助食用

用法

首先安装依赖

bash 复制代码
$ npm install next-themes
# or
$ yarn add next-themes

然后在layout中使用,这里suppressHydrationWarning其实就是帮我们处理添加data-theme,以方便我们切换主题

jsx 复制代码
// app/layout.jsx
import { ThemeProvider } from 'next-themes'

export default function Layout({ children }) {
  return (
    <html suppressHydrationWarning>
      <head />
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

// 如果你是 tailwind,那么需要添加 attribute="class" 属性
// 具体参考 https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-tailwind

接着,我们通过api setTheme切换主题;不难发现,它是通过useTheme()解构出来的,使用很方便

jsx 复制代码
import { useTheme } from 'next-themes'

const ThemeChanger = () => {
  const { theme, setTheme } = useTheme()

  return (
    <div>
      当前主题是: {theme}
      <button onClick={() => setTheme('light')}>亮色模式</button>
      <button onClick={() => setTheme('dark')}>暗色模式</button>
    </div>
  )
}

需要注意的是,当我们setTheme时,将会改变全局的主题色,也就是说其他页面的主题也会被我们影响到,因为上面我们说过,它的原理其实还是在html标签上修改data-theme。那么如果我们希望某个独特的页面固定为暗色主题,该怎么办呢?

下面就要开始介绍ThemeProvider的属性了,这里仅挑选重要的几个属性来讲~

  • storageKey = 'theme': 在 storage 里存储的 key,也就是说当我们 setTheme 时,也会在 localStoreage 里存储

  • forcedTheme: 上面的问题正需要这个属性了,这个属性是当前页面的强制主题色,使用方式如下

    jsx 复制代码
    function Providers({ children }) {
        const isForceDark = '根据路径或者其他方式判断'
        return(
         <ThemeProvider
          attribute="class"
          themes={['light', 'dark']}
          forcedTheme={isForceDark ? 'dark' : null}
         >
           {children}
         </ThemeProvider>
        )
    }
  • enableSystem = true: 是否跟随系统设置切换亮暗色

Hydration Error

在过去的一个月里,我的Next应用遇到这样一个问题:本地开发没有报错,但部署上线后却报了 10 个错误,打开一看全是hydration相关的

我们知道,hydration报错本质上就是ssrcsr不匹配,但线上报错本地不报错,着实让我摸不着头脑。这时组长给我明确了一个方向就是:ssr 的数据异常

还别说,打开network查看服务端渲染返回的HTML,还真找到了我的代码纰漏。不过修复后却依然存在这个报错,这是为什么呢?

原来就是这个next-themes库导致的问题,因为当 ssr 的时候其实拿不到theme此时为空,所以需要在 ssr 的时候返回 null,等到 csr 的时候再返回真正的UI

jsx 复制代码
import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'

const ThemeSwitch = () => {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

  // useEffect only runs on the client, so now we can safely show the UI
  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return null
  }

  return (
    <select value={theme} onChange={e => setTheme(e.target.value)}>
      <option value="system">System</option>
      <option value="dark">Dark</option>
      <option value="light">Light</option>
    </select>
  )
}

export default ThemeSwitch
相关推荐
anyup_前端梦工厂2 小时前
了解几个 HTML 标签属性,实现优化页面加载性能
前端·html
前端御书房2 小时前
前端PDF转图片技术调研实战指南:从踩坑到高可用方案的深度解析
前端·javascript
2301_789169542 小时前
angular中使用animation.css实现翻转展示卡片正反两面效果
前端·css·angular.js
风口上的猪20153 小时前
thingboard告警信息格式美化
java·服务器·前端
程序员黄同学4 小时前
请谈谈 Vue 中的响应式原理,如何实现?
前端·javascript·vue.js
爱编程的小庄4 小时前
web网络安全:SQL 注入攻击
前端·sql·web安全
宁波阿成5 小时前
vue3里组件的v-model:value与v-model的区别
前端·javascript·vue.js
柯腾啊5 小时前
VSCode 中使用 Snippets 设置常用代码块
开发语言·前端·javascript·ide·vscode·编辑器·代码片段
weixin_535854225 小时前
oppo,汤臣倍健,康冠科技,高途教育25届春招内推
c语言·前端·嵌入式硬件·硬件工程·求职招聘
扣丁梦想家5 小时前
设计模式教程:装饰器模式(Decorator Pattern)
java·前端·装饰器模式