如何修复 NextJS 中的深色模式背景颜色闪烁?

问题描述 投票:0回答:3

所以我的问题是 Next.js 无法在客户端访问

localStorage
,因此将发送默认情况下具有或不具有
class="dark"
的 HTML。

这意味着,当用户重新加载页面时,

<html>
会暂时没有
class="dark"
,导致浅色背景颜色闪烁,然后执行一些 javascript 并将
class="dark"
添加到
<html>
。如果我使用
class="dark"
发送 HTML,则会出现同样的问题,但情况相反:然后,在
class="dark"
<html>
中删除之前,浅色模式用户将体验到深色背景颜色的闪烁。

有没有办法在页面渲染之前执行一些javascript?然后我就可以根据用户的 class="dark"

 添加或不添加 
<html>
localStorage

javascript reactjs next.js themes user-experience
3个回答
5
投票
当然,将包含以下内容的

noflash.js

 文件添加到您的公共目录

(function () { // Change these if you use something different in your hook. var storageKey = 'darkMode'; var classNameDark = 'dark-mode'; var classNameLight = 'light-mode'; function setClassOnDocumentBody(darkMode) { document.body.classList.add(darkMode ? classNameDark : classNameLight); document.body.classList.remove(darkMode ? classNameLight : classNameDark); } var preferDarkQuery = '(prefers-color-scheme: dark)'; var mql = window.matchMedia(preferDarkQuery); var supportsColorSchemeQuery = mql.media === preferDarkQuery; var localStorageTheme = null; try { localStorageTheme = localStorage.getItem(storageKey); } catch (err) {} var localStorageExists = localStorageTheme !== null; if (localStorageExists) { localStorageTheme = JSON.parse(localStorageTheme); } // Determine the source of truth if (localStorageExists) { // source of truth from localStorage setClassOnDocumentBody(localStorageTheme); } else if (supportsColorSchemeQuery) { // source of truth from system setClassOnDocumentBody(mql.matches); localStorage.setItem(storageKey, mql.matches); } else { // source of truth from document.body var isDarkMode = document.body.classList.contains(classNameDark); localStorage.setItem(storageKey, JSON.stringify(isDarkMode)); } })(); // https://github.com/donavon/use-dark-mode/blob/develop/noflash.js.txt
然后,将以下 

script src

 标签添加到包含在 
Head
 文件的 
pages/_document
 类中的返回内容中

import Document, { Head, Html, Main, NextScript, DocumentContext } from 'next/document'; class MyDocument extends Document { static async getInitialProps(ctx: DocumentContext) { const initialProps = await Document.getInitialProps(ctx); return { ...initialProps }; } render() { return ( <Html lang='en-US'> <Head> <meta charSet='utf-8' /> <script type="text/javascript" src='/noflash.js' /> </Head> <body className='loading'> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
上述方法有效,但以下方法与 Nextv10+ 完美配合。它只需要将以下配置添加到您的根 next.config.js 文件中。

next.config.js


module.exports = { env: { noflash: fs.readFileSync('/noflash.js').toString() } }
然后,在您的 

pages/_document

 文件中更改以下脚本标记,如下所示

before


// <Head> <meta charSet='utf-8' /> <script type="text/javascript" src='/noflash.js' /> </Head> //

after


// <Head> <meta charSet='utf-8' /> <script type="text/javascript" dangerouslySetInnerHTML={{ __html: process.env.noflash}} /> </Head> //

链接到我使用第一种方法的存储库(从 2020 年秋季开始,在 tailwindcss 内置深色模式支持之前)


1
投票
我的解决方法是在第一次渲染中进行条件渲染,

function MyApp() { const [theme, setTheme] = useState(null); useEffect(() => { let theme = localStorage.getItem('theme') || 'light'; setTheme(theme); }, []); if (!theme) { return; // `theme` is null in the first render } return ( <Component {...pageProps} /> ); }
更多详情请看

我的回答


0
投票
我相信我在这篇中等文章中找到了一个更简单的解决方案:

https://kulembetov.medium.com/preventing-flash-of-unstyled-content-fouc-in-next-js-applications-61b9a878f0f7

基本上,它包括添加visibility:hidden;默认情况下添加到 body 元素,然后在安装主布局后将可见性添加回客户端(例如: document.body.classList.add('body-visible');

© www.soinside.com 2019 - 2024. All rights reserved.