Skip to content

Feature: allow same paradigm for lazy loading modules #839

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

Closed
wants to merge 1 commit into from

Conversation

blake-newman
Copy link
Member

As described in #837

This will keep the same paradigm in Vuex as rest of ecosystem for dynamically loading store modules:

store.registerModule('a', () => import('@/store/modules/a'))

mutations: { foo: mutationSpy }
})

store.registerModule(['a', 'b'], module()).then(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be store.registerModule(['a', 'b'], module).then(() => { ?
(don't call the module function here)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good spot, either way it will work

src/store.js Outdated
return module
}

if (isPromise(rawModule)) return rawModule.then(moduleResolved)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to check whether rawModule is a function or not for proposed API rather than Promise.

src/store.js Outdated
const moduleResolved = module => {
this._modules.register(path, module.default || module)
installModule(this, this.state, path, this._modules.get(path))
// reset store to update getters...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be redundant spaces before the comment 🙏

docs/en/api.md Outdated
@@ -156,10 +156,10 @@ const store = new Vuex.Store({ ...options })

Most commonly used in plugins. [Details](plugins.md)

- **`registerModule(path: string | Array<string>, module: Module)`**
- **`registerModule(path: string | Array<string>, module: Module|Function): Module|Function`**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to add a space around | ?

src/store.js Outdated
installModule(this, this.state, path, this._modules.get(path))
// reset store to update getters...
resetStoreVM(this, this.state)
return module
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to return the module here? I have no idea about use cases 🤔

src/store.js Outdated
}

if (isPromise(rawModule)) return rawModule.then(moduleResolved)
return moduleResolved(rawModule)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to always return Promise for consistency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you return a Promise, it will mean you will need to ensure that Vuex is used with Promises which may require pollyfils. This will ensure users don't need it if they are following standard flow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vuex requires Promise to work. So we can consider that Promise is always there 🙂

@ktsn
Copy link
Member

ktsn commented Jun 25, 2017

I have a small concern of this API. When some getters/actions/mutations are used before loading the corresponding module, it can be failed.
I guess the returning Promise of registerModule is there for handling such situation. But I wonder if this syntax is needed for Vuex since we need to handle the loading completions of async modules in the user land. Or are you considering like lazy dispatch/commit to handle it in Vuex?

@blake-newman
Copy link
Member Author

@ktsn I will run through the code and check some of points, some points may be right to change need to explore.

The main purpose of the API is to keep code splitting of modules simple to use and consistent with other parts of the Vue ecosystem.

Perhaps extra documentation for lazy loading is needed to make this feature more informative. I think having a section on Lazy loading dynamic modules would be great.

There is some gotchas users should be aware of, using the client asyncData mixins from SSR docs makes this easier. I was thinking that the client mixins are actually more fitting to be in main Vue docs as it can be used without SSR.

None the less we should highlight prerequisites and patterns required for this use, in the right environment this feature will be extremely powerful for applications that are large.

@blake-newman
Copy link
Member Author

Furthermore doing the latter of your comment would invoke technical complexity, as this mains if you use an action to get changes is will be deferred.

The current API just makes it easier to use code splitting but will require user to correctly manage registering modules.

@ktsn
Copy link
Member

ktsn commented Jun 25, 2017

@blake-newman
I see the point. I agree that the extra documentation for lazy loading would be informative 🙂

@nickmessing
Copy link
Member

@blake-newman, thank you for the effort, was thinking about this feature for some time, would help me a lot.

@blake-newman blake-newman force-pushed the feature/simplify-dynamic-registration branch 2 times, most recently from ff327e2 to 679a32e Compare June 26, 2017 16:43
types/index.d.ts Outdated
@@ -22,6 +22,7 @@ export declare class Store<S> {

registerModule<T>(path: string, module: Module<T, S>): void;
registerModule<T>(path: string[], module: Module<T, S>): void;
registerModule<T>(path: string[], module: () => Promise<Module<T, S>>): Promise<Module<T, S>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it returns Promise<void>

@@ -242,7 +242,57 @@ The module's state will be exposed as `store.state.myModule` and `store.state.ne

Dynamic module registration makes it possible for other Vue plugins to also leverage Vuex for state management by attaching a module to the application's store. For example, the [`vuex-router-sync`](https://github.com/vuejs/vuex-router-sync) library integrates vue-router with vuex by managing the application's route state in a dynamically attached module.

You can also remove a dynamically registered module with `store.unregisterModule(moduleName)`. Note you cannot remove static modules (declared at store creation) with this method.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove this? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks to be an accident... Will ammend

@blake-newman blake-newman force-pushed the feature/simplify-dynamic-registration branch 3 times, most recently from 3ff2951 to 6bb72f7 Compare June 27, 2017 17:37
@blake-newman blake-newman force-pushed the feature/simplify-dynamic-registration branch from 6bb72f7 to cfb3c65 Compare June 27, 2017 17:39
Copy link
Member

@ktsn ktsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@yyx990803
Copy link
Member

I like the idea but I'm not sure if this is entirely necessary, because reigsterModule is always used imperatively, you can already do:

asyncData (store) {
  return import('./foo.module.js')
    .then(m => store.registerModule('foo', m))
    .then(() => store.dispatch('foo/bar'))
}

Or

async asyncData (store) {
  store.registerModule('foo', await import('./foo.module.js'))
  await store.dispatch('foo/bar')
}

The primary reason for the async component syntax is because components need to be used in declarative scenarios where await or Promise resolving are not available. If we were to support async vuex modules with the same syntax, it makes more sense to support it when declaring stores:

const store = new Vuex.Store({
  modules: {
    foo: import('./foo.module.js')
  }
})

But this is extremely tricky to get right, since the state access is synchronous.

@blake-newman
Copy link
Member Author

@yyx990803 agree. Probably just needs documentation rather than internal changes. IMO in hindsight this doesn't add much value other than syntax sugar that only abstracts implementation detail that is truly not that much more difficult.

@alexjoverm
Copy link
Contributor

I agree on this is not necessary, but I consider that if it doesn't cause any trouble, it's a nice syntax sugar added.

When writing Lazy load in Vue using Webpack's code splitting, I could easily realize it's easy by doing it in the way Evan mentioned. But some people were expecting it to work in the same way than components or router (even some though registerModule understand import function already).

Aside: does it make sense to lazy load modules on declaration? I think the use case is to load them under a determined condition

@kiaking kiaking deleted the feature/simplify-dynamic-registration branch April 23, 2020 04:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants