mui / toolpad

Toolpad: Full stack components and low-code builder for dashboards and internal apps.
https://mui.com/toolpad/
MIT License
964 stars 243 forks source link

Decrease bundle size #1008

Closed WangLarry closed 1 year ago

WangLarry commented 2 years ago

Duplicates

Latest version

Summary 💡

I use @next/bundle-analyzer to analyze toolpad-app. It show there are two huge packages: monaco-editor and @mui/icons-material.

截屏2022-09-15 19 19 08 截屏2022-09-15 19 20 11

Examples 🌈

No response

Motivation 🔦

No response

Janpot commented 2 years ago
WangLarry commented 2 years ago
info  - Collecting page data  
Route (pages)                                Size     First Load JS
┌ λ /                                        1.16 kB         195 kB
├   /_app                                    0 B             119 kB
├ λ /_toolpad/[[...index]]                   330 kB         1.53 MB
├ λ /404                                     199 B           120 kB
├ λ /api/data/[appId]/[version]/[queryId]    0 B             119 kB
├ λ /api/dataSources/[dataSource]/[...path]  0 B             119 kB
├ λ /api/health-check                        0 B             119 kB
├ λ /api/rpc                                 0 B             119 kB
├ λ /app-canvas/[[...path]]                  2.92 kB         1.2 MB
├ λ /app/[appId]/[version]/[[...path]]       358 B           1.2 MB
└ λ /deploy/[appId]/[[...path]]              353 B           1.2 MB
+ First Load JS shared by all                119 kB
  ├ chunks/framework-2422bd4d7ba12811.js     45.9 kB
  ├ chunks/main-5b7e023216b20801.js          35 kB
  ├ chunks/pages/_app-5b8c0ac2d0c62fea.js    32 kB
  └ chunks/webpack-f2ca0da3ac4cec04.js       6.56 kB

ƒ Middleware                                 20.5 kB
WangLarry commented 2 years ago

http://localhost:3000/deploy/cl7q3tf6a0005px8cz8jbuij0/pages

截屏2022-09-15 23 04 39 截屏2022-09-15 23 02 21
Janpot commented 2 years ago

@WangLarry Are there any specific problems you'd like to point out?

WangLarry commented 2 years ago

235 request, too many. Can optimize load @mui/material/* module, using the method of dealing @mui/icons-material

I have tested,it will decrease request times to 95

Are you understand? Sorry my bad english

Janpot commented 2 years ago

Can optimize load @mui/material/* module, using the method of dealing @mui/icons-material

I'm not sure that would work well with imports like:

import Button, { buttonClasses } from "@mui/material/Button";
WangLarry commented 2 years ago

@Janpot


const muiMaterialExports = new Map<string, ExportMapValue>([
  ['@mui/material', import('@mui/material')],
  // ['@mui/material/Accordion', import('@mui/material/Accordion')],
  // ['@mui/material/CssBaseline', import('@mui/material/CssBaseline')],
  // ['@mui/material/List', import('@mui/material/List')],
  // ['@mui/material/Snackbar', import('@mui/material/Snackbar')],
  // ['@mui/material/ToggleButtonGroup', import('@mui/material/ToggleButtonGroup')],
  // ['@mui/material/AccordionActions', import('@mui/material/AccordionActions')],
  // ['@mui/material/Dialog', import('@mui/material/Dialog')],
  // ['@mui/material/ListItem', import('@mui/material/ListItem')],
  // ['@mui/material/SnackbarContent', import('@mui/material/SnackbarContent')],
  // ['@mui/material/Toolbar', import('@mui/material/Toolbar')],
  // ['@mui/material/AccordionDetails', import('@mui/material/AccordionDetails')],
  // ['@mui/material/DialogActions', import('@mui/material/DialogActions')],
  // ['@mui/material/ListItemAvatar', import('@mui/material/ListItemAvatar')],
  // ['@mui/material/SpeedDial', import('@mui/material/SpeedDial')],

....

      const compMatch = /^@mui\/material\/(.*)$/.exec(moduleId);
      if (compMatch) {
        const compName = compMatch[1];
        const compModule = modules.get('@mui/material');
        const compClasses = `${uncapitalize(compName)}Classes`;
        esModule = {
          default: (compModule as any)[compName],
          [compClasses]: (compModule as any)[compClasses],
        };
      }
截屏2022-09-16 09 48 02
Janpot commented 1 year ago

@WangLarry The point was that it requires us to hardcode the exports of each of those modules imported, which will be a maintenance burden as we'll have to keep it up to date as @mui/material evolves. So I don't feel like we should go that way. If the problem is the bundle splitting, we could organize the imports in a way that would create a single chunk for all library code:

// ./loadModule.ts

// ...
    const muiExports = await import('./muiExports')
// ...
import muiMaterialDefault, * as muiMaterial from '@mui/material';
import muiMaterialAccordionDefault, * as muiMaterialAccordion from '@mui/material/Accordion';
// ...

function esm(defaultExport: any, namedExports: any) {
  return { ...namedExports, default: defaultExport, __esModule: true };
}

export default new Map([
  ['@mui/material', esm(muiMaterial, muiMaterialDefault)],
  ['@mui/material/Accordion', esm(muiMaterialAccordionDefault, muiMaterialAccordion)],
  // ...
]);

It's still not great as it still imports all library code, even when unused, but it will use less requests. It also hacks esm support on top of commonjs which will for sure run into edge cases at some point. In the future we could look into either a pure ESM runtime that only loads used components, or a serverside compilation step for deployed applications.

Janpot commented 1 year ago

Closing this issue as we have replaced the runtime with vite and are now compiling only the libraries that are being used. See https://github.com/mui/mui-toolpad/pull/1881