-
-
Notifications
You must be signed in to change notification settings - Fork 48
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
Alternative to confusing async component loading behavior #193
Comments
This is also tripped us up. We had quite a number of components doing DOM / $ref calculations and modifications on the I think the direction of these changes is good but we're missing the documentation around the side-effects of v2. We are also missing documentation for an easy "escape hatch" for this behaviour for larger projects |
I don't understand why they all have to be async imports in the first place. The file generated for dev looks like this: import Vue from 'vue'
import { wrapFunctional } from './utils'
const components = {
MyComponent: () => import('../../app/components/MyComponent.vue' /* webpackChunkName: "components/MyComponent" */).then(c => wrapFunctional(c.default || c)),
}
for (const name in components) {
Vue.component(name, components[name])
Vue.component('Lazy' + name, components[name])
} But it could just as easily generate static imports for non-lazy components: import Vue from 'vue'
import { wrapFunctional } from './utils'
import { MyComponent as _MyComponent } from '../../app/components/MyComponent.vue'
const components = {
MyComponent: _MyComponent,
LazyMyComponent: () => import('../../app/components/MyComponent.vue' /* webpackChunkName: "components/MyComponent" */).then(c => wrapFunctional(c.default || c)),
}
for (const name in components) {
Vue.component(name, components[name])
} Turns out you already have this anyway in import * as components from './index'
for (const name in components) {
Vue.component(name, components[name])
} |
Hi, @AaronBeaudoin thanks for explaining your concerns. For sure documentation needs to be improved for the migration guide. (nuxt/website-v2#1244, nuxt/website-v2#1434). Meanwhile, I will try to make the readme also more clear. Why loader is disabled in development mode? Fixing development performance issues (see #18) and edge cases (see next section) which was much wider than async import edge cases. Why it is stated It doesn't mean this option is unofficial nor that will be removed in nuxt2. But backs to edge cases the loader simply cannot detect components. Most notably:
Why async components have such issues? Unfortunately, they are mostly known vue2 issues waiting to be released or fixed. See vuejs/vue#11963 and vuejs/vue#11837. Also see nuxt/nuxt#8981 (comment) regarding mounted/ref issue. So can I use Absolutely! Considering performance and usage edge cases are fine. |
@KaelWD Seems a good idea to try. There is only one problem that was in contrast to our future development plans (lazy compilation support via vite). We can at least enable this behavior under a flag. |
Another edge case I just found with
I feel like I'd probably turn that off anyway. Having everything be async is probably fine for really basic apps, but as soon as you start touching the DOM manually or using |
As i mentioned most of async component edge cases back to known issues of vue@2. By using nuxt you are already using async components (pages). Also
Also for this an issue with reproduction would be nice 😊 |
If you ask me, this is the heart of the discussion. With async components it's very tough to reliably do anything that depends on the DOM, which most large applications have to do in one way or another. And as long as behavior in development doesn't match production, we're basically asking for unexpected bugs in our production applications. I can see how async components would be a better solution if they didn't have Vue issues, but considering all the bugs with async components that have yet to be resolved in Vue, it seems to me that they are simply too unstable to be the default behavior at the moment. The key idea I have to disagree with is that the loader edge cases are much wider than the async component edge cases. The issues with async components are huge, in my opinion.
These loader edge cases all seem like very predictable trade-offs to me. It makes perfect sense to me that the loader doesn't work in the scenarios you listed. Personally, I can easily live with these trade-offs. However, none of the issues I'm seeing pop up as a result of using async components seem intuitive to me. They were very difficult to debug and contradicted the documented behavior I expected from Vue components. I'd suggest that the trade-offs of async components are far worse than the trade-offs of using a loader, because from what I understand, the loader trade-offs are simply limitations, but the async component trade-offs are bugs. That makes the loader trade-offs expected, while the async component trade-offs are generally unexpected, but that's just my two cents. |
I fully understand your thoughts @AaronBeaudoin and it is unfortunate hearing inconveniences you occurred. But the fact is that there is no single choice fitting for all and choosing to disable loader was based on amount of issues reporting that components is not working with not easy way to debug either or even fix some (like only solution for jsx was explicitly using global/ dir) Choosing loader: false for default was ensuring we use an option that is more futuristic for supporting vite and fixing root causes in vue itself and meanwhile fixing dx issues originating from components module itself (so that once root cause is fixed we don't need breaking changes again) And it probably makes sense to enable it for development if project is depending on features that involve dom access. @KaelWD mentioned a good idea that when loader is disabled and during development, we can use non async imports. It makes issue appearing again for supporting lazy compiling but since it is not a case as of now, adding a flag would be nice (we can enable it bg default after trying it for a while for tradeoffs) . |
Can I somehow check that all asynchronous components have been loaded in the updated event? |
It's frustrating that I need to come back here a 3rd time, but here I am. I just wasted an entire day of work trying to find the source of a problem and it ended up being this exact same cause again. Please see the below CodeSandbox containing a minimal test case and think about how, in a complex project, you would have figured out what the core problem is. I guarantee a new user starting off with Nuxt would just uninstall and move on to React if they ran into an issue like this right off the bat. https://codesandbox.io/s/relaxed-hofstadter-4jdoy Simple Explaination of IssueIn the attached example, a minimal In the JS for the When the page component first renders, the path is to the How would one resolve this? I tried over a dozen ideas over the course of an entire day. In the end, the only way to resolve it was to either use |
@AaronBeaudoin I return to this issue every few days hoping for signs of progress. I have been using the Nuxt framework intermittently (work-permitting) for the past 3 years and this is the only significant regression I've had to deal with. I went from loving Does Next offer an equivalent of |
Hi, @lucadalli sorry to hear about that inconvenience. We are busy working on nuxt3 that's why sometimes features like this are delayed but PRs are always welcome. By regression, you mean async component issues in development? You can work around it by using |
@pi0 I understand that your plates are full. In fact, I don't have it enabled right now because it did not fix the issues I was facing. I will try re-enabling it. It should at least get me closer to my desired behavior. |
Thanks for explaining. In the meantime, I will try to implement #193 (comment) for using non-async imports in dev (without vite there is no perf improvement in dev). Still sharing more details about your current issue with loader would be appreciated. |
The Problem
In Nuxt components v2, components are dynamically imported with async. This change was a nightmare for me because it result in several issues which took the better part of several days to find the cause of.
Hard to Find Bug Resulting from the Problem
Here is the issue I had created on the Nuxt repository the first time I ran into the problem:
nuxt/nuxt#8981
The solution required me to write extra code in my custom component because I needed to ensure that
$el
existed before I could initialize agsap
animation. It was very counter-intuitive for me to not be able to assume that$el
was populated inside of themounted()
hook, instead having to write a conditional inside theupdated()
hook to check if the element was populated each time. Honestly not a great solution in my opinion.Also, this only broke I after I upgraded to v2 of Nuxt components, and unfortunately the migration guide was of no help to me because it didn't explain that components being automatically made global also meant they were being imported dynamically. I was totally in the dark on that detail.
Another Different Hard to Find Bug Resulting from the Problem
Today I just ran into another problem which I eventually discovered was resulting from async component loading. This time, my styling broke because I relied on the assumption that an
id
attribute would be present, but it wasn't due to this same async component loading. Nothing about this behavior was intuitive to me.Here is a simple Code Sandbox that shows the second problem in action:
https://codesandbox.io/s/boring-bassi-et0fb?file=/layouts/default.vue
What I'd Like to See
Documentation for Effects of Async Component Loading
First, we need a clear part of the documentation that explains the downsides of the async component loading so people don't spend hours trying to debug the source of weird issues. I imagine that 99% of Vue users expect
$el
to always exist in themounted()
hook, andid
attributes to never disappear without a clear cause. Any behavior that changes this makes for a very dangerous default, in my humble opinion.Official Support for
loader: true
In the issue I linked above, @pi0 revealed that we can set
loader: true
to re-enable v1 behavior. However, he also stated that this is "not recommended nor guaranteed to always work". I propose that this solution be made officially supported for those who wish to not lose sleep at night.Somewhere in
nuxt.config.js
to Exclude Specific Components from Async LoadingAdditionally, provide a way to list components in
nuxt.config.js
that should not be loaded in an async manner.Why not just
import
components manually? Random importing certain components here and there in a codebase to force them to not be loaded async is confusing, because there is not immediately any apparent reason why one component is manually imported and another isn't, unless clear comments are left. Anyone could remove the component import thinking they are just cleaning things up and end up breaking the application. Also, manually importing components defeats part of what I love about Nuxt components, which is cleaner code due to not having to import them manually.TL;DR / Summary
loader: true
officially supported, rename it toasync: false
or something like that so it's clear what it does, and document how and when to use it.staticComponents
orexcludeAsync
something like that where paths to components or maybe even entire directories of components can be specified to be excluded from being loaded dynamically with async.Bottom line, let's not force people to stop using the
mounted()
hook or manually import components (negating the benefit of Nuxt components) to make initial component state consistent in their applications.Thanks so much for your hard work on Nuxt, and I appreciate your consideration of my suggestion.
The text was updated successfully, but these errors were encountered: