Skip to content

feat(build): avoid settling seen preload dependencies for performance - #23446

Merged
sapphi-red merged 5 commits into
vitejs:mainfrom
StirStudios:perf/preload-pending-promises
Sep 8, 2026
Merged

feat(build): avoid settling seen preload dependencies for performance#23446
sapphi-red merged 5 commits into
vitejs:mainfrom
StirStudios:perf/preload-pending-promises

Conversation

@StirStudios

@StirStudios StirStudios commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

perf(build): avoid settling empty preload dependency results

The runtime preload helper maps every dependency result through its allSettled compatibility implementation. Already-seen dependencies, existing stylesheet links and newly inserted JavaScript preload links return undefined, so each still creates a resolved promise and settlement callback even though there is no pending work to await.

Filter only undefined entries before the settlement mapper. The preload loop still creates the same links. Pending CSS loads remain awaited, and the existing error event/cancellation behavior and browser compatibility implementation are preserved.

The build regression loads the CSS-bearing hello fixture twice and checks that these empty results are not passed to Promise.resolve. It fails against stock Vite 8.2.2 (three calls) and passes with this change.

Validation against current main (68aeb8a):

  • Unit tests: 985 passed, 3 skipped.
  • Selected preload, CSS splitting/dynamic import, CSP and chunk import map build-browser tests: 47 passed, 5 skipped.
  • JS source-map build tests: 8 passed. Updated the preload snapshot for the helper length change; decoded mappings preserve all original source locations, with generated columns shifting by 22 characters.
  • Vite build, type checking and targeted lint passed.
  • Representative Nuxt production consumer: three interleaved mobile Lighthouse runs, median TBT 607 → 453 ms; LCP 2.04 → 1.99 s; 162 script requests in both variants. This is a local case study, not a general performance guarantee.
  • CSS success/failure, canceled preload errors, duplicate dependencies, module rejection and CSP nonce behavior were also exercised in the diagnostic.

Related work checked: #19805 adds the compatibility implementation retained here; #19722 and #19757 address different preload injection/chunk-mapping concerns. Latest stable 8.2.2 and main were checked on September 6, 2026; no equivalent fix was found.

The source regression was first verified failing/passing against v8.2.2. The change was then applied to current main and the checks above rerun. Full cross-platform CI and unrelated browser playground suites were not run locally.

@sapphi-red

Copy link
Copy Markdown
Member
  • Representative Nuxt production consumer: three interleaved mobile Lighthouse runs, median TBT 607 → 453 ms; LCP 2.04 → 1.99 s; 162 script requests in both variants. This is a local case study, not a general performance guarantee.

Is there a project I can test this with?

@StirStudios

StirStudios commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Yes @sapphi-red — I've prepared a standalone standard Nuxt reproduction here:
https://github.com/StirStudios/vite/tree/repro/nuxt-preload-23446

It uses Nuxt 4.5.2 with 48 ordinary lazy Vue components and scoped CSS, without Drupal, our layer, custom chunking or artificial delays. stock/ and patched/ have identical app source and resolved package versions; the latter applies this change to Vite 8.2.2 through pnpm. The README includes build/run instructions, a browser regression test, and an optional interleaved Lighthouse runner.

The browser test observed 144 Promise.resolve(undefined) calls on stock versus 0 patched, with 52 script requests and 48 stylesheet links in both, and successful hydration/interactivity. The instrumentation is only used for that mechanism check, not the timing runs.

To distinguish this from the quoted production result: this is a reduced synthetic consumer, not the original site. Its three-run local median TBT was 90.15 → 80.90 ms, and LCP 660.72 → 651.29 ms (Lighthouse 13.4.1, applied mobile throttling). All six samples are included. This does not reproduce the original 154 ms reduction or establish a general performance guarantee; it does give you a runnable Nuxt example of the extra settlements. The existing Vite playground regression in this PR remains the focused correctness test.

@sapphi-red sapphi-red changed the title perf(build): avoid settling empty preload dependency results feat: avoid settling seen preload dependencies for performance Sep 8, 2026
@sapphi-red sapphi-red added the p2-nice-to-have Not breaking anything but nice to have (priority) label Sep 8, 2026
@sapphi-red sapphi-red changed the title feat: avoid settling seen preload dependencies for performance feat(build): avoid settling seen preload dependencies for performance Sep 8, 2026

@sapphi-red sapphi-red left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@sapphi-red

Copy link
Copy Markdown
Member

/ecosystem-ci run

@pkg-pr-new

pkg-pr-new Bot commented Sep 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vitejs/plugin-legacy

