跳转到内容

Minimizing Bundle Size 最小化打包文件大小

了解有关可用于减少打包文件大小的工具的详细信息。

打包文件的大小是很重要的

Material-UI 的打包文件大小至关重要。 Size snapshots are taken on every commit for every package and critical parts of those packages (view the latest snapshot). 结合 dangerJS 一起,我们可以在每个 Pull Request 中都可以查看详细的打包文件的大小变化

When and how to use tree-shaking?

Tree-shaking of Material-UI works out of the box in modern frameworks. Material-UI exposes its full API on the top-level material-ui import. If you're using ES6 modules and a bundler that supports tree-shaking (webpack >= 2.x, parcel with a flag) you can safely use named imports and still get an optimised bundle size automatically:

import { Button, TextField } from '@material-ui/core';

⚠️ The following instructions are only needed if you want to optimize your development startup times or if you are using an older bundler that doesn't support tree-shaking.

Development environment

Development bundles can contain the full library which can lead to slower startup times. This is especially noticeable if you import from @material-ui/icons. 加载时间会大约比那些从顶层 API 的命名导入方式慢六倍。

如果您觉得这样不妥,您还有以下几个选择:

选项1

您可以使用路径导入,这样可以避免导入用不到的模块。 For instance, use:

// 🚀 Fast
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';

instead of top level imports (without a Babel plugin):

import { Button, TextField } from '@material-ui/core';

This is the option we document in all the demos, since it requires no configuration. It is encouraged for library authors extending the components. Head to Option 2 for the approach that yields the best DX and UX.

尽管这样直接导入并不会使用 @material-ui/core/index.js 中的导出模式,但是对于那些公开的模块来说,此文件仍可以作为一个方便的参考。

请注意,我们只支持第一级和第二级的导入。 Anything deeper is considered private and can cause issues, such as module duplication in your bundle.

// ✅ OK
import { Add as AddIcon } from '@material-ui/icons';
import { Tabs } from '@material-ui/core';
//                                 ^^^^ 1st or top-level

// ✅ OK
import AddIcon from '@material-ui/icons/Add';
import Tabs from '@material-ui/core/Tabs';
//                                  ^^^^ 2nd level

// ❌ NOT OK
import TabIndicator from '@material-ui/core/Tabs/TabIndicator';
//                                               ^^^^^^^^^^^^ 3rd level

选项2

This option provides the best User Experience and Developer Experience:

  • UX: The Babel plugin enables top level tree-shaking even if your bundler doesn't support it.
  • DX: The Babel plugin makes startup time in dev mode as fast as Option 1.
  • DX: This syntax reduces the duplication of code, requiring only a single import for multiple modules. Overall, the code is easier to read, and you are less likely to make a mistake when importing a new module.
    import { Button, TextField } from '@material-ui/core';

However, you need to apply the two following steps correctly.

1。 Configure Babel

请在以下插件中选择一个:

  • babel-plugin-import with the following configuration:

    yarn add -D babel-plugin-import

    Create a .babelrc.js file in the root directory of your project:

    const plugins = [
      [
        'babel-plugin-import',
        {
          'libraryName': '@material-ui/core',
          // Use "'libraryDirectory': ''," if your bundler does not support ES modules
          'libraryDirectory': 'esm',
          'camel2DashComponentName': false
        },
        'core'
      ],
      [
        'babel-plugin-import',
        {
          'libraryName': '@material-ui/icons',
          // Use "'libraryDirectory': ''," if your bundler does not support ES modules
          'libraryDirectory': 'esm',
          'camel2DashComponentName': false
        },
        'icons'
      ]
    ];
    
    module.exports = {plugins};
  • babel-plugin-transform-imports with the following configuration:

    yarn add -D babel-plugin-transform-imports

    Create a .babelrc.js file in the root directory of your project:

    const plugins = [
      [
        'babel-plugin-transform-imports',
        {
          '@material-ui/core': {
            // Use "transform: '@material-ui/core/${member}'," if your bundler does not support ES modules
            'transform': '@material-ui/core/esm/${member}',
            'preventFullImport': true
          },
          '@material-ui/icons': {
            // Use "transform: '@material-ui/icons/${member}'," if your bundler does not support ES modules
            'transform': '@material-ui/icons/esm/${member}',
            'preventFullImport': true
          }
        }
      ]
    ];
    
    module.exports = {plugins};

If you are using Create React App, you will need to use a couple of projects that let you use .babelrc configuration, without ejecting.

yarn add -D react-app-rewired customize-cra

Create a config-overrides.js file in the root directory:

  /* config-overrides.js */
  const { useBabelRc, override } = require('customize-cra')

  module.exports = override(
    useBabelRc()
  );

If you wish, babel-plugin-import can be configured through config-overrides.js instead of .babelrc by using this configuration.

Modify your package.json start command:

  "scripts": {
-  "start": "react-scripts start"
+  "start": "react-app-rewired start"
  }

Note: You may run into errors like these:

Module not found: Can't resolve '@material-ui/core/makeStyles' in '/your/project'

This is because @material-ui/styles is re-exported through core, but the full import is not allowed.

You have an import like this in your code:

  import { makeStyles, createStyles } from '@material-ui/core';

The fix is simple, define the import separately:

  import { makeStyles, createStyles } from '@material-ui/core/styles';

Enjoy significantly faster start times.

2。 Convert all your imports

Finally, you can convert your existing codebase to this option with this top-level-imports codemod. It will perform the following diffs:

-import Button from '@material-ui/core/Button';
-import TextField from '@material-ui/core/TextField';
+import { Button, TextField } from '@material-ui/core';

ECMAScript

考虑到一些支持的平台,在 npm 上发布的包是和 Babel 一起被编译的。

A second version of the components is also published, which you can find under the /es folder. 所有非官方的语义都被编译成ECMA-262 的标准,仅此而已。 这样一来,针对不同的浏览器,您可以编译出不同的打包文件。 一些旧的浏览器需编译一些 JavaScript 的功能,这样会增加打包文件的大小。 ES2015 运行的时候的功能中不包含垫片。 IE11+ 和一些长青浏览器会支持所有必要的功能。 如果您需要支持其他浏览器,请考虑使用 @ babel/polyfill

⚠️ In order to minimize duplication of code in users' bundles, library authors are strongly discouraged from using the /es folder.