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
相关产品推荐
相关产品推荐

