Repository nuxt.js

Pass the component definition as second argument to `asyncData` (and `fetch`)

antoinerey
0
antoinerey
commented 2 months ago

What problem does this feature solve?

First, a bit of background.

I'm building a Nuxt application but reorganized the whole directory structure to use self contained modules. I find it easier to maintain in the long run, and modules can be reused/shared with other applications easily.

These modules may include routes, components, configurations… but also Vuex modules. And to keep the application performances as high as possible, I'm splitting those modules into different chunks.

Basically, I got :

  • A main chunk, the application core (router, store, layout…)
  • A chunk for each route (layout, components, configuration, store module…)

A basic module looks like this :

  • A routes.js file
  • A store.js file
  • A Layout.vue file
  • Pages components

At the moment, the Layout component is responsible of lazy loading the store module. That way, the Vuex store is initialized with only the required modules. I'm achieving this by doing :

// Layout.vue

import module from './store.js'

export default {
  asyncData ({ store }) {
    if (!store.state.namespace) {
      store.registerModule('namespace', module)
    }
  }
}

The second step has been to move this logic into a custom mixin (while creating a custom Vue options merge strategy) :

// mixins/index.js

export const withStore = (namespace, module) => ({
  asyncData ({ store }) {
    if (!store.state[namespace]) {
      store.registerModule(namespace, module)
    }
  }
})
// Layout.vue

import module from './store.js'
import { withStore } from '@/mixins'

export default {
  mixins: [
    withStore('namespace', module)
  ]
}

This is better, but I'd like to go even further by adding a custom component options, and applying the mixin globally :

// mixins/index.js

export const withStore = {
  asyncData ({ store }) {
    const { namespace, module } = ... // <---- This is where I need more informations

    if (!store.state[namespace]) {
      store.registerModule(namespace, module)
    }
  }
}
// plugins/index.js

import Vue from 'vue'
import { withStore } from '@/mixins' 

Vue.mixin(withStore)
// Layout.vue

import module from './store.js'

export default {
  store: {
    namespace: 'namespace',
    module: module
  }
}

That's what causing me issues.

I'd like to access to the component options from the asyncData method.

What does the proposed changes look like?

As far as I understand, we can't access the component options from the asyncData method (Note: I'm not trying to access the component instance but the component options, which are statically defined).

My proposal is to pass the component definition as second argument to asyncData (and fetch to keep the same signature).

So, my previous mixin would be written as :

// mixins/index.js

export const withStore = {
  asyncData ({ store }, Component) {
    const { namespace, module } = Component.options.store

    if (!store.state[namespace]) {
      store.registerModule(namespace, module)
    }
  }
}

On one hand, this would solve my issue, and allow people to use component methods (this would still be undefined though). But on the other hand, this would still be undefined and most methods would crash. So, we may pass a subset of the component definition instead of passing the whole object.

What do you think about it ? Is there any other solution I didn't think of ?

I'd be happy to contribute and make a PR if this proposal is accepted. 😺

idea
0
galvez
1.5k
galvez
commented 2 months ago

@Atinux do you think this is worth pursuing?

0
Atinux
20.5k
Atinux
commented 2 months ago

Actually I like this idea and can bring more possibilities for plugins actually. I am also thinking of the nuxt-i18n module.

One thing that bother me is the fact that we can use asyncData or fetch with a callback as 2nd argument. But the more I think, the less I think callback is still relevant for asyncData and fetch.

Would need @pi0 and @clarkdo thoughts on this subject.

0
Capu-zennio
0
Capu-zennio
commented 21 days ago

@antoinerey I'm interested in this, but I'm not able to create a mixin that can accept asyncData. Do you mind sharing your custom merge strategy? I can access both functions but I have no way to pass the context as arguments, so the mixin is mostly useless.

0
antoinerey
0
antoinerey
commented 21 days ago

I was using this :

import Vue from 'vue'

Vue.config.optionMergeStrategies.asyncData = (to, from) => (...args) => {
  return Promise.all([
    to && to(...args),
    from && from(...args)
  ])
}

But, it was an experiment and may have some side effects.

0
antoinerey
0
antoinerey
commented a day ago

Did you get time to think about this proposal ? It is something you would accept a PR for ?

I'd be pleased to work on it, but I need some validation about the expected interface/usage.

@Atinux @pi0 @clarkdo

0
Informations
Feature RequestOpen
#c7531 - Created 2 months ago