Async component not rendering in SSR bundle, due to `default` export being missed
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).
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.