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

Bundle requests twice in Firefox 93 with default polyfillModulePreload enabled #5532

Closed
7 tasks done
Tracked by #216
imShara opened this issue Nov 3, 2021 · 26 comments
Closed
7 tasks done
Tracked by #216
Labels
p4-important Violate documented behavior or significantly improves performance (priority)

Comments

@imShara
Copy link
Contributor

imShara commented Nov 3, 2021

Describe the bug

I have found that default vite build makes bundle request twice in firefox.

2021-11-03_02-54

When I switch off build.polyfillModulePreload: false it makes only one request. Why this happens? Vite docs says that polyfill need for firefox and safari, Can I Use says that modulepreload not supported in Firefox. Is this proper polyfill work?

There is no problem in Chrome or Safari

Reproduction

pnpm create vite test
> vue
> vue-ts
cd test
pnpm install
pnpm  build
pnpm serve

Open http://127.0.0.1:5000/ in Firefox 93.0 (64-bit)
Check dev tools F12 -> Network ->Reload

System Info

System:
    OS: Linux 5.14 Arch Linux
    CPU: (16) x64 AMD Ryzen 7 5800H with Radeon Graphics
    Memory: 19.28 GB / 27.27 GB
    Container: Yes
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 16.11.1 - /usr/bin/node
    Yarn: 1.22.17 - /usr/bin/yarn
    npm: 8.1.2 - /usr/bin/npm
  Browsers:
    Firefox: 93.0

Used Package Manager

pnpm

Logs

▶ pnpm vite build --debug
  vite:config bundled config file loaded in 89.20ms +0ms
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'alias',
  vite:config     'vite:modulepreload-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html-inline-script-proxy',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:vue',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'rollup-plugin-dynamic-import-variables',
  vite:config     'vite:asset-import-meta-url',
  vite:config     'vite:build-import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:reporter',
  vite:config     'vite:load-fallback'
  vite:config   ],
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
  vite:config     polyfillModulePreload: true,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     cssTarget: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
  vite:config     sourcemap: false,
  vite:config     rollupOptions: {},
  vite:config     minify: 'esbuild',
  vite:config     terserOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: false,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     reportCompressedSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: null,
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] }
  vite:config   },
  vite:config   define: { __VUE_OPTIONS_API__: true, __VUE_PROD_DEVTOOLS__: false },
  vite:config   ssr: { external: [ 'vue', '@vue/server-renderer' ] },
  vite:config   configFile: '/home/user/test/vite.config.ts',
  vite:config   configFileDependencies: [ 'vite.config.ts' ],
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: undefined,
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     build: {}
  vite:config   },
  vite:config   root: '/home/user/test',
  vite:config   base: '/',
  vite:config   resolve: { dedupe: undefined, alias: [ [Object], [Object] ] },
  vite:config   publicDir: '/home/user/test/public',
  vite:config   cacheDir: '/home/user/test/node_modules/.vite',
  vite:config   command: 'build',
  vite:config   mode: 'production',
  vite:config   isProduction: true,
  vite:config   server: { fs: { strict: undefined, allow: [Array] } },
  vite:config   env: { BASE_URL: '/', MODE: 'production', DEV: false, PROD: true },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     warnOnce: [Function: warnOnce],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen],
  vite:config     hasErrorLogged: [Function: hasErrorLogged]
  vite:config   },
  vite:config   createResolver: [Function: createResolver],
  vite:config   optimizeDeps: {
  vite:config     esbuildOptions: { keepNames: undefined, preserveSymlinks: undefined }
  vite:config   }
  vite:config } +6ms
vite v2.6.13 building for production...
✓ 14 modules transformed.
dist/assets/logo.03d6d6da.png    6.69 KiB
dist/index.html                  0.48 KiB
dist/assets/index.e40264f2.js    1.95 KiB / gzip: 1.03 KiB
dist/assets/index.459f8680.css   0.34 KiB / gzip: 0.24 KiB
dist/assets/vendor.dac2b406.js   49.61 KiB / gzip: 19.93 KiB

Validations

@SergeiMinaev
Copy link

The same here. It looks like this polyfill doesn't work in Firefox.

@imShara
Copy link
Contributor Author

imShara commented Nov 9, 2021

Actually, it looks like polyfill works AND native import works too. I can see no problems when polyfill disabled.

It's wrong, see below.

@imShara
Copy link
Contributor Author

imShara commented Nov 17, 2021

Okay, I have made minimal reproducible example of polyfill used in vite and confirm that it makes two requests by design.

First, dummy, request makes polyfill with fetch(link) function, which used to cache resource.
Second request makes import from link when module loads at first time in application runtime.

index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="modulepreload" href="module.mjs">
</head>