pnpm add https://pkg.pr.new/@vitejs/plugin-legacy@23446 -D
npm i https://pkg.pr.new/@vitejs/plugin-legacy@23446 -D
yarn add https://pkg.pr.new/@vitejs/plugin-legacy@23446.tgz -D

vite

pnpm add https://pkg.pr.new/vite@23446 -D
npm i https://pkg.pr.new/vite@23446 -D
yarn add https://pkg.pr.new/vite@23446.tgz -D

commit: 6aa4fa1

@vite-ecosystem-ci

Copy link
Copy Markdown

📝 Ran ecosystem CI on abf4a55: Open

suite result latest scheduled
react-router failure failure
netlify-vite-plugin failure failure
qwik failure failure
sveltekit failure failure
storybook failure success
vitest failure failure
tanstack-start ⏹️ cancelled ⏹️ cancelled
vite-plugin-rsc failure failure

module-federation, vite-environment-examples, analogjs, vike, waku, nitro, nuxt, vite-plugin-cloudflare, vite-plugin-vue, laravel, vinext, vite-plugin-react, quasar, marko, unocss, vite-plugin-pwa, vite-setup-catalogue, vite-plugin-svelte, vuepress, vitepress, astro

@sapphi-red
sapphi-red merged commit e6f6b3e into vitejs:main Sep 8, 2026
28 checks passed
StirStudios added a commit to StirStudios/nuxtjs-drupal-stir that referenced this pull request Sep 11, 2026
Vite 8.3.0 ships vitejs/vite#23446 (the preload-bookkeeping fix our
patch stood in for), so pnpm update was failing with
ERR_PNPM_UNUSED_PATCH once the dependency tree moved past 8.2.2.
Removed the patch, its pnpm-workspace.yaml entry, and its README
section; vite now resolves cleanly to 8.3.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
renovate Bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Sep 12, 2026
| datasource | package | from  | to    |
| ---------- | ------- | ----- | ----- |
| npm        | vite    | 8.2.0 | 8.3.0 |


## [v8.3.0](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#830-2026-09-10)

##### Features

