最小化包大小
了解更多关于可以用来减小包大小的工具。
包大小很重要
Material UI 的维护者非常重视包大小。每次提交都会为每个包和这些包的关键部分拍摄大小快照。结合 dangerJS,我们可以在每个 Pull Request 上检查 详细的包大小更改。
何时以及如何使用 tree-shaking?
Tree-shaking Material UI 在现代框架中开箱即用。Material UI 在顶层 @mui
导入中公开其完整 API。如果您正在使用 ES6 模块和一个支持 tree-shaking 的打包器 (webpack
>= 2.x, parcel
带标志),您可以安全地使用命名导入,并且仍然可以自动获得优化的包大小
import { Button, TextField } from '@mui/material';
开发环境
开发包可能包含完整的库,这可能导致较慢的启动时间。如果您使用来自 @mui/icons-material
的命名导入,这一点尤其明显,这可能比默认导入慢六倍。例如,在以下两个导入之间,第一个(命名)可能比第二个(默认)慢得多
// 🐌 Named
import { Delete } from '@mui/icons-material';
// 🚀 Default
import Delete from '@mui/icons-material/Delete';
如果这对您来说是一个问题,您有两个选择
选项一:使用路径导入
您可以使用路径导入来避免引入未使用的模块。例如,使用
// 🚀 Fast
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
而不是顶层导入(不使用 Babel 插件)
import { Button, TextField } from '@mui/material';
这是我们在所有演示中记录的选项,因为它不需要任何配置。对于正在扩展组件的库作者,建议使用此选项。前往选项 2,了解可获得最佳 DX 和 UX 的方法。
虽然以这种方式直接导入不会使用 @mui/material
的主文件中的导出,但此文件可以作为方便的参考,了解哪些模块是公共的。
请注意,我们仅支持第一级和第二级导入。任何更深层次的都被认为是私有的,可能会导致问题,例如包中的模块重复。
// ✅ OK
import { Add as AddIcon } from '@mui/icons-material';
import { Tabs } from '@mui/material';
// ^^^^^^^^ 1st or top-level
// ✅ OK
import AddIcon from '@mui/icons-material/Add';
import Tabs from '@mui/material/Tabs';
// ^^^^ 2nd level
// ❌ NOT OK
import TabIndicator from '@mui/material/Tabs/TabIndicator';
// ^^^^^^^^^^^^ 3rd level
如果您正在使用 ESLint,您可以使用 no-restricted-imports
规则捕获有问题的导入。以下 .eslintrc
配置将突出显示来自 @mui
包的有问题的导入
{
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": ["@mui/*/*/*"]
}
]
}
}
选项二:使用 Babel 插件
此选项提供最佳的用户体验和开发者体验,除非您正在使用 Next.js 13.5 或更高版本,在这种情况下,此优化通过 Next.js 中的 optimizePackageImports
选项自动应用。在这种情况下,使用 Babel 插件是不必要的。
- UX:即使您的打包器不支持,Babel 插件也能实现顶层 tree-shaking。
- DX:Babel 插件使开发模式下的启动时间与选项 1 一样快。
- DX:此语法减少了代码的重复,只需要为多个模块进行一次导入。总的来说,代码更易于阅读,并且在导入新模块时您不太可能犯错误。
import { Button, TextField } from '@mui/material';
但是,您需要正确应用以下步骤。
1. 配置 Babel
使用 babel-plugin-import 以及以下配置
yarn add -D babel-plugin-import
在您的项目根目录中创建一个 .babelrc.js
文件
const plugins = [
[
'babel-plugin-import',
{
libraryName: '@mui/material',
libraryDirectory: '',
camel2DashComponentName: false,
},
'core',
],
[
'babel-plugin-import',
{
libraryName: '@mui/icons-material',
libraryDirectory: '',
camel2DashComponentName: false,
},
'icons',
],
];
module.exports = { plugins };
如果您正在使用 Create React App,您将需要使用几个允许您使用 .babelrc
配置的项目,而无需 ejecting。
yarn add -D react-app-rewired customize-cra
在根目录中创建一个 config-overrides.js
文件
/* config-overrides.js */
/* eslint-disable react-hooks/rules-of-hooks */
const { useBabelRc, override } = require('customize-cra');
module.exports = override(useBabelRc());
如果您愿意,可以使用此配置通过 config-overrides.js
而不是 .babelrc
来配置 babel-plugin-import
。
修改您的 package.json
命令
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
+ "test": "react-app-rewired test",
"eject": "react-scripts eject"
}
享受显着更快的启动时间。
2. 转换所有导入
最后,您可以使用此 top-level-imports codemod 将您现有的代码库转换为此选项。它将执行以下差异
-import Button from '@mui/material/Button';
-import TextField from '@mui/material/TextField';
+import { Button, TextField } from '@mui/material';
可用捆绑包
默认捆绑包
在 npm 上发布的包使用 Babel 进行转译,并针对支持的平台进行了性能优化。
现代捆绑包也可用。
如何使用自定义捆绑包?
使用这些捆绑包的一个好方法是配置捆绑器导出条件,例如使用 webpack 的 resolve.conditionNames
或 vite 的 resolve.conditions
// webpack.config.js
{
resolve: {
conditionNames: ['mui-modern', '...'],
}
}
// vite.config.js
{
resolve: {
conditions: ['mui-modern', 'module', 'browser', 'development|production']
}
}