Subscribe on changes!

Async component not rendering in SSR bundle, due to `default` export being missed

avatar
May 17th 2023

Vue version

3.3.2

Link to minimal reproduction

https://github.com/AaronBeaudoin/vite-issue-ssr-vue-async

Steps to reproduce

See reproduction repository for steps.

The async component is not rendered after bundling because inside the defineAsyncComponent function the code to set the comp variable to the default export of the "module" is not being run.

When running, it is helpful to attach a debugger and break at the relevant code inside the defineAsyncComponent function so you can see the problem more clearly:

🔴 if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) {
  comp = comp.default;
}

The challenge for me is that I'm not 100% sure if this issue is Vue's problem, or if there is something not being done over on esbuild's side to ensure this condition gets tripped. This problem is a blocker for my project though, so I need to start with an issue somewhere and hope someone helps quickly, otherwise my project is just at a standstill because my production site depends on my async components rendering in SSR for SEO purposes.

What is expected?

At a high level: The async component renders. At a lower level: The "module" of the async component is detected as having a "default" export and the relevant code is run to properly set the comp variable inside of defineAsyncComponent.

What is actually happening?

At a high level: The async component does not render. At a lower level: The "module" of the async component has a "default" export, but this is not detected and the relevant code is not run. As a result, the ssrRender function never runs and the component is not rendered.

System Info

System:
    OS: macOS 13.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 106.42 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.19.0 - ~/.volta/tools/image/node/16.19.0/bin/node
    npm: 8.19.3 - ~/.volta/tools/image/node/16.19.0/bin/npm
  Browsers:
    Chrome: 112.0.5615.49
    Safari: 16.4
  npmPackages:
    vue: ^3.3.2 => 3.3.2

Any additional comments?

I originally ran into this bug while using vite-plugin-ssr, but I was able to narrow the issue down to the linked minimal reproduction. As a result, it is clear that this issue affects at the very least all SSR projects utilizing async components and needing to bundle their server-side code, at least with esbuild (Ex: Cloudflare Workers).

avatar
May 18th 2023

Update: I have discovered the root issue in my project. Unfortunately, it appears that esbuild does not add [Symbol.toStringTag]: "Module" in it's "internal exporting logic" when bundling without code splitting on async imports, which is necessary for deployment to certain platforms such as Cloudflare Workers. See esbuild#2664. This can be resolved by changing your async component import as follows:

// BEFORE
const Component = defineAsyncComponent(() => import("./Async.vue"));

// AFTER
const Component = defineAsyncComponent(() => import("./Async.vue").then(_ => _.default));

Or alternatively, Rollup has implemented the exact feature we are needing here.

However, I don't think this means that there is nothing to be done here on the Vue side. When I ran into this issue originally, there was no error or warning helping me understand why my async component was not rendering. Therefore, I think it would be helpful to do something to point users in the right direction in this case. Perhaps, when attempting to run comp.ssrRender later, if Vue doesn't find the function it can check comp.default.ssrRender and issue a warning. Or perhaps this is not a good solution. Either way, the core idea is to save people the 3 days I spent on this.