<body>
  Hello

  <script>
    function processPreload(link) {
      const fetchOpts = {};
      if (link.integrity)
        fetchOpts.integrity = link.integrity;
      if (link.referrerpolicy)
        fetchOpts.referrerPolicy = link.referrerpolicy;
      if (link.crossorigin === 'use-credentials')
        fetchOpts.credentials = 'include';
      else if (link.crossorigin === 'anonymous')
        fetchOpts.credentials = 'omit';
      else
        fetchOpts.credentials = 'same-origin';

      fetch(link.href, fetchOpts).then(res => res.ok && res.arrayBuffer());
    }

    for (const link of document.querySelectorAll('link[rel=modulepreload]')) {
      processPreload(link);
    }
  </script>

  <script type="module">
    import { test } from './module.mjs';
    test('yay');
  </script>
</body>

</html>

module.mjs

export function test(message) {
  alert(message)
}

access.log

"GET / HTTP/2.0" 200 919 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0"
"GET /module.mjs HTTP/2.0" 200 50 "https://modulepreload.local/" "Mozilla/5.0 (X11; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0"
"GET /module.mjs HTTP/2.0" 200 50 "https://modulepreload.local/" "Mozilla/5.0 (X11; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0"
"GET /favicon.ico HTTP/2.0" 404 153 "https://modulepreload.local/" "Mozilla/5.0 (X11; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0"

I think it's can't be fixed. Any suggestions?

@kunKun-tx
Copy link

kunKun-tx commented Dec 30, 2021

Can confirm this issue also occurs on Firefox 95.0.2. It's a simple React project using Vite and Firefox is downloading all non index.js assets twice.

@Kadeluxe
Copy link

Kadeluxe commented Jan 6, 2022

So what is the workaround for that?
I created empty Vue project using pnpm create vite, did cd project && pnpm build and it indeed makes double requests for all js files except index.js in Firefox 95.0.2.
It does double requests even if I use build.polyfillModulePreload: false. What's the deal?

Edit: actually if I turn off build.polyfillModulePreload: false then it only does double requests for dynamically imported modules because it generates the following code:

