如何阻止Emotion样式注入影响页面其他元素?
解决JsonForms+Emotion全局样式污染问题
问题场景
我在服务端渲染的Web应用中通过JsonForms React组件动态生成表单,用npm run build编译React组件得到main.js后,按以下方式引入页面:
<html> <head> ... <script type="text/javascript" src="main.js"></script> </head> <body> <div id="root"></div> </body> </html>
功能正常,但页面中组件外的元素CSS被修改——Chrome计算样式显示有Emotion注入的<style>标签覆盖了body的font-family,删除该标签后样式恢复正常。
我的JsonForms组件代码和官方seed应用基本一致:
import { Fragment, useState, useMemo } from 'react'; import { JsonForms } from '@jsonforms/react'; import Grid from '@mui/material/Grid'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; import logo from './logo.svg'; import './App.css'; import schema from './schema.json'; import uischema from './uischema.json'; import { materialCells, materialRenderers, } from '@jsonforms/material-renderers'; import RatingControl from './RatingControl'; import ratingControlTester from './ratingControlTester'; import { makeStyles } from '@mui/styles'; const useStyles = makeStyles({ container: { padding: '1em', width: '100%', }, title: { textAlign: 'center', padding: '0.25em', }, dataContent: { display: 'flex', justifyContent: 'center', borderRadius: '0.25em', backgroundColor: '#cecece', marginBottom: '1rem', }, resetButton: { margin: 'auto !important', display: 'block !important', }, demoform: { margin: 'auto', padding: '1rem', }, }); const initialData = { name: 'Send email to Adrian', description: 'Confirm if you have passed the subject\nHereby ...', done: true, recurrence: 'Daily', rating: 3, }; const renderers = [ ...materialRenderers, //register custom renderers { tester: ratingControlTester, renderer: RatingControl }, ]; const App = () => { const classes = useStyles(); const [data, setData] = useState<any>(initialData); const stringifiedData = useMemo(() => JSON.stringify(data, null, 2), [data]); const clearData = () => { setData({}); }; return ( <Fragment> <div className='App'> <header className='App-header'> <img src={logo} className='App-logo' alt='logo' /> <h1 className='App-title'>Welcome to JSON Forms with React</h1> <p className='App-intro'>More Forms. Less Code.</p> </header> </div> <Grid container justifyContent={'center'} spacing={1} className={classes.container} > <Grid item sm={6}> <Typography variant={'h4'} className={classes.title}> Bound data </Typography> <div className={classes.dataContent}> <pre id='boundData'>{stringifiedData}</pre> </div> <Button className={classes.resetButton} onClick={clearData} color='primary' variant='contained' > Clear data </Button> </Grid> <Grid item sm={6}> <Typography variant={'h4'} className={classes.title}> Rendered form </Typography> <div className={classes.demoform}> <JsonForms schema={schema} uischema={uischema} data={data} renderers={renderers} cells={materialCells} onChange={({ errors, data }) => setData(data)} /> </div> </Grid> </Grid> </Fragment> ); }; export default App;
解决方案
一、定位Emotion的CSS修改定义
- Chrome DevTools溯源:
- 打开Chrome DevTools,找到Emotion生成的
<style>标签,右键选择「检查」,在Styles面板找到对应的body样式规则。 - 点击规则右侧的文件链接(类似
emotion-styles-xxx.js),直接跳转到生成该样式的代码位置。
- 打开Chrome DevTools,找到Emotion生成的
- 排查依赖链:
项目中使用的@mui/material和@jsonforms/material-renderers会通过Emotion注入全局重置样式(比如MUI的CssBaseline组件),可以检查JsonForms的material渲染器是否自动引入了这类组件。
二、配置组件避免影响全局元素
方法1:限定MUI全局样式作用范围
给React应用根节点加唯一类名(比如jsonforms-container),通过MUI的ThemeProvider自定义主题,把全局样式限制在容器内:
import { createTheme, ThemeProvider } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; const theme = createTheme({ components: { MuiCssBaseline: { styleOverrides: { '.jsonforms-container': { // 自定义局部重置样式,或留空避免全局覆盖 fontFamily: 'inherit', }, }, }, }, }); const WrappedApp = () => ( <ThemeProvider theme={theme}> <div className="jsonforms-container"> <CssBaseline /> <App /> </div> </ThemeProvider> ); export default WrappedApp;
方法2:用Emotion的CacheProvider隔离样式
通过CacheProvider指定样式只注入到React根容器内:
import { CacheProvider } from '@emotion/react'; import createCache from '@emotion/cache'; // 创建自定义缓存,限定样式注入容器 const cache = createCache({ key: 'jsonforms-cache', container: document.getElementById('root'), }); const WrappedApp = () => ( <CacheProvider value={cache}> <App /> </CacheProvider> ); export default WrappedApp;
方法3:修改JsonForms渲染器配置
查阅@jsonforms/material-renderers文档,看是否有开关可以禁用默认的全局样式注入,部分渲染器会提供此类配置项。
内容的提问来源于stack exchange,提问作者Bioman
相关产品推荐
相关产品推荐

