Hydrogen项目中基础MUI组件在TypeScript环境下无法运行
在Shopify Hydrogen项目中集成Material UI的解决方案
我在创建Shopify Hydrogen(React)项目时,引入Material UI库开发,使用以下基础代码:
import * as React from 'react'; import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import Grid from '@mui/material/Grid'; const Item = styled(Paper)(({ theme }) => ({ backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff', ...theme.typography.body2, padding: theme.spacing(1), textAlign: 'center', color: theme.palette.text.secondary, })); export default function Home() { return ( <Box sx={{ flexGrow: 1 }}> <Grid container spacing={2}> <Grid item xs={8}> <Item>xs=8</Item> </Grid> <Grid item xs={4}> <Item>xs=4</Item> </Grid> <Grid item xs={4}> <Item>xs=4</Item> </Grid> <Grid item xs={8}> <Item>xs=8</Item> </Grid> </Grid> </Box> ) }
运行后出现以下错误:
Warning: Only plain objects can be passed to client components from server components. Classes or other objects with methods are not supported. Remove sheet from these props: {key: "css", sheet: {_insertTag: function, isSpeedy: false, tags: [...], ctr: 0, nonce: undefined, key: "css", container: undefined, ...}, nonce: undefined, inserted: {...}, registered: {...}, insert: function} Error processing route: http://localhost:3000/rx Error: Functions cannot be passed directly to client components because they're not serializable. Remove _insertTag (function) from this object, or avoid the entire object: {_insertTag: function, isSpeedy: false, tags: [...], ctr: 0, nonce: undefined, key: "css", container: undefined, ...} at resolveModelToJSON (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1515:13) at StyleSheet.toJSON (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1029:14) at stringify (<anonymous>) at processModelChunk (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:164:14) at retryTask (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1625:26) at performWork (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1658:7) at eval (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1755:12) at scheduleWork (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:58:3) at startWork (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1754:3) at Object.start (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1866:7) at setupReadableByteStreamController (node:internal/webstreams/readablestream:3110:23) at setupReadableByteStreamControllerFromSource (node:internal/webstreams/readablestream:3147:3) at new ReadableStream (node:internal/webstreams/readablestream:250:7) at Module.renderToReadableStream (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite-writer.browser.server.js?v=fa4e9613:1863:16) at runRSC (/node_modules/@shopify/hydrogen/dist/esnext/entry-server.js?v=fa4e9613:598:46) at processRequest (/node_modules/@shopify/hydrogen/dist/esnext/entry-server.js?v=fa4e9613:200:15) at handleRequest (/node_modules/@shopify/hydrogen/dist/esnext/entry-server.js?v=fa4e9613:163:26) /.../node_modules/yoga-layout-prebuilt/yoga-layout/build/Release/nbind.js:53 throw ex; ^ TypeError: Cannot read properties of undefined (reading 'null') at getOrCreateServerContext (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite.js?v=fa4e9613:116:23) at resolveProvider (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite.js?v=fa4e9613:395:51) at processFullRow (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite.js?v=fa4e9613:469:9) at processBinaryChunk (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite.js?v=fa4e9613:513:5) at progress (/node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/esm/react-server-dom-vite.js?v=fa4e9613:566:5)
解决方案
1. 标记组件为客户端组件
Hydrogen默认使用React Server Components,而Material UI的styled、sx等样式方案依赖客户端环境的样式处理逻辑,无法在服务器端序列化传递。需在组件文件最顶部添加'use client'指令,强制组件在客户端渲染:
'use client'; // 必须放在文件第一行 import * as React from 'react'; import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import Grid from '@mui/material/Grid'; // 其余代码保持不变
2. 拆分客户端与服务器组件(更规范的写法)
创建单独的客户端组件文件(可通过.client.jsx后缀让Hydrogen自动识别),将所有MUI相关代码放入其中:
// MuiGrid.client.jsx 'use client'; import * as React from 'react'; import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import Grid from '@mui/material/Grid'; const Item = styled(Paper)(({ theme }) => ({ backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff', ...theme.typography.body2, padding: theme.spacing(1), textAlign: 'center', color: theme.palette.text.secondary, })); export default function MuiGrid() { return ( <Box sx={{ flexGrow: 1 }}> <Grid container spacing={2}> <Grid item xs={8}> <Item>xs=8</Item> </Grid> <Grid item xs={4}> <Item>xs=4</Item> </Grid> <Grid item xs={4}> <Item>xs=4</Item> </Grid> <Grid item xs={8}> <Item>xs=8</Item> </Grid> </Grid> </Box> ); }
然后在服务器组件中引入该客户端组件:
// Home.jsx(服务器组件) import MuiGrid from './MuiGrid.client'; export default function Home() { return <MuiGrid />; }
3. 优化SSR样式渲染(可选)
若要避免首屏样式闪烁,可在Hydrogen的服务器入口文件中收集MUI的服务器端样式:
// entry-server.jsx import { renderToString } from 'react-dom/server'; import { ServerStyleSheets, ThemeProvider, createTheme } from '@mui/material/styles'; import App from './App'; export default function handleRequest(request, response) { const sheets = new ServerStyleSheets(); const theme = createTheme(); const html = renderToString( sheets.collect( <ThemeProvider theme={theme}> <App /> </ThemeProvider> ) ); const css = sheets.toString(); response.send(` <!DOCTYPE html> <html> <head> <style id="jss-server-side">${css}</style> </head> <body> <div id="root">${html}</div> </body> </html> `); }
同时在客户端入口移除服务器注入的样式:
// entry-client.jsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import App from './App'; const theme = createTheme(); ReactDOM.hydrateRoot( document.getElementById('root'), <ThemeProvider theme={theme}> <App /> </ThemeProvider> ); // 清理服务器端样式 const serverStyle = document.getElementById('jss-server-side'); if (serverStyle) serverStyle.remove();
问题核心原因
React Server Components要求传递给客户端的props必须是可序列化的纯对象,而Material UI的styled生成的组件包含了_insertTag等样式处理函数,这类非纯对象无法被序列化传递,因此必须将使用MUI的组件强制在客户端环境运行。
内容的提问来源于stack exchange,提问作者Sylv ain
相关产品推荐
相关产品推荐

