tusen-ai / naive-ui

A Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.
https://www.naiveui.com
MIT License
16.04k stars 1.67k forks source link

nuxt 3 dark mode refresh page broken #3765

Closed productdevbook closed 2 years ago

productdevbook commented 2 years ago

TuSimple/naive-ui version (版本)

2.33.3

Vue version (Vue 版本)

3.2.39

Browser and its version (浏览器及其版本)

Version 105.0.5195.125 (Official Build) (64-bit)

System and its version (系统及其版本)

ubuntu 22

Node version (Node 版本)

16

Reappearance link (重现链接)

https://github.com/productdevbook/oku-nuxt3-template

Reappearance steps (重现步骤)

yarn install yarn dev

open -> http://localhost:3000/naive dark mode select refresh page

Expected results (期望的结果)

dark mode working

Actual results (实际的结果)

half dark half white mode

Remarks (补充说明)

image

and white this problem

image

07akioni commented 2 years ago

In SSR Mode. colorMode is always light. It results in the SSR output using light theme variables & causes the style glitch.

If you want SSR page output correct result. You need to make theme a param of URL.

image
levidavidmurray commented 1 year ago

I don't really care if the SSR output respects the client's color scheme, but I'd at least like it to hydrate properly once rendered client side. This doesn't always seem to work as expected for some naive-ui components.

image

As you can see here, the SSR NButton is rendered with the light theme, while the Client Only NButton is rendered with dark (wrapped in ClientOnly tag). In my experience, the only naive-ui components that are hydrated correctly are those that are forced to load/reload client side (e.g. relying on async data or wrapped in ClientOnly tag).

I'm sure there's a far better method for getting around this, but my simple hack was adding a plugin to hook into the page:finish lifecycle hook, and then simply toggle dark mode off and on (only if already enabled):

// plugins/setTheme.client.ts

export default defineNuxtPlugin((nuxtApp) => {

    const isDark = useDark()

    // SSR is causing dark mode hydration issues with naive-ui
    // This fix quickly toggles dark mode on 'page:finish' hook
    nuxtApp.hook('page:finish', () => {
        setTimeout(() => {
            const prefersDark = usePreferredDark()
            isDark.value = false

            setTimeout(() => {
                isDark.value = prefersDark.value
            }, 0)
        }, 0)
    })

    return { 
        provide: {
            isDark
        }
    }

})
<!-- app.vue -->

<template>
<div>
    <nuxt-layout>
        <n-config-provider :theme="$isDark ? darkTheme : lightTheme">
            <n-message-provider>
                <n-dialog-provider>
                    <nuxt-page />
                </n-dialog-provider>
            </n-message-provider>
        </n-config-provider>
    </nuxt-layout>
</div>
</template>

<script lang="ts" setup>
import { NMessageProvider, NConfigProvider, NDialogProvider } from 'naive-ui';
import { darkTheme, lightTheme } from 'naive-ui';

const { $isDark } = useNuxtApp()

</script>
byronogis commented 5 months ago

I think this is still a bug. When the server renders, and then sends it to the client to render as well, the client actually gets the color pattern responsively and passes the prop to the NConfigProvider component accordingly, but the components don't respond to the change correctly.

M1CK431 commented 1 week ago

Alternative workaround:

<NConfigProvider :theme>
  <!-- your code here -->
</NConfigProvider>
import { darkTheme } from "naive-ui"

const theme = ref(null)
watchPostEffect(
  () => theme.value = colorMode.value === "dark" ? darkTheme : null
)

The trick is the delayed theme change by watchPostEffect.