Replies: 3 comments
|
Drop the top-level await from // i18n.js, alongside what's already there
export const setupI18n = async () => createI18n(await createI18nOptions());// main.js
import { setupI18n } from './i18n';
const bootstrap = async () => {
const app = createApp(App);
app.use(store);
app.use(await setupI18n());
app.mount('#app');
};
bootstrap();That alone fixes the blank page, and you can keep the lodash import exactly where it is. You've hit two separate problems that happen to look like one. The blank pageIt isn't the circular reference by itself. It's a module deadlock. The entry chunk starts evaluating, reaches This is plain ESM semantics, not Vite. The same graph with no Vite, no Rollup and no lodash deadlocks in the browser too: // entry.js
export const shared = 1;
const m = await import('./leaf.js');
console.log('never runs');
// leaf.js
import './entry.js';
export default 42;In Chrome that hangs silently, same as your app. Node deadlocks on it as well, but it's kinder about it: you get The tree shakinglodash 4.17.21 is CommonJS. No "module" field, no "exports" field, one But note your What I measuredYour repro,
Blank exactly when the cycle and the top-level await are both present. The console was empty in all seven runs. Two traps worth knowing. For the size, To your question, the chunking is expected Rollup behavior and the deadlock is standard ESM evaluation, so this isn't a Vite bug. It's a sharp edge of top-level await in an entry chunk. Consistent with that, |
|
I think there are actually two separate issues here: the circular chunk dependency is a consequence of how Rollup has to share the CommonJS lodash module, while the blank page is caused by the combination of that cycle with the top-level The important part is this in const option = await createI18nOptions();Your entry chunk starts evaluating and then suspends on the top-level At that point await import(`./locales/${locale.lang}.js`);Rollup has put some of the lodash/CommonJS machinery in the entry chunk, so the generated locale chunk ends up containing something equivalent to: import "./index-wFQsXaez.js";Now you effectively have: The entry is waiting for the locale to finish, while the locale is waiting for the entry's evaluation to finish. That's a module evaluation deadlock, so the application can remain blank without a normal runtime exception. A simple way to verify this is to remove the top-level // i18n.js
export const setupI18n = async () => {
const options = await createI18nOptions();
return createI18n(options);
};Then: // main.js
import { createApp } from "vue";
import App from "./App.vue";
import { store } from "./store";
import { setupI18n } from "./i18n";
const bootstrap = async () => {
const app = createApp(App);
app.use(store);
app.use(await setupI18n());
app.mount("#app");
};
bootstrap();This keeps the async boundary outside the module's top-level evaluation, so the locale chunk is allowed to load normally. Regarding this line: import { floor } from "lodash";the fact that If you want reliable per-function tree shaking, use an ESM build such as: npm install lodash-esand: import { floor } from "lodash-es";Or import the function directly: import floor from "lodash/floor";But I would treat that as a separate bundle-size improvement rather than the fix for the blank page. So I don't think the generated: locale -> index
index -> localerelationship by itself proves a Vite bug. Rollup is allowed to create shared-module/chunk dependencies like this. The problematic part is combining that graph with a top-level The most useful test is:
If the blank page disappears, that confirms the root cause is the ESM evaluation cycle rather than Vue or the unused lodash import itself. |
|
kaminagakur4 nailed the module deadlock fix. The lodash piece they did not address is also worth a precise answer. Two problems in your setup, in order of impact:
The prebundled index.js suspends at kaminagakur4's fix is correct: move the await into a function. Code: // i18n.js
import { createI18n } from 'vue-i18n'
import { useLocaleStoreWithOut } from './locale'
export const createI18nOptions = async () => {
const localeStore = useLocaleStoreWithOut()
const locale = localeStore.getCurrentLocale
const localeMap = localeStore.getLocaleMap
const defaultLocal = await import(`./locales/${locale.lang}.js`)
const message = defaultLocal.default ?? {}
return {
legacy: false,
locale: locale.lang,
fallbackLocale: locale.lang,
messages: { [locale.lang]: message },
}
}
export const setupI18n = async () => createI18n(await createI18nOptions())// main.js
import { setupI18n } from './i18n'
const bootstrap = async () => {
const app = createApp(App)
app.use(store)
app.use(await setupI18n())
app.mount('#app')
}
bootstrap()This breaks the suspension chain because main.js owns the entry point and no locale chunk tries to import it back.
Three fixes: A. Import from the modular lodash subpath: // instead of: import { floor } from 'lodash'
import floor from 'lodash/floor'
// or
import { floor } from 'lodash-es'
B. (If you must keep // vite.config.ts
export default defineConfig({
optimizeDeps: {
include: ['lodash'],
},
build: {
rollupOptions: {
// tell Rollup to treat lodash's default export as a namespace
// so named imports tree-shake correctly
treeshake: 'smallest',
},
},
})This is not a magic bullet. optimizeDeps.include forces Vite's dev pre-bundler to convert lodash to ESM on the fly, which fixes the dev-side resolution but does not change the production bundle path. In production, you need lodash-es. C. (Cleanest) Drop lodash entirely for const floor = Math.floorFor a single function import there is no reason to take the lodash dependency. Math.floor is identically defined and shipped with every JS runtime. For your zh-CN.js, none of these change the runtime — the floor import is unused in your snippet, so even the bare Combining both fixes (the deadlock + the lodash tree-shaking) gives you a working app with the correct bundle size. Order of operations:
Cross-references:
|
Uh oh!
There was an error while loading. Please reload this page.
vite-app-import-lodash-circular-reference-issue
vite-app-import-lodash-circular-reference-issue
In the module file for vue-i18n multi-language support, a lodash method is imported with
import { floor } from 'lodash';, but this lodash method is not used. After packaging with vite, it is found that tree-shaking does not work, and the entire lodash file is packaged into the final output JS. Moreover, after the vue app starts, the page goes blank, and no error messages are displayed in the browser console. Setting breakpoints in the output files reveals that the multi-language module JS imports the main JS file, and the methods in the main JS file dynamically import this multi-language module JS file, resulting in a circular reference issue.this is
ZH-CN.js, import floor function but not usethis is
i18n.jsthis is
main.jsthis code fragment is in vite build main js (
dist\assets\index-wFQsXaez.js) :this is vite build
ZH-CN.js:you can see
index-wFQsXaez.jsimportZH-CH.js, andZH-CN.jsimportindex-wFQsXaez.js, there is a circular reference issue.Is this phenomenon a bug?
All reactions