vuejs / rfcs

RFCs for substantial changes / feature additions to Vue core
4.87k stars 546 forks source link

Lazy load for components #185

Closed rhengles closed 4 years ago

rhengles commented 4 years ago

Full Rendered Proposal

CyberAP commented 4 years ago

Lazy load for components is already supported in Vue 2 by defining component as a function (foo: () => import('./Foo.vue')) and in Vue 3 via RFC #26.

yyx990803 commented 4 years ago

This has been in Vue 2 since the very beginning: https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components

rhengles commented 4 years ago

This has left out the part about "no webpack" and "no build step". Could I get an answer about that? I wrote extensively about it on "motivation".

rhengles commented 4 years ago

@CyberAP Also, about RFC #26, it requires that you predefine the component names. My proposal doesn't have this limitation. The compiler finds a component name in the template and calls a function to get that component definition. All I'm asking is that my code can provide that function. Why are we so constrained with build tools? I'm sure there's an audience that would prefer to get rid of them if possible.

The rfc template mentions that increase of conplexity is a concern for Vue codebase. Why then the complexity of webpack is shoven down our throats? I'm trying to reduce the complexity of the final application. This is such a simple change, what is the motivation to decline it? I honestly can't understand.

CyberAP commented 4 years ago

Sorry I don't understand what exactly are you proposing from reading the rendered proposal. If you need runtime template compilation it is already possible. You don't have to define a component name in order to create a component in runtime:

<component :is="{ template: `<div>hello world</div>` }" />

It also does not require a build step.

yyx990803 commented 4 years ago

The API doesn't have a hard requirement on the build setup. You can do anything inside the async component factory, including custom AJAX/fetch requests.

rhengles commented 4 years ago

@CyberAP what? How would that resemble anything that looks like a organized codebase? You mean I have to replace every single component call into < component is="foo">< /component>? Really? Every single one?? You're telling me I can't have simply < foo>< /foo>?

That would look very strange, maybe ugly. Probably ugly. See, people use < component is=""> when they don't know beforehand what component will be rendered. When I call < foo>, I know I want < foo>, no need to repeat it again in the options : components: { foo: { getFoo } }, because that list will get big and tedious to be constantly updating.

I'm sorry if I come across as unpleasant or rude, but I can't reasonably think you have seen my examples from the answers I got. For instance, this:

https://github.com/arijs/vue-next-example/blob/master/js/index.js

That example is a little too succint, because the only component getting loaded is < app--root>< /app--root>. But assume I have several dozens of components, and all and each of them will be loaded in this manner.

When a template finds < app--foo>< /app--foo>, I need it to load by ajax without defining it in the "components:{}" object, and it is possible.

privatenumber commented 4 years ago

A build step (eg. Webpack) should only be necessary for compiling the SFC. You might not even need it considering Codepen seems to offer client-side support (vueify?).

Here's an example using async components without a build: https://jsfiddle.net/hirokiosame/076s9em4/

Here's an example using globally registered async components (not in components hash): https://jsfiddle.net/hirokiosame/076s9em4/3/

Here's an example that resolves async components globally & dynamically: https://jsfiddle.net/hirokiosame/v0s4zron/

(I simulate an external file using URL.createObjectURL(new Blob(...)), which you can ignore)

Hope that helps

rhengles commented 4 years ago

Thank you @privatenumber for your genuine effort into writing those examples and trying to help me. 🙏

Wait, what? There exists a function called

resolveComponent?

😳😱 That's exactly what I needed!!!!!!

However, there's one small issue - it doesn't fallback to the standard resolving of components if I return undefined.

Example:

Vue.resolveComponent = function(name) {
    console.log('ResolveComponent', name);
};
// output:
// => ResolveComponent block--header
// => ResolveComponent router-view
// => [Vue warn]: Invalid vnode type when creating vnode: undefined. 
//  at <Anonymous> 
//  at <AsyncComponentWrapper>

How do I resolve router-view and every other component created by plugins?

rhengles commented 4 years ago

Ah, I got it:

var originalResolveComponent = Vue.resolveComponent;

Vue.resolveComponent = function(name) {
    console.log('ResolveComponent', name);
    return originalResolveComponent.apply(this, arguments);
};

I ❤ U @privatenumber !

After all these years, this was the answer... I'm sad that this wasn't shown before.

rhengles commented 4 years ago

@privatenumber @CyberAP

Now I'm having an issue trying to combine my solution above with <component>.

Here's an reproduction link: https://jsbin.com/bejupuyuli/edit?html,js,console,output

Can anyone shine a light on this issue? Is it related to Vue.resolveDynamicComponent() ?

Here's a sample code. The static works, the dynamic doesn't.

function compAsyncTimeout(comp, time) {

return Vue.defineAsyncComponent(function() {
  return new Promise(function(resolve) {
    setTimeout(resolve, time, comp);
  });
});

}

var MyComp = {
  foo: compAsyncTimeout({
    template: '<div class="foo">foo component resolve async</div>'
  }, 3000)
};

var originalResolveComponent = Vue.resolveComponent;

Vue.resolveComponent = function(name) {
  return MyComp[name] || originalResolveComponent(name);
};

Vue.createApp({
  template: "\
  <div class=\"app-root\">\
    <p>static:</p>\
    <foo></foo>\
    <p>dynamic:</p>\
    <component is=\"foo\"></component>\
  </div>\
  "
}).mount('#app');
rhengles commented 4 years ago

@privatenumber @CyberAP @underfin

Oh, I had to extend resolveDynamicComponent as well. I don't understand the relation between RC and RDC. Why doesn't RDC consume the output of RC by default?

var originalRDC = Vue.resolveDynamicComponent;

Vue.resolveDynamicComponent = function(name) {
    var res = Vue.resolveComponent(name);
    if (!res || 'string' === typeof res) {
        return originalRDC.apply(this, arguments);
    } else {
        return res;
    }
};
rhengles commented 4 years ago

@privatenumber @CyberAP @underfin

Since today is a two-month "anniversary" of last post, I would be very grateful if the purpose of the resolveDynamicComponent function would be explained. The docs don't mention it and I couldn't find the reasoning by search, maybe if you could just point me in the right direction that'd already be enough.

Thanks!

privatenumber commented 4 years ago

I'm not apart of the core team or have any involvement with building Vue 3, and I'm still using Vue 2 in my projects so I'm probably not the best person to ask.

Based on the source code (resolveComponent & resolveDynamicComponent), it seems like the differences are:

There's probably more if you dig deeper.


vue/rfcs is a place to propose changes to Vue. When you have questions in the future, try the forums, chats, or Stack Overflow (as recommended at the top of the page here). People that are proactively looking to help browse these places so you'll get more traffic there.

Alternatively, if it's a simple question "What's the difference between Vue 3's RDC and RC?", you might have some luck tweeting at some of the core members -- they're very friendly and helpful.

PS If you want more responses, be less wordy with posts as most people are busy and want to skim. A lot of people won't even read if it's too long.

Good luck