npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@skryu/vuex-class-modules

v1.4.3

Published

Typescript class decorators for class-style vuex modules.

Downloads

6,694

Readme

vuex-class-modules

This is yet another package to introduce a simple type-safe class style syntax for your vuex modules, inspired by vue-class-component.

npm

New

  • Add created lifecycle to replace the constructor initialization logic. Constructor initialization will lost this bind if you operate within an asynchronous function.
class Foo extends VuexModule {
  // ...

  public created() {
    // Do something here
  }
}

Installation

npm install @skryu/vuex-class-modules

And make sure to have the --experimentalDecorators flag enabled.

Both a commonjs and a esm module build are published. If you have a webpack-based setup, it will use the esm modules by default.

Usage

Vuex modules can be written using decorators as a class:

// user-module.ts
import { VuexModule, Module, Mutation, Action } from "vuex-class-modules";

@Module
class UserModule extends VuexModule {
  // state
  firstName = "Foo";
  lastName = "Bar";

  // getters
  get fullName() {
    return this.firstName + " " + this.lastName;
  }

  // mutations
  @Mutation
  setFirstName(firstName: string) {
    this.firstName = firstName;
  }
  @Mutation
  setLastName(lastName: string) {
    this.lastName = lastName;
  }

  // actions
  @Action
  async loadUser() {
    const user = await fetchUser();
    this.setFirstName(user.firstName);
    this.setLastName(user.lastName);
  }
}

// register module (could be in any file)
import store from "path/to/store";
export const userModule = new UserModule({ store, name: "user" });

The module will automatically be registered to the store as a namespaced dynamic module when it is instantiated. (The modules are namespaced to avoid name conflicts between modules for getters/mutations/actions.)

The module can then be used in vue components as follows:

// MyComponent.vue
import Vue from "vue";
import { userModule } from "path/to/user-module.ts";

export class MyComponent extends Vue {
  get firstName() {
    return userModule.firstName; // -> store.state.user.firstName
  }
  get fullName() {
    return userModule.fullName; // -> store.getters["user/fullName]
  }

  created() {
    userModule.setFirstName("Foo"); // -> store.commit("user/setFirstName", "Foo")
    userModule.loadUser(); // -> store.dispatch("user/loadUser")
  }
}

What about rootState and rootGetters?

There are two ways to access other modules within a module, or dispatch actions to other modules.

  1. Simply import the instantiated module (suitable if the modules are instantiated in the same file as they are defined):
// my-module.ts

// import the module instance
import { otherModule } from "./other-module";

@Module
class MyModule extends VuexModule {
  get myGetter() {
    return otherModule.foo;
  }

  @Action
  async myAction() {
    await otherModule.someAction();
    // ...
  }
}
  1. The other module can be registered through the constructor (suitable if the modules are instantiated elsewhere)
// my-module.ts

// import the class, not the instance
import { OtherModule } from "./other-module";

@Module
export class MyModule extends VuexModule {
  private otherModule: OtherModule;

  constructor(otherModule: OtherModule, options: RegisterOptions) {
    super(options);
    this.otherModule = otherModule;
  }

  get myGetter() {
    return this.otherModule.foo;
  }

  @Action
  async myAction() {
    await this.otherModule.someAction();
    // ...
  }
}

// register-modules.ts
import store from "path/to/store";
import { OtherModule } from "path/to/other-module";
import { MyModule } from "path/to/my-module";

export const otherModule = new OtherModule({ store, name: "otherModule" });
export const myModule = new MyModule(otherModule, { store, name: "myModule" });

The local modules will not be part of the state and cannot be accessed from the outside, so they should always be declared private.

myModule.otherModule; // -> undefined

The store.watch function

Vuex can also be used ouside of vue modules. To listen for changes to the state, vuex provides a watch method.

This api is also provided by vuex-class-modules under the method name $watch to prevent name collisions. For example you can do:

import store from "./store";
import { MyModule } from "./my-module";

const myModule = new MyModule({ store, name: "MyModule" });
myModule.$watch(
  (theModule) => theModule.fullName,
  (newName: string, oldName: string) => {
    // ...
  },
  {
    deep: false,
    immediate: false,
  }
);

