You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vite+SSR集成Mantine UI遇Hydration不匹配错误求助

解决Vite SSR + Mantine UI的Hydration错误步骤

1. 修复Mantine服务端样式收集与注入

Mantine SSR需要主动收集渲染过程中生成的样式并注入到HTML中,当前你的entry-server.ts仅返回HTML字符串,未处理样式收集,这是导致<style>匹配错误的核心原因。

修改entry-server.ts,引入Mantine的样式服务工具:

import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom/server";
import App from "./App";
import { Provider } from "react-redux";
import store from "./redux/store";
import { createStylesServer, ServerStyles } from "@mantine/core";

// 创建样式服务实例
const stylesServer = createStylesServer();

interface IRenderProps {
  path: string;
}

export const render = ({ path }: IRenderProps) => {
  // 包裹组件以收集样式
  const html = ReactDOMServer.renderToString(
    <Provider store={store}>
      <ServerStyles server={stylesServer}>
        <StaticRouter location={path}>
          <App />
        </StaticRouter>
      </ServerStyles>
    </Provider>
  );

  // 返回HTML和提取的样式
  return {
    html,
    head: stylesServer.extractStyles().map(style => 
      `<style data-mantine-styles>${style}</style>`
    ).join(''),
  };
};

2. 确保客户端与服务端Store状态一致

全局复用同一个RTK Store会导致多请求状态污染,且客户端与服务端初始状态不匹配,直接引发Hydration UI差异。

将store.ts改为工厂函数:

// redux/store.ts
import { configureStore } from "@reduxjs/toolkit";
import yourSlice from "./slices/yourSlice";

// 每次调用生成新的Store实例
export const createStore = () => configureStore({
  reducer: {
    your: yourSlice,
  },
});

export type AppStore = ReturnType<typeof createStore>;
export type RootState = ReturnType<AppStore["getState"]>;
export type AppDispatch = AppStore["dispatch"];

更新entry-server.ts:

// entry-server.ts
import { createStore } from "./redux/store";

export const render = ({ path }: IRenderProps) => {
  // 每个请求创建独立Store
  const store = createStore();
  
  const html = ReactDOMServer.renderToString(
    <Provider store={store}>
      <ServerStyles server={stylesServer}>
        <StaticRouter location={path}>
          <App />
        </StaticRouter>
      </ServerStyles>
    </Provider>
  );

  // 返回初始状态用于客户端同步
  return {
    html,
    head: stylesServer.extractStyles().map(style => 
      `<style data-mantine-styles>${style}</style>`
    ).join(''),
    initialState: store.getState(),
  };
};

更新entry-client.ts:

// entry-client.ts
import { createStore } from "./redux/store";

// 从服务端注入的全局变量获取初始状态
const initialState = (window as any).__INITIAL_STATE__;

const store = createStore({
  preloadedState: initialState,
});

ReactDOM.hydrateRoot(
  document.getElementById("root") as HTMLElement, 
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
);

3. 修复server.js的HTML注入逻辑

当前server.js的render调用参数不匹配,且未注入Store初始状态,需同步修改:

// server.js中Serve HTML部分
const rendered = await render({ path: url });

// 注入初始状态到全局变量
const initialStateScript = `<script>window.__INITIAL_STATE__ = ${JSON.stringify(rendered.initialState)}</script>`;

const html = template
  .replace(`<!--app-head-->`, `${rendered.head ?? ''}${initialStateScript}`)
  .replace(`<!--app-html-->`, rendered.html ?? '')

4. 优化Mantine全局样式配置

移除App.ts中重复的全局样式导入,改用MantineProvider的内置配置:

// App.ts
import { MantineProvider } from "@mantine/core";
import Routing from "./routes/Routing";

function App() {
  return (
    // 自动注入全局样式与重置样式
    <MantineProvider withGlobalStyles withNormalizeCSS>
      <Routing />
    </MantineProvider>
  );
}

export default App;

5. 处理异步组件的Hydration边界

若路由中存在依赖异步数据的组件,需用Suspense包裹,避免Hydration时因数据未加载导致UI不匹配:

// routes/Routing.tsx
import { Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

function Routing() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <Routes>
        {/* 你的路由配置 */}
      </Routes>
    </Suspense>
  );
}

export default Routing;

内容的提问来源于stack exchange,提问作者Saurav019

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:09:15