- **build:** avoid settling seen preload dependencies for performance ([#23446](vitejs/vite#23446)) ([e6f6b3e](vitejs/vite@e6f6b3e))

##### Bug Fixes

- handle CRLF line endings in code frame positions ([#23219](vitejs/vite#23219)) ([9913672](vitejs/vite@9913672))
- only treat whole `node_modules` path segments as dependencies (fix [#17467](vitejs/vite#17467)) ([#23437](vitejs/vite#23437)) ([ef0dc17](vitejs/vite@ef0dc17))

##### Performance Improvements

- **proxy:** pre-compile context matchers at server creation ([#23263](vitejs/vite#23263)) ([8abf700](vitejs/vite@8abf700))


## [v8.2.2](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-822-2026-08-20-small)

##### Features

- **deps:** widen `@vitejs/devtools` peer range to v0.5.0 ([#23302](vitejs/vite#23302)) ([495d9ff](vitejs/vite@495d9ff))

##### Bug Fixes

- **bundled-dev:** handle lazy request error ([#23291](vitejs/vite#23291)) ([3ba026d](vitejs/vite@3ba026d))
- **bundled-dev:** hot update through circular imports instead of reloading ([#23259](vitejs/vite#23259)) ([3dbddef](vitejs/vite@3dbddef))
- **config:** resolve sourcemap paths against sourcemap location ([#23239](vitejs/vite#23239)) ([05a003e](vitejs/vite@05a003e))
- **css:** don't pass empty targets to lightningcss ([#23295](vitejs/vite#23295)) ([2804636](vitejs/vite@2804636))
- **define:** fix match escaped dots to support $-prefixed define keys ([#23249](vitejs/vite#23249)) ([dcf88bd](vitejs/vite@dcf88bd))
- **deps:** update all non-major dependencies ([#23217](vitejs/vite#23217)) ([ba958bd](vitejs/vite@ba958bd))
- **deps:** update rolldown-related dependencies ([#23218](vitejs/vite#23218)) ([83ecb2c](vitejs/vite@83ecb2c))
- **module-runner:** exclude completed modules from in-flight cycle detection (fix [#22999](vitejs/vite#22999)) ([#23009](vitejs/vite#23009)) ([d9b10a9](vitejs/vite@d9b10a9))
- **optimizer:** close custom extension analysis bundles ([#23207](vitejs/vite#23207)) ([8fb7675](vitejs/vite@8fb7675))
- reduce Windows 8.3-short-name detection false-positives ([#23066](vitejs/vite#23066)) ([02cffa9](vitejs/vite@02cffa9))
- respect `resolve.preserveSymlinks` when resolving root (fix [#23197](vitejs/vite#23197)) ([#23198](vitejs/vite#23198)) ([8413052](vitejs/vite@8413052))
- **ssr:** rewrite computed key of destructing parameter ([#23307](vitejs/vite#23307)) ([9db0b61](vitejs/vite@9db0b61))
- **vite:** update outdated upstream file links in license comments ([#23285](vitejs/vite#23285)) ([c0f2fc6](vitejs/vite@c0f2fc6))

##### Documentation

- **build:** note cssTarget precedence ([#23200](vitejs/vite#23200)) ([a20a35e](vitejs/vite@a20a35e))

##### Miscellaneous Chores

- fix ts errors in build test cases ([#23209](vitejs/vite#23209)) ([a0cfcf7](vitejs/vite@a0cfcf7))

##### Code Refactoring

- use JSON import attributes instead of readFileSync in constants ([#23258](vitejs/vite#23258)) ([1d9fa39](vitejs/vite@1d9fa39))
- use named regex constants over inline literals ([#22964](vitejs/vite#22964)) ([5c1c6c6](vitejs/vite@5c1c6c6))

##### Tests

- **define:** close rolldown bundler after generate ([#23231](vitejs/vite#23231)) ([b4d66fe](vitejs/vite@b4d66fe))
- **module-runner:** add TLA circular import case ([#23299](vitejs/vite#23299)) ([4a261f2](vitejs/vite@4a261f2))
- **module-runner:** simplify server-hmr tests ([#23300](vitejs/vite#23300)) ([599b44b](vitejs/vite@599b44b))
- **ssr:** add destructing assignment case for moduleRunnerTransform ([#23308](vitejs/vite#23308)) ([cb77e2a](vitejs/vite@cb77e2a))

##### Build System

- use JSON import attributes instead of readFIleSync in rolldown configs ([#23251](vitejs/vite#23251)) ([d615bcd](vitejs/vite@d615bcd))


## [v8.2.1](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-821-2026-08-06-small)

##### Bug Fixes

- **build:** make client chunkImportMap work with `sharedPlugins: true` ([#23184](vitejs/vite#23184)) ([15f0307](vitejs/vite@15f0307))
- **bundled-dev:** inject client script tag before chunk scripts ([#23161](vitejs/vite#23161)) ([eac0cc8](vitejs/vite@eac0cc8))
- **css:** don't re-run lightningcss visitor during minify (fix [#23146](vitejs/vite#23146)) ([#23147](vitejs/vite#23147)) ([de041a7](vitejs/vite@de041a7))
- **deps:** update all non-major dependencies ([#23136](vitejs/vite#23136)) ([14454fd](vitejs/vite@14454fd))
- **deps:** update rolldown-related dependencies ([#23070](vitejs/vite#23070)) ([7ac6f7f](vitejs/vite@7ac6f7f))
- don't mutate the user config when resolving the lib entry from the top-level `input` ([#23135](vitejs/vite#23135)) ([b4bf596](vitejs/vite@b4bf596))
- handle shebang ending with uncommon line terminators ([#23038](vitejs/vite#23038)) ([17f7b2f](vitejs/vite@17f7b2f))
- **server:** use a random port when port is 0 ([#23158](vitejs/vite#23158)) ([fddf4ea](vitejs/vite@fddf4ea))

##### Performance Improvements

- **css:** look up pure CSS chunks through a Set ([#23114](vitejs/vite#23114)) ([1331b0b](vitejs/vite@1331b0b))

##### Documentation

- **build:** fix incomplete `@default` for build.minify ([#23177](vitejs/vite#23177)) ([ef02435](vitejs/vite@ef02435))

##### Miscellaneous Chores

- **deps:** update dependency rolldown-plugin-dts to ^0.28.0 ([#23137](vitejs/vite#23137)) ([4adc1e7](vitejs/vite@4adc1e7))
- **deps:** update dependency strip-literal to v4 ([#23140](vitejs/vite#23140)) ([9db65ce](vitejs/vite@9db65ce))

##### Code Refactoring

- **bundled-dev:** avoid injecting server values in the bundle ([#22967](vitejs/vite#22967)) ([23b8a08](vitejs/vite@23b8a08))
- **bundled-dev:** remove rolldown lazy stub module workaround ([#23129](vitejs/vite#23129)) ([e72036e](vitejs/vite@e72036e))

##### Tests

- **bundled-dev:** enable sourcemap playgrounds ([#23080](vitejs/vite#23080)) ([c2155fe](vitejs/vite@c2155fe))
- reduce logs ([#23138](vitejs/vite#23138)) ([7673c02](vitejs/vite@7673c02))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat: build p2-nice-to-have Not breaking anything but nice to have (priority) trigger: preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants