The Vuex of Vue3.0 series

This series will take you to experience Vue3.0 and its ecology 🚀

Series of articles:

  • Experience Vue3 and its Ecological Series I (Build Vue3 Project)
  • Experience Vue3 and its Ecology Series 2 (Type Inference of Vuex)
  • Other series of developments…

preface

🤔 has generally checked that Vuex is basically used under JS on the Internet. This paper will improve the inference of State and Model type ~😎 corresponding to Vuex under Ts of Vue3.0

Vuex was introduced into the project


Vue-cli Generates Vuex by selecting the default configuration when creating a projectCopy the code
  1. NPM install
 npm install vuex --save
Copy the code
  1. Direct download/CDN reference
https://unpkg.com/[email protected]/dist/vuex.global.js this way to specify a particular versionCopy the code

The above nonsense is routine and now I’m going to get into the subject.

Vuex is in normal use

The default store generated in vue-CLI scaffolding looks like this:

// store/index.ts
import { createStore } from "vuex";

export default createStore({
  state: {},
  mutations: {},
  actions: {},
  modules: {},});Copy the code

Simple simulation project usage

// store/index.ts
import { createStore } from "vuex";

export default createStore({
  state: {
    name: "gby".isVip: true.hobby: ["Football"."Go to the movies"."Other"],},mutations: {
   // State cannot be changed directly
  },
  actions: {
      // Business operation...
  },
  modules: {
      / / user module
      / / the login module
      / /...}});Copy the code

Actually used in home.vue:

It is found that there is no shortcut prompt for name, isVip and other attributes of state under vue file invocation, and no type inference.

First encapsulation

The useStore combinatorial function type declaration is as follows:

When writing a Vue component using a composite API, you might want useStore to return a typed store. In order for useStore to return a typed store correctly, the following steps must be performed:

  1. Defines typedInjectionKey.
  2. Provides typed store when installed into a Vue applicationInjectionKey 。
  3. Will typeInjectionKeyTo pass touseStoreMethods.

The following is the specific implementation of the official website sample code.

// store/index.ts
import { InjectionKey } from "vue";  // Define typed 'InjectionKey'
import { createStore, Store } from "vuex";

// Declare type for store state
export interface State {
  name: string;
  isVip: boolean;
  hobby: string[];
}
// 1. Define the injection key
export const key: InjectionKey<Store<State>> = Symbol(a);export default createStore<State>({
  state: {
    name: "gby".isVip: true.hobby: ["Football"."Go to the movies"."Other"],},... });Copy the code
// main.ts.import { createApp } from 'vue'
import { store, key } from  "./store/index"

const app = createApp(App)
// 2. Provide typed 'InjectionKey' when installing store into Vue application.
app.use(store, key)
...
app.mount("#app");
Copy the code

Using in home.vue requires passing typed InjectionKey to the useStore method. :

/ / the vue components
import { useStore } from "vuex";
import { key } from "@/store/index";

export default defineComponent({
  ...
  setup () {
    // 3. Pass typed 'InjectionKey' to 'useStore'.
    const store = useStore(key)
    retrun {}
  }
})
Copy the code

The actual effect is as follows:

As you can see, there is now an attribute for state and an error when I assign false to a name of type String.

Secondary encapsulation

Although there is type inference in state above, there are two problems:

  1. Every time you use it in a VUE component, you need to import a key defined under store (inconvenient)
  2. It’s usually added tomodulesIn thestateWill there be the same prompt?

For the first point :(inconvenient to use each time in a vue component) the optimization is as follows 👇

// store/index.ts

import { createStore, Store, useStore as baseUseStore } from "vuex"; .// define your own 'useStore' combinatorial function
export function useStore() :Store<State> {
  return baseUseStore(key);
}
Copy the code

This makes it a little more comfortable each time you use it in a VUE component

// import { useStore } from "vuex"; / / before optimization
// import { key } from "@/store/index"; / / before optimization
import { useStore } from "@/store/index"; / / after optimization

export default defineComponent({
  setup() {
      // const store = useStore(key); / / before optimization
      const store = useStore(); / / after optimization}})Copy the code

Now I’m going to add a module and see if the state in modules also gives a hint.

// store/login/login.ts
import { State as RootSate } from "@/store/index";
import { Module } from "vuex";

export interface ILoginSate {
  token: string; // token
  lastLoginTime: string; // Last login time
  role: "VIP" | "SVIP" | "developer"; // User permissions
}

// Module
      
        => S is the state interface of the current Module, R is the state interface of the globally defined Store
      ,>
const loginModel: Module<ILoginSate, RootSate> = {
  namespaced: true.state: () = > {
    return {
      token: "".lastLoginTime: "".role: "developer"}; },... };export default loginModel;
Copy the code
// store/index.ts.import loginModel from "./login/login"; .export default createStore<State>({
  ...
  modules: {
    / / the login module
    login: loginModel,
  },
});

Copy the code

Invoking discovery in a VUE component does not prompt for state under Login or its module. (This is not a GIF)

The constraint passed in by key is then encapsulated as follows:

// store/index.ts.import loginModel, { ILoginSate } from "./login/login"; // call ILoginSate
/ / new
export interface State {
  name: string;
  isVip: boolean;
  hobby: string[];
}
interface IModulesState {
  login: ILoginSate;
  // Other modules
}
type IStoreType = State & IModulesState; // Merge type

// re-constrain key
// export const key: InjectionKey<Store<State>> = Symbol();
export const key: InjectionKey<Store<IStoreType>> = Symbol(a);// re-constrain useStore
// export function useStore(): Store<State> {
export function useStore() :Store<IStoreType> {...Copy the code

The final running effect

You can see that there are good hints for state and modules and type inference 😄, and that wraps it up here.

The above constraint on keys can be put in a separate type.ts file to make our index.ts clearer and more maintainable.

Follow this pattern and actually use it in your project.

There is another method that does not use InjectionKey.

Leave a pit (I will open a new article to write, I will post a link when I write it)

At the end

  • Experience Vue3 and its Ecological Series I (Build Vue3 Project)
  • Experience Vue3 and its Ecological Series 2 (Packaged Vuex)
  • Other series of developments…