Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Long reload time and huge number of network requests when modify the files of stories. #87

Open
ThinCats opened this issue Aug 19, 2021 · 14 comments
Labels
performance Not as fast as it should be

Comments

@ThinCats
Copy link

ThinCats commented Aug 19, 2021

Background

When diving into the example project (https://github.com/eirslett/storybook-builder-vite/tree/main/packages/example-react), I found that the page will have a long loading time when I modify the stories.

Using the devtools power by Chrome, you can see that there will be more than 500 network requests if the iframe is reloaded, and the number of requests will increase a lot with the addition of addons, which affects the development experience.

image

Trace

After debug, it was found that the storybook addons were imported using the absolute path in the vite-app.js generated by this builder.

These absolute paths seem to confuse vite's auto dependency discovery (see vitejs/vite#1571 (comment)), resulting in the inability to bundle the storybook addons as external dependencies, which has a large number of internal package references.

image

The related code is located in codegen-iframe-script.js, the configEntries here contains the user configuration and configuration injected by addons. It would be better to treat those injected configs to be external dependencies.

Workaround

I still don't find a better way to solve this problem. But there is a workaround without modifying the builder's source code.

In the project's .storybook/main.js, manually writing a vite plugin to replace all the import path which are external dependencies in vite-app.js. At the same time, the builder's default configuration for optimizeDeps.include is replaced, because most of them will not be used.

Note: This vite plugin is still experimental, and many edge cases are not considered.

const path = require("path");

const forceBundleConfigDeps = () => {
    const virtualFileId = '/virtual:/@storybook/builder-vite/vite-app.js';

    return {
        name: 'force-bundle-config-dep',
        enforce: 'pre',
        /**
         * @param {string} code
         */
        transform(code, id) {
            if (id !== virtualFileId) {
                return;
            }

            // match last node_modules
            // .../node_modules/.../node_modules/yy/zz -> yy/zz
            const transformedCode = code.replace(
                /import \* as (config_.*?) from '.*\/node_modules\/(.*?)'/g,
                (_substr, name, mpath) => {
                    return `import * as ${name} from '${mpath}'`;
                }
            );

            return {
                code: transformedCode,
                map: null,
            };
        },
    };
};

module.exports = {
  stories: [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  addons: [
    "@storybook/addon-a11y", // it works now, without optimizeDeps.include (see issue https://github.com/eirslett/storybook-builder-vite/issues/6)
    "@storybook/addon-links",
    "@storybook/addon-essentials",
  ],
  core: {
    builder: "storybook-builder-vite",
  },
  async viteFinal(config, options) {
    // customize the Vite config here
    return {
      ...config,
      plugins: [...config.plugins, forceBundleConfigDeps()],
      optimizeDeps: {
        include: [
          "@storybook/react",
          "@storybook/client-api",
          "@storybook/client-logger",
        ],
        entries: [
          `${path.relative(
            config.root,
            path.resolve(__dirname, "../stories")
          )}/**/*.stories.@(js|jsx|ts|tsx)`,
        ],
      },
    };
  },
};

After the optimization, only 90 network requests are required for one reload.

image

The external dependencies in vite-app.js are also bundled correctly.

image

Reproduction Repo

Slow loading repo (copied from example-react): https://github.com/ThinCats/storybook-builder-vite-reproduce-slow-reload/tree/main/packages/react-slow-reload

Workaround Modified repo: https://github.com/ThinCats/storybook-builder-vite-reproduce-slow-reload/tree/main/packages/react-slow-reload-workaround

@ThinCats
Copy link
Author

Because of the vite's auto dependency discovery mechanism, we even don't need the option optimizeDeps.include. Now the addon @storybook/addon-a11y works like a charm.

@eirslett
Copy link
Collaborator

This could maybe be improved if Storybook delivered npm packages for addons that were already run through Rollup or some sort of bundler that only generated one single file... but the general weakness for Vite vs Storybook is the number of requests that the browser has to make.

Vite should have added cache headers to all the files, to let the browser skip some of the heavy lifting. Usually, it should handle around 500 requests without a problem, but more than that, and things start to become sluggish.

Workflow-wise, I recommend making as few changes to the stories as possible, and doing most of the changes in the components - then you get hot reloading, which improves the developer experience a lot.

rdlopes added a commit to rdlopes/template-lit that referenced this issue Aug 21, 2021
@jscastanos
Copy link

@ThinCats Your workaround is really good.

Here's mine

Before
Screenshot from 2021-09-21 13-59-47

After
Screenshot from 2021-09-21 14-00-36

What should we look out for with this workaround?

@TomCaserta
Copy link

I have a very large project and noticed the same speed problems. That being said the problem of having a large module count is being exasperated by the storybook server itself. It's using the https module instead of something that can handle http2 requests like spdy (chosen because it works with express).

I did a little testing by modifying the server-init file in storybook/core-server and it's 2x faster to load 1600 modules when using spdy with storybook than it is with the current server implementation. I think it might be wise to see if we can get them to change.

@eirslett
Copy link
Collaborator

eirslett commented Sep 29, 2021

I guess

  1. Use http2 in the Storybook server
  2. Storybook itself (the manager) should be pre-bundled
  3. Lazy loading stories

would help. All of these things have to be implemented in Storybook core.

@rburnham52
Copy link

After converting our project and stories to vite I've noticed very long load times. It seems to be rebuilding vite cache for eveything. I can see node_modules\.vite gets wiped and then recreated. It's taking about 5-10mins to load. Sadly the same thing happens when you stop and start with the npm script start-storybook -p 6006

@zheeeng
Copy link

zheeeng commented Dec 7, 2021

I'm building a series of wrapped components on https://github.com/ant-design/ant-design. I encountered a huge loading time preboundling consumption, the iframe Vite hosted keeps reloading times and times. Is there any progress on large-scale components developing experience improvement?

@IanVS
Copy link
Member

IanVS commented Dec 7, 2021

I've found that adding my stories glob as optimizeDeps.entries in viteFinal helps a lot. And add anything else that is still found by vite when building to the optimizeDeps.include array, (while making sure to preserve what this package already provides). Lastly, enabling features.storyStoreV7 on storybook 6.4.0 can save a lot of time in the browser's load time, because it lazy loads the stories instead of all at once.

@IanVS IanVS added the performance Not as fast as it should be label Dec 7, 2021
@airtonix
Copy link

if-only-it-was-real-butter

jebus help us! it's sooo slow.

@IanVS
Copy link
Member

IanVS commented Aug 19, 2022

There is work ongoing right now to address #87 (comment).

  1. Explore replacing express with fastify in dev server storybook#17971
  2. Storybook 7.0 uses esbuild and tsup to pre-bundle the manager and addons.
  3. We may be able to HMR stories in 7.0 because webpack is no longer running which was causing HMR conflicts with vite.

Hang in there, things will improve a lot in storybook 7! We're working on getting the vite builder running with it, and we'll announce when there's an alpha version that you can try out. It will probably be in the next few weeks or so.

@airtonix
Copy link

tears-of-joy-clapping

@jjpxiii
Copy link

jjpxiii commented Feb 21, 2023

The GIF above may suggest that a solution has been found but it has not.
This issue has been ongoing for some time now and I think it'll never work correctly as long as vite serves the files unbundled.
Has anyone found a workaround with HTTP2 in order to gain some performance ?

Here's what I tried :
Enabling automatic code-splitting : it works great for HMR but it doesn't affect the cold start time, alas !
I also tried to simplify all the imports and avoid circular references but to no end.
Changed between react plugins esbuild and swc, no visible gain.
I added some kind of pre-loading in post-install with the --smoke-test option in my package.json with no visible performance gain.

TL; DR subscribe to this issue and wait 🔔

@IanVS
Copy link
Member

IanVS commented Feb 21, 2023

@jjpxiii you're right that it can take some time for Vite to start up, and it is something I hope to spend more time investigating after 7.0 is released. If you are already using the 7.0 beta and experiencing long load times, there are some things you can do to investigate:

  1. Check your .storybook/preview.js to see if you are importing something that brings in your entire app. The preview.js file is loaded for every story, so try to keep it as small as possible to improve first-story startup time.
  2. Avoid the use of index.js "barrel files," which re-export from lots of other files. They cause problems for tree-shaking and will cause lots of unneeded imports.
  3. Look in your network tab while storybook is starting. If you see components/files being loaded that shouldn't be, that's a good indication that you're doing one of the two things above. Otherwise, investigate why they are being downloaded.
  4. Docgen takes a significant amount of time during startup. If you're not using args tables or docs, you may be able to disable docgen and get some performance improvement. You can do this in .storybook/main.js with the setting: typescript: {reactDocgen: false}.

I hope some of these tips can help tide you over until we can spend more time to improve Vite startup performance in Storybook.

@jjpxiii
Copy link

jjpxiii commented Feb 22, 2023

Well, thanks for this great answer Ian, I wish I had complained earlier because this led to some huge improvements !

Yesterday morning, with SB 6.5.13 and Vite 4, the cold start took something between 55s and 1 minute. Preview time was between 9 and 10 seconds.
By cold start, I mean :

  • removed files in node_modules/.vite-storybook/deps
  • empty browser cache

From your answer :

  1. point 1 did very little as our number of imports in the preview file is quite small.

  2. and 3. we had already did our little investigation with the network tab, and the number of JS components was huge, that's why we were considering rewriting the imports and getting rid of the barrel files.
    Based on your advice, it took a handful of hours but we rewrote the imports and got everything working.
    CS times went down to ~38s and the warm cache starts stayed the same.
    The number of JS requests went from 1500 to 800, a great gain from rewriting the imports.
    As a result the preview times were cut in half, from 10 seconds to under 5.

  3. Deactivating react-docgen led to a massive gain, cold start takes now just north of 10 seconds, warm starts a little above 1 second, the preview time is under 1 second.
    However, we are using it, so it's very good to know there is room for improvement, and also to know where to find it !

Thanks again Ian, you've been really helpful for us and I hope for everyone who'll stumble on this thread 🤩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Not as fast as it should be
Projects
None yet
Development

No branches or pull requests

9 participants