CRA迁移至Vite遇样式组件未定义错误,求SSR适配方案
迁移CRA到Vite+SSR时Emotion样式报错:
Error: You are trying to create a styled element with an undefined component 问题背景
正在将Create React App(CRA)项目迁移到Vite以实现服务端渲染(SSR),但遇到以下错误:
Error: You are trying to create a styled element with an undefined component. You may have forgotten to import it.
以下是当前的配置文件:
vite.config.ts
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import tsconfigPaths from 'vite-tsconfig-paths'; import svgrPlugin from 'vite-plugin-svgr'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ react({ jsxImportSource: '@emotion/react', babel: { plugins: ['@emotion/babel-plugin'], }, }), tsconfigPaths(), svgrPlugin(), ], });
tsconfig.json
{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "jsxImportSource": "@emotion/react", /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "baseUrl": "./src" }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] }
tsconfig.node.json
{ "compilerOptions": { "composite": true, "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "jsx": "preserve" }, "include": ["vite.config.ts", "src/entry-server.tsx", "src/vite-env.d.ts"] }
预期配置能正常运行,但实际触发上述错误,需要解决该问题。
解决方案
1. 检查Styled组件的导入方式
确保所有使用styled创建的组件都正确导入了基础组件。比如:
- 错误示例:
import styled from '@emotion/styled'; const Button = styled(ButtonBase)`/* 样式 */`; // ButtonBase未导入导致undefined - 正确做法:
import styled from '@emotion/styled'; import ButtonBase from '@mui/material/ButtonBase'; // 或对应基础组件 const Button = styled(ButtonBase)`/* 样式 */`;
2. 补全Emotion的SSR环境配置
Vite SSR需要额外处理Emotion的服务端渲染:
- 安装依赖:
npm install @emotion/server # 或 yarn add @emotion/server - 在
entry-server.tsx中包裹CacheProvider并提取样式:import { renderToString } from 'react-dom/server'; import { CacheProvider } from '@emotion/react'; import createCache from '@emotion/cache'; import createEmotionServer from '@emotion/server/create-instance'; import App from './App'; export async function render(url: string) { const cache = createCache({ key: 'ssr' }); const { extractCriticalToChunks, constructStyleTagsFromChunks } = createEmotionServer(cache); const html = renderToString( <CacheProvider value={cache}> <App /> </CacheProvider> ); const chunks = extractCriticalToChunks(html); const styles = constructStyleTagsFromChunks(chunks); return { html, styles }; }
3. 调整Vite插件的Emotion配置
完善@vitejs/plugin-react的babel插件参数,确保SSR场景下的兼容性:
// vite.config.ts react({ jsxImportSource: '@emotion/react', babel: { plugins: [ ['@emotion/babel-plugin', { sourceMap: true, autoLabel: 'dev-only' }] ], }, })
4. 修正服务端入口的TS配置
将tsconfig.node.json中的"jsx": "preserve"改为"jsx": "react-jsx",因为服务端入口需要编译JSX代码:
{ "compilerOptions": { "composite": true, "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "jsx": "react-jsx" }, "include": ["vite.config.ts", "src/entry-server.tsx", "src/vite-env.d.ts"] }
5. 排查第三方库的样式冲突
如果项目依赖第三方UI库(如Material UI),确认是否正确集成Emotion,避免混用不同样式库的styled方法,确保统一使用@emotion/styled。
内容的提问来源于stack exchange,提问作者Uttam Sharma
相关产品推荐
相关产品推荐

