跳到内容
+

最小化包大小

了解更多关于可以用来减小包大小的工具。

包大小很重要

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']
  }
}