and to unwatch:

const unwatch = myModule.$watch(...);
unwatch();

Register options

  • name [required]: Name of the module
  • store [required]: The vuex store - which can just be instantiated as empty:
// store.ts
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({});

Module options

The module decorator can also accept options:

  • generateMutationSetters [optional, default=false]: Whether automatic mutation setters for the state properties should be generated, see Generate Mutation Setters.

Example

The vuex shopping cart example rewritten using vue-class-component and vuex-class-modules can be found in the example directory. Build the example using:

npm run example

Caveats of this

As for vue-class-component this inside the module is just a proxy object to the store. It can therefore only access what the corresponding vuex module function would be able to access:

@Module
class MyModule extends VuexModule {
  foo = "bar";

  get someGetter() {
    return 123;
  }
  get myGetter() {
    this.foo; // -> "bar"
    this.someGetter; // -> 123
    this.someMutation(); // undefined, getters cannot call mutations
    this.someAction(); // -> undefined, getters cannot call actions
  }

  @Mutation
  someMutation() {
    /* ... */
  }
  @Mutation
  myMutation() {
    this.foo; // -> "bar"
    this.someGetter; // -> undefined, mutations dont have access to getters
    this.someMutation(); // -> undefined, mutations cannot call other mutations
    this.someAction(); // -> undefined, mutations cannot call actions
  }

  @Action
  async someAction() {
    /* ... */
  }
  @Action
  async myAction() {
    this.foo; // -> "bar"
    this.someGetter; // -> 123
    this.myMutation(); // Ok
    await this.someAction(); // Ok
  }
}

Local Functions

The module can have non-mutation/action functions which can be used inside the module. As for local modules, these functions will not be exposed outside the module and should therefore be private. this will be passed on to the local function from the getter/mutation/action.

@Module
class MyModule extends VuexModule {
  get myGetter() {
    return myGetterHelper();
  }
  private myGetterHelper() {
    // same 'this' context as myGetter
  }

  @Mutation
  myMutation() {
    this.myMutationHelper();
  }

  // should be private
  myMutationHelper() { /* ... */}
}
const myModule = new MyModule({ store, name: "myModule });
myModule.myMutationHelper // -> undefined.

Generate Mutation Setters

As I often find myself writing a lot of simple setter mutations like

@Module
class UserModule extends VuexModule {
  firstName = "Foo";
  lastName = "Bar";

  @Mutation
  setFirstName(firstName: string) {
    this.firstName = firstName;
  }
  @Mutation
  setLastName(lastName: string) {
    this.lastName = lastName;
  }
}

a module option generateMutationSetters has been added, which when enabled will generate a setter mutation for each state property. The state can then be modified directly from the actions:

@Module({ generateMutationSetters: true })
class UserModule extends VuexModule {
  firstName = "Foo";
  lastName = "Bar";

  // Auto generated:
  // @Mutation set__firstName(val: any) { this.firstName = val }
  // @Mutation set__lastName(val: any) { this.lastName = val }

  @Action
  async loadUser() {
    const user = await fetchUser();
    this.firstName = user.firstName; // -> this.set__firstName(user.firstName);
    this.lastName = user.lastName; // -> this.set__lastName(user.lastName);
  }
}

NOTE: Setters are only generated for root-level state properties, so in order to update a property of an object you have to use a mutation or replace the entire object:

@Module({ generateMutationSetters: true })
class UserModule extends VuexModule {
  user = {
    id: 123,
    name: "Foo",
  };

  @Mutation
  setUserName() {
    this.user.name = "Bar"; // OK!
  }

  @Action
  async loadUser() {
    this.user.name = "Bar"; // Bad, the state is mutated outside a mutation
    this.user = { ...this.user, name: "Bar" }; // OK!
  }
}

Vite HMR

Vite (and possibly other bundlers) uses import.meta.hot for HMR, which vuex-class-modules doesn't support currently. Instead a static property

VuexModule.__useHotUpdate = true; // default false

is provided, which will force hot updates to the store instead of throwing an error when a module with a duplicate name is registered. This could for instance be set only in dev mode

VuexModule.__useHotUpdate = import.meta.env.DEV;

License

MIT