Vue: Add some new lifecycle hooks for components

Created on 11 Nov 2016  Â·  9Comments  Â·  Source: vuejs/vue

Hello,

I would like to propose add some some new lifecycle hooks for components including async ones.

Now it too difficult to know when async components are loaded and you need to add some wrappers:

https://github.com/vuejs/vue/blob/dev/test/unit/features/component/component-async.spec.js#L62
https://github.com/vuejs/vue/blob/dev/test/unit/features/component/component-async.spec.js#L70

It would nice to use built in lifecycle hooks instead:

import Vue from 'vue';

const vm = new Vue({
    components: {
        component1: resolve => {
            require(['./component1'], resolve);
        },
        component2: resolve => {
            require(['./component2'], resolve);
        },
        component3: resolve => {
            require(['./component3'], resolve);
        }
        // etc
    },
    ready() {
        console.log('All components are loaded', components);
        // For instance, you may use `expect` here for tests
    }
});

vm.$mount();
feature request

Most helpful comment

@posva When you have one async component it will be easy to handle it, but when you have more than one async components it will be difficult 😢.

All 9 comments

Or maybe a hook each time a component is resolved:

resolved (component: ComponentOptions, finished: boolean) {
}

Currently you can achieve this by using a mixin and emitting events: https://jsfiddle.net/posva/0erqgsgb/

Do you have any other use cases other than testing? I was thinking about lazy loadin some components and allowing the parent Component to know whether they have been loaded or not so it can display different content waiting for them

@posva Thanks for the response.

I'm currently using mixin as workaround and it uses different approach:

readyMixin.js

export default {
    beforeCreate: function () {
        const promises = [];

        for (const name in this.$options.components) {
            const value = this.$options.components[name];

            if (typeof value === 'function' && name !== this.$options.name) {
                promises.push(new Promise(done => {
                    this.$options.components[name] = resolve => {
                        value.call(this, component => {
                            resolve(component);

                            done();
                        });
                    };
                }));
            } else {
                promises.push(Promise.resolve())
            }
        }

        Promise.all(promises).then(() => {
            if (typeof this.$options.ready === 'function') {
                this.$options.ready.call(this);
            }
        });
    }
}
it('async', done => {
    const vm = new MainComponent({
        ready() {
            expect(this.$el.querySelector('.async-1')).not.toBeNull();

            done();
        }
    });

    vm.$moun(document.createElement('vm'));
});

It is dirty now and I'm going to to try different approaches (including yours).

The main purpose is to have ability to know when async component is loaded/resolved.

If we have main component which includes some async ones, we will not know when they are loaded / resolved.

It is difficult to test main component and create some additional behavior (for instance, show/hide waiting content).

You need to create some additional wrappers/mixins, but it would be nice to have such functionality out of the box.

@posva When you have one async component it will be easy to handle it, but when you have more than one async components it will be difficult 😢.

@mdreizin I actually find your mixin approach quite plausible - since this is not a super common use case and can be handled in userland, I think it's better to leave it in userland.

@yyx990803 @posva Thanks guys! Currently I have found a workaround and if I create really common solution I will post here.

@yyx990803 can we get back to this? Kind of having a problem with dynamic components where static or async component can be passed and there is absolutely no way I can find out when all children finished rendering. And the parent component must execute code when all of the child components are done loading. Problem is, parent lifecycle is done before children created is called and the solution @mdreizin posted does not work because the components aren't known.

@donnysim What's preventing you from applying a mixin that would add a beforeCreate hook on any component?

@Akryum kind of requires me to know how many components to wait for loading and so requires 2 different implementations of the code, because the second time you enter the page, the components are already resolved and the normal lifecycle runs.

So the big problem is the <component :is= as the component will not appear in any list until it is resolved, and that is too late for any lifecycle to detect what components or how many are not loaded yet. In this case I kind of can work around this problem by injecting global mixin that emits mounted/created events on all components and count how many I'm loading in the loop, but sadly this is not the only case where async components increase difficulty for dealing with lifecycle where something needs to happen on mounted event, but async components get created after mounted.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lmnsg picture lmnsg  Â·  3Comments

aviggngyv picture aviggngyv  Â·  3Comments

fergaldoyle picture fergaldoyle  Â·  3Comments

seemsindie picture seemsindie  Â·  3Comments

bfis picture bfis  Â·  3Comments