常见问题
遇到特定问题?首先在常见问题解答中查看这些常见问题。
如果您仍然找不到您要查找的内容,您可以参考我们的支持页面。
MUI 是一个很棒的组织。我如何支持它?
有很多方法可以支持我们
- 口口相传。通过在您的网站上链接到 mui.com 来宣传 MUI 的产品——每个反向链接都很重要。在 X 上关注我们,点赞并转发重要新闻。或者只是和您的朋友谈论我们。
- 给我们反馈。告诉我们哪些方面做得好,或者哪些方面有改进的机会。请为您最希望解决的问题投票 (👍)。
- 帮助新用户。您可以在 Stack Overflow 上回答问题。
- 推动改变发生.
- 在 Open Collective 上为我们提供经济支持。如果您在商业项目中使用 Material UI,并希望通过成为赞助商来支持其持续开发,或者在业余或爱好项目中使用,并希望成为支持者,您可以通过 Open Collective 来做到这一点。所有捐款都以透明的方式管理,赞助商将在 README 和主页上获得认可。
当模态框打开时,为什么固定定位的元素会移动?
一旦模态框打开,滚动就会被阻止。这可以防止在模态框应该是唯一交互内容时与背景交互。但是,删除滚动条可能会使您的固定定位元素移动。在这种情况下,您可以应用全局 .mui-fixed
类名来告诉 Material UI 处理这些元素。
如何全局禁用涟漪效果?
涟漪效果完全来自 BaseButton
组件。您可以通过在主题中提供以下内容来全局禁用涟漪效果
import { createTheme } from '@mui/material';
const theme = createTheme({
components: {
// Name of the component ⚛️
MuiButtonBase: {
defaultProps: {
// The props to apply
disableRipple: true, // No more ripple, on the whole application 💣!
},
},
},
});
如何全局禁用过渡效果?
Material UI 使用相同的主题辅助函数来创建所有过渡效果。因此,您可以通过覆盖主题中的辅助函数来禁用所有过渡效果
import { createTheme } from '@mui/material';
const theme = createTheme({
transitions: {
// So `transition: none;` gets applied everywhere
create: () => 'none',
},
});
在视觉测试期间禁用过渡效果或提高低端设备的性能可能很有用。
您可以更进一步,禁用所有过渡和动画效果
import { createTheme } from '@mui/material';
const theme = createTheme({
components: {
// Name of the component ⚛️
MuiCssBaseline: {
styleOverrides: {
'*, *::before, *::after': {
transition: 'none !important',
animation: 'none !important',
},
},
},
},
});
请注意,上述方法需要使用 CssBaseline
才能生效。如果您选择不使用它,您仍然可以通过包含以下 CSS 规则来禁用过渡和动画效果
*,
*::before,
*::after {
transition: 'none !important';
animation: 'none !important';
}
我是否必须使用 Emotion 来为我的应用程序设置样式?
不,这不是必需的。但是,如果您使用的是默认的样式引擎 (@mui/styled-engine
),则 Emotion 依赖项已内置,因此不会增加额外的捆绑包大小开销。
也许,但是,您正在向已经使用其他样式解决方案的应用程序添加一些 Material UI 组件,或者已经熟悉不同的 API,并且不想学习新的 API?在这种情况下,请前往样式库互操作性部分,了解如何使用其他样式库重新设置 Material UI 组件的样式。
我应该何时使用内联样式 vs. CSS?
作为经验法则,仅对动态样式属性使用内联样式。CSS 替代方案提供了更多优势,例如
- 自动添加前缀
- 更好的调试
- 媒体查询
- 关键帧
我如何使用 react-router?
访问关于与第三方路由库集成的指南,例如 react-router 或 Next.js,以了解更多详情。
我如何访问 DOM 元素?
所有 Material UI 组件都应该在 DOM 中渲染一些内容,并将它们的 ref 转发到基础 DOM 组件。这意味着您可以通过读取附加到 Material UI 组件的 ref 来获取 DOM 元素
// or a ref setter function
const ref = React.createRef();
// render
<Button ref={ref} />;
// usage
const element = ref.current;
如果您不确定有问题的 Material UI 组件是否转发了它的 ref,您可以查看 “Props” 下的 API 文档。您应该找到如下消息,例如在 Button API 中。
ref 被转发到根元素。
我的应用程序在服务器上无法正确渲染
如果它不起作用,99% 的情况下是配置问题。缺少属性、错误的调用顺序或缺少组件——服务器端渲染对配置要求严格。
找出问题所在的最佳方法是将您的项目与已经正常工作的设置进行比较。逐位查看参考实现。
为什么我看到的颜色与我在这里看到的颜色不同?
文档站点正在使用自定义主题。因此,颜色调色板与 Material UI 附带的默认主题不同。请参考此页面以了解有关主题自定义的信息。
为什么组件 X 需要 prop 中的 DOM 节点而不是 ref 对象?
像 Portal 或 Popper 这样的组件分别需要在 container
或 anchorEl
prop 中使用 DOM 节点。在这些 prop 中简单地传递 ref 对象并让 Material UI 访问当前值似乎很方便。
这在简单场景中有效
function App() {
const container = React.useRef(null);
return (
<div className="App">
<Portal container={container}>
<span>portaled children</span>
</Portal>
<div ref={container} />
</div>
);
}
其中 Portal
只会在 container.current
可用时将子组件挂载到容器中。这是一个 Portal 的朴素实现
function Portal({ children, container }) {
const [node, setNode] = React.useState(null);
React.useEffect(() => {
setNode(container.current);
}, [container]);
if (node === null) {
return null;
}
return ReactDOM.createPortal(children, node);
}
使用这种简单的启发式方法,Portal
可能会在挂载后重新渲染,因为 refs 在任何 effect 运行之前都是最新的。但是,仅仅因为 ref 是最新的,并不意味着它指向已定义的实例。如果 ref 附加到 ref 转发组件,则不清楚 DOM 节点何时可用。在上面的示例中,Portal
将运行一次 effect,但可能不会重新渲染,因为 ref.current
仍然是 null
。这对于 Suspense 中的 React.lazy 组件尤其明显。上面的实现也无法解释 DOM 节点的变化。
这就是为什么需要一个 prop 来传递实际的 DOM 节点,以便 React 可以负责确定 Portal
何时应该重新渲染
function App() {
const [container, setContainer] = React.useState(null);
const handleRef = React.useCallback(
(instance) => setContainer(instance),
[setContainer],
);
return (
<div className="App">
<Portal container={container}>
<span>Portaled</span>
</Portal>
<div ref={handleRef} />
</div>
);
}
clsx 依赖项是做什么用的?
clsx 是一个小型实用程序,用于有条件地构造 className
字符串,它使用一个对象,其中键是类字符串,值是布尔值。
您可以不用写
// let disabled = false, selected = true;
return (
<div
className={`MuiButton-root ${disabled ? 'Mui-disabled' : ''} ${
selected ? 'Mui-selected' : ''
}`}
/>
);
您可以这样做
import clsx from 'clsx';
return (
<div
className={clsx('MuiButton-root', {
'Mui-disabled': disabled,
'Mui-selected': selected,
})}
/>
);
我无法在 styled() 实用程序中使用组件作为选择器。我该怎么办?
如果您收到错误:TypeError: Cannot convert a Symbol value to a string
,请查看 styled() 文档页面,了解如何修复此问题。
我如何为免费模板贡献代码?
模板是使用共享主题构建的。以下是创建新模板的结构
模板页面
在 docs/pages/material-ui/getting-started/templates/<name>.js
目录中创建一个新页面,并包含以下代码
import * as React from 'react';
import AppTheme from 'docs/src/modules/components/AppTheme';
import TemplateFrame from 'docs/src/modules/components/TemplateFrame';
import Template from 'docs/data/material/getting-started/templates/<name>/<Template>';
export default function Page() {
return (
<AppTheme>
<TemplateFrame>
<Template />
</TemplateFrame>
</AppTheme>
);
}
然后在 docs/data/material/getting-started/templates/<name>/<Template>.tsx
创建一个模板文件(如果需要,可以添加更多文件)
注意:
<Template>
必须是<name>
文件夹的帕斯卡命名法字符串。
共享主题
模板必须使用来自 ../shared-theme/AppTheme
的 AppTheme
,以确保所有模板的外观和风格一致。
如果模板包含自定义主题组件,例如带有 MUI X 主题组件的仪表板模板,请将它们传递给 AppTheme
的 themedComponents
prop
import AppTheme from '../shared-theme/AppTheme';
const xThemeComponents = {
...chartsCustomizations,
...dataGridCustomizations,
...datePickersCustomizations,
...treeViewCustomizations,
};
export default function Dashboard(props: { disableCustomTheme?: boolean }) {
return (
<AppTheme {...props} themeComponents={xThemeComponents}>...</AppTheme>
)
}
颜色模式切换
共享主题提供了颜色模式切换的 2 种外观,ColorModeSelect
和 ColorModeIconDropdown
。您可以在模板中使用其中任何一个,它将在 TemplateFrame
中隐藏,但在 CodeSandbox 和 StackBlitz 中可见。
模板框架
如果模板具有需要固定在顶部的侧边栏或页眉,请参考 CSS 变量 --template-frame-height
进行调整。
例如,仪表板模板具有需要考虑模板框架高度的固定页眉
<AppBar
position="fixed"
sx={{
top: 'var(--template-frame-height, 0px)',
// ...other styles
}}
>
这将使 AppBar
在预览模式下保持在 TemplateFrame
下方,但在 CodeSandbox 和 StackBlitz 中固定在顶部。
[legacy] 我在页面上有多个样式实例
如果您在控制台中看到如下警告消息,您可能在页面上初始化了多个 @mui/styles
实例。
可能的原因
发生这种情况有几个常见原因
- 您的依赖项中的某个位置有另一个
@mui/styles
库。 - 您的项目具有 monorepo 结构(例如,lerna 或 yarn workspaces),并且
@mui/styles
模块是多个包中的依赖项(这或多或少与前一个相同)。 - 您有多个使用
@mui/styles
的应用程序在同一页面上运行(例如,webpack 中的多个入口点加载在同一页面上)。
node_modules 中的重复模块
如果您认为问题可能在于 @mui/styles 模块在您的依赖项中的重复,则有几种方法可以检查这一点。您可以在应用程序文件夹中使用 npm ls @mui/styles
、yarn list @mui/styles
或 find -L ./node_modules | grep /@mui/styles/package.json
命令。
如果这些命令都没有识别出重复项,请尝试分析您的捆绑包中是否包含 @mui/styles 的多个实例。您可以只检查您的捆绑包源代码,或使用像 source-map-explorer 或 webpack-bundle-analyzer 这样的工具。
如果您确定重复是您遇到的问题,您可以尝试以下几种方法来解决它
如果您使用的是 npm,您可以尝试运行 npm dedupe
。此命令搜索本地依赖项,并尝试通过将公共依赖项进一步向上移动树结构来简化结构。
如果您使用的是 webpack,您可以更改它解析 @mui/styles 模块的方式。您可以覆盖 webpack 查找您的依赖项的默认顺序,并使您的应用程序 node_modules 比默认的 node 模块解析顺序更优先
resolve: {
+ alias: {
+ '@mui/styles': path.resolve(appFolder, 'node_modules', '@mui/styles'),
+ },
},
在一个页面上运行多个应用程序
如果您在一个页面上运行多个应用程序,请考虑对所有应用程序使用一个 @mui/styles 模块。如果您使用的是 webpack,您可以使用 CommonsChunkPlugin 来创建一个显式的vendor chunk,它将包含 @mui/styles 模块
module.exports = {
entry: {
+ vendor: ['@mui/styles'],
app1: './src/app.1.js',
app2: './src/app.2.js',
},
plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'vendor',
+ minChunks: Infinity,
+ }),
]
}
[legacy] 为什么我的组件在生产版本中无法正确渲染?
发生这种情况的首要原因可能是代码进入生产捆绑包后类名冲突。为了使 Material UI 正常工作,页面上所有组件的 className
值必须由 类名生成器的单个实例生成。
要纠正此问题,需要初始化页面上的所有组件,以便它们之间始终只有一个类名生成器。
在各种情况下,您可能会意外地使用两个类名生成器
- 您意外地捆绑了两个版本的
@mui/styles
。您可能有一个依赖项没有正确地将 Material UI 设置为 peer dependency。 - 您正在为 React 树的子集使用
StylesProvider
。 - 您正在使用 bundler,并且它以导致创建多个类名生成器实例的方式拆分代码。
总的来说,通过在每个 Material UI 应用程序的组件树顶部用 StylesProvider
组件包裹它们并使用它们之间共享的单个类名生成器,可以很容易地从这个问题中恢复过来。
[legacy] CSS 仅在首次加载时有效,然后就消失了
CSS 仅在页面首次加载时生成。然后,对于后续请求,服务器上缺少 CSS。
需要采取的行动
样式解决方案依赖于缓存,即sheets manager,来为每个组件类型仅注入一次 CSS(如果您使用两个按钮,则只需要按钮的 CSS 一次)。您需要为每个请求创建一个新的 sheets
实例。
修复示例
-// Create a sheets instance.
-const sheets = new ServerStyleSheets();
function handleRender(req, res) {
+ // Create a sheets instance.
+ const sheets = new ServerStyleSheets();
//…
// Render the component to a string.
const html = ReactDOMServer.renderToString(
[legacy] React 类名 hydration 不匹配
客户端和服务器之间存在类名不匹配。它可能在第一次请求时有效。另一个症状是样式在初始页面加载和客户端脚本下载之间发生变化。
需要采取的行动
类名值依赖于类名生成器的概念。整个页面需要用单个生成器渲染。此生成器需要在服务器端和客户端的行为相同。例如
您需要为每个请求提供一个新的类名生成器。但是您不应该在不同的请求之间共享
createGenerateClassName()
修复示例
-// Create a new class name generator. -const generateClassName = createGenerateClassName(); function handleRender(req, res) { + // Create a new class name generator. + const generateClassName = createGenerateClassName(); //… // Render the component to a string. const html = ReactDOMServer.renderToString(
您需要验证您的客户端和服务器是否运行完全相同的版本的 Material UI。即使是次要版本的版本不匹配也可能导致样式问题。要检查版本号,请在构建应用程序的环境以及部署环境中运行
npm list @mui/styles
。您还可以通过在 package.json 的依赖项中指定特定的 Material UI 版本来确保不同环境中的版本相同。
修复示例 (package.json)
"dependencies": { ... - "@mui/styles": "^5.0.0", + "@mui/styles": "5.0.0", ... },
您需要确保服务器和客户端共享相同的
process.env.NODE_ENV
值。