function __variableDynamicImportRuntime0__(path) {
  switch (path) {
    case "../../pages/overview.vue":
      return __vitePreload(() => import("./overview.85cf6486.js"), true ? ["assets/overview.85cf6486.js","assets/vendor.0dbf3cec.js"] : void 0);
    default:
      return new Promise(function(resolve, reject) {
        (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
      });
  }
}

Notice how module has dependency on itself. That's why it would load twice, first by doing dependency loading and then by doing browser import.

@Zsapi
Copy link

Zsapi commented Feb 18, 2022

I've just noticed this myself, and although it is not causing any measurable harm in my case, it is really messing with me. Vite version 2.8.4, Firefox 97.0.1. I'm using Vue, and the vendor js file is downloaded twice.

@sutarmin
Copy link

Same for me, vendor.js is loaded twice and that bothers me a lot!

@fahidmohammad
Copy link

Any update on this issue?
not only bundle, but every file is loading twice.
image

@sapphi-red
Copy link
Member

I think this is because maxAge is not set here.


If there is no Cache-Control header, browser needs to revalidate the response.

@gangsthub
Copy link

It seems that Vite is trying to be smart with this option. It’s a recommended optimization technique, so the JS chunks and their dependencies are loaded in parallel, instead of in waterfall.

First, they download (modulepreload) the assets, then you see a second request because it's trying to import native JS modules in the browser.

The fact that we see these duplicated requests happening on Safari and Firefox is because it relies on (a polyfill of) a feature that is only available in Chromium browsers. More info: https://developer.chrome.com/blog/modulepreload/

I wouldn’t worry about non-Chromium browsers for now, since the second request is always returned from cache.

I think it’s a good default we can have for now. Even if it is bothering to see in the Network tab.

@kito-inv
Copy link

I wouldn’t worry about non-Chromium browsers for now, since the second request is always returned from cache.

That doesn't always happen sadly, if the first request hasn't finished before the second one started then the second one is not returned from cache

@mihai-sysbio
Copy link

mihai-sysbio commented Oct 28, 2022

since the second request is always returned from cache

On the latest Firefox (macOS) I do see the data transferred twice, so, as @kito-inv mentioned above, it's not cached.

I think it’s a good default we can have for now. Even if it is bothering to see in the Network tab.

According to the caniuse data, ~25% of the userbase may be affected. Personally, it's not really a percentage I am comfortable with on a production server for a default behavior.

mihai-sysbio added a commit to MetabolicAtlas/MetabolicAtlas that referenced this issue Oct 28, 2022
mihai-sysbio added a commit to MetabolicAtlas/MetabolicAtlas that referenced this issue Oct 28, 2022
mihai-sysbio added a commit to MetabolicAtlas/MetabolicAtlas that referenced this issue Oct 28, 2022
@tarasovsn
Copy link

The same problem, but in Firefox 102.5.0esr (64-bit) (Mac OS Monterey 12.6.1)

screenshot

In Safari and Chrome there is no such problem.

@owlas
Copy link

owlas commented Jan 20, 2023

I'm seeing the behaviour in both safari and Firefox but not chrome. Loading vendor files are a big chunk of load time so this really affects the user experience in these two browsers:

Screenshot from safari:

Screenshot 2023-01-20 at 13 33 07

@EthanML
Copy link

EthanML commented Jan 26, 2023

Chiming in to say I see the same issue on Firefox 109.0 for Mac OS 👎 Same issue with Safari, but not in Chrome.

Screenshot 2023-01-26 at 13 19 38

Could we get confirmation on what the recommended course of action here is? If we disable the preload polyfill option as described above by the original poster, is that going to cause issues elsewhere? If so, what?


Edit: Found my way to #9938 and followed instructions there to disable this feature. I believe this issue should be closed and pointed to that PR.

@benmccann benmccann added bug p4-important Violate documented behavior or significantly improves performance (priority) and removed pending triage labels Feb 10, 2023
@hadson172
Copy link

hadson172 commented Feb 18, 2023

Having same issues, any updates on the topic?

I am using dynamic imports for subpages and react router. On each of the browser this is what i am getting:

obraz

There should be single request for dynamic import not two of those.

@Shakeskeyboarde
Copy link
Contributor

Same issue. I've tried disabling optimizeDeps and modulePreload. Still happening. I'm fully capable of parallelizing/preloading my own promises, thanks 🙄. This isn't something that it should be generating code for and trying to do automatically.

@Teamoh
Copy link

Teamoh commented Apr 10, 2023

I am having the same behavior, no matter if modulePreload is polyfilled or not.

It looks like this is not an issue from Vite, but a behavior (bug?) from Firefox.

When doing a dynamic import (import(...) Firefox ignores the fact, that this module was already preloaded using <link rel="preload">.

So either this is a bug in Firefox, or it will be repaired as soon as Firefox starts supporting rel=modulepreload.

But if you modifiy the generated index.js to always use rel=preload even in Chrome, it will still work in Chrome (i.e. load only once), so there is definitely a different behavior between browsers here and Firefox should understand that the module was already loaded even today.

I also tested to change the Cache-Control header using preview.headers which defaults to no-cache but this doesn't fix it in Firefox.

@Teamoh
Copy link

Teamoh commented Apr 10, 2023

I would like to be able to disable the preload feature completly by setting build.modulePreload to false, but as soon as I do that, my CSS is not loaded anymore as a dependency. The CSS files are correctly built, but they are not referenced anymore in any of the generated JS files.

@Kadeluxe
Copy link

It's not a Firefox fault, as I stated in my comment there is a clear reason why this is not working properly.

@mohammad1111
Copy link

I'm seeing the behaviour in both safari and Firefox but not chrome. Loading vendor files are a big chunk of load time so this really affects the user experience in these two browsers:

Screenshot from safari:

Screenshot 2023-01-20 at 13 33 07

I have the same problem as you. Did you solve this problem? plz guide me.

@mihai-sysbio
Copy link

Bumping this as it is still unresolved for Firefox.

@asvishnyakov
Copy link

asvishnyakov commented Aug 10, 2023

I have exactly the same behavior for Chrome with vite 4.4.8, but only in dev mode. production is fine. Also modulePreload: { polyfill: false } doesn't help

@adbenitez
Copy link

notice that this is not only about network traffic, I am migrating a project to vite and there a canvas is added to the body in a index.ts script, I noticed that 2 canvas were added to the body and was scratching my head until I realized it was vite loading the same script twice

@iamphonghg
Copy link

iamphonghg commented Oct 7, 2023

Hey folks,

I just switched from webpack to vite (using vite for react in laravel 6) and encountered the same thing and found a solution.

Files that are dynamically imported will be called twice. I don't know if your case is the same as mine, but I noticed that the Request URL of the two calls is not the same:
1st time: ...domain/assets/chunk-file.js, this is wrong, returns the html of the web.
2nd time: ...domain/dist/assets/chunk-file.js, this is correct, returns chunk file.

My public folder structure looks like this:
/public/dist/assets/[chunk js and css]

And a similar problem with the CSS file of that chunk file, it is only loaded once like the 1st of the chunk file: ...domain/assets/css-of-chunk-file.css, this is not correct so Can't get css for chunk file.

And I added 'base' config like this:
Before:

build: {
  manifest: true,
  input: 'resources/js/index.jsx',
  outDir: 'public/dist',
  copyPublicDir: false
},

After:

 base: '/dist/',
 build: {
     manifest: true,
     input: 'resources/js/index.jsx',
     outDir: 'public/dist',
     copyPublicDir: false
  }

Dynamic chunks are now only loaded once and CSS is also loaded correctly. Hope it will be useful to someone.

@benmccann
Copy link
Collaborator

Fixed in Firefox 115: https://caniuse.com/link-rel-modulepreload

@github-actions github-actions bot locked and limited conversation to collaborators Nov 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
p4-important Violate documented behavior or significantly improves performance (priority)
Projects
None yet
Development

No branches or pull requests