Is Vuex a bit tedious?

Vuex was designed for Vue2, because the Option API itself has many shortcomings, so Vuex had to make various patches to compensate for these shortcomings, so it became “complex”.

Vue3 now has a Composition API that is more powerful and compensates for its shortcomings, but Vuex 4.0 is only compatible with Vue3 and doesn’t seem to change in style.

Vuex’s state is also reactive and provides /inject, but there is no further support. What about other features of Composition API? It feels so wasted.

Although Vuex can also use compositionAPI to implement the function, it always feels like a cannon hitting a mosquito.

Do a lightweight state manager.

In accordance with the “do it yourself” principle, we do a lightweight state management.

Mimicking Vuex and trying to implement the basic functionality, I can see why Vuex is so convoluted, because it is a bit difficult to include the action functions. So keep it light, only including states, not including mutations, action and other operational functions.

So the function looks like this:

State: global state, local state Function: initialize state, inject local state in the specified component, load local state in the sub-component

The cache function is to store the state in localstorage and load the state from localstorage during initialization. The caching function has not been implemented for the time being, and the local state caching scheme has not been well thought out.

The function is simple, it is much easier to write, and then incidentally made into a plug-in form, easy to use.

// Copy Vuex to write a simple data, state, cache management

import { reactive, provide, inject } from 'vue'   

export default {
  // State container
  store: {
    state: {}, // Global status
    init: () = > {}, // Initialize the global state
    reg: {}, // Register local state
    get: {} // Get local state
  },

  // Use the symbol to make a symbol, avoid the same name
  storeFlag: Symbol('VuexDataState'),

  // Create an instance to install the plug-in
  createStore(info) {
    BlogState: {// Each state must be an object. The base aaa type is not supported: 'state demo'}}, // local state, need to manually inject local: {dataList() {return {}}}, // init(state) {}} */

    for (const key in info.global) {
      // Store the global state to state
      this.store.state[key] = reactive(info.global[key])
    }
    
    for (const key in info.local) {
      const localKey = Symbol(key)
      // Add the registration function
      this.store.reg[key] = () = > {
        // Change the local state to reactive
        const state = reactive(info.local[key]())
        / / injection
        provide(localKey, state)
        // Return status,
        return state
      }
      this.store.get[key] = () = > {
        // Change the local state to reactive
        const state = inject(localKey)
        // Return status,
        return state
      }
    }

    // add the initialization function
    if (typeof info.init === 'function') {
      this.store.init = info.init
    }
    
    const _store = this.store
    const _storeFlag = this.storeFlag
    return {
      // Install the plug-in
      install (app, options) {
        // console.log('install-- my status ', _store)
        // Inject state, use symbol as mark, avoid the same name, avoid external direct use of inject
        app.provide(_storeFlag, _store)
        // Set the template usage state
        app.config.globalProperties.$state = _store.state
        // call initialization to assign the global state
        _store.init(_store.state)
      }
    }
  },

  // call in the code
  useStore() {
    // Get the global status
    const { state, reg, get } = inject(this.storeFlag)

    return {
      state, // Return global status
      reg, // Register the local state function and return the corresponding local state
      get // Get the state from the child component}}}Copy the code

How about that? Light enough, no more than 100 lines of code, maybe 30 or so if you strip out the comment space.

  • reactive, provide, inject

For a state to be reactive, of course it has to be reactive. Provide and Inject Inject functions.

  • store

Internal state containers. State: state init: the function for initializing the global state reg: the function for injecting the local state GET: the function for obtaining the local state

  • storeFlag

Use symbol to mark global status to avoid duplicate names. Is there a sense of superiority? [head]

  • useStore

Does not look familiar, in the code to get global status. In addition to returning global state, you can also return local state injection functions and fetch functions. Because symbol is used as the key, it cannot be retrieved externally, so you need to provide a function internally. (I won’t tell you I did it on purpose)

If you want to make the state readonlyReactive and then return it, you can do it here.

  • Use methods in the component
import VueDS from 'vue-data-state' 

const { state, reg, get } = VueDS.useStore()

state // Global status

// The parent injects state and returns local state for use by the parent
constLocal state = reg. Local state name ()// The child component gets local state
constLocal state = get. Local state name ()Copy the code
  • createStore

Vuex createStore takes parameters and creates a store that is then injected into the Vue app via a plugin. The function returns Install, which installs the plug-in.

  • _info

This is not useful, just to introduce the parameter attribute format, the implementation of the code when looking at the convenience. In addition, remove the comments can be used for testing.

  • The first for

Go through the global state and make reactive and hang in the store.

  • The second for

Iterating over the local state becomes a function for injection and retrieval, hanging in reg and GET. The provide key is also in the form of symbol to avoid duplication.

  • init

Hang up the initialization function.

  • install

Install the plugin, as shown in the Vue official website example. By the way, I just attached the global state to the template, not the local state. Local states don’t seem to hang and need to be considered again.

Install the resource pack first

yarn add vue-data-state 
Copy the code

Define state

// /store-ds/index.js
import VuexDataState from 'vue-data-state'

export default VuexDataState.createStore({
  global: { // Global status
    userInfo: {
      name:'Current logged-in'}},local: { // Local state
    // List of data, which needs to be registered before use
    dataListState() { // Displays the status of the blog list
      return {
        findKind: {}, // Query mode
        find: {}, // Query the keyword
        page: { // Paging parameters
          pageTotal: 100.pageSize: 2.pageIndex: 1.orderBy: { id: false}},_query: {}, // Cache query criteria
        isReload: false // Reload the data and count the total}}},// You can set the initial state for the global state, which can be asynchronous
  init(state) {
    setTimeout(() = > {
      state.blogState.name = 'Data set in int can be asynchronous'
    },3000)}})Copy the code
  • global

Global state. Each state must be an object (including an array), not an underlying type. Global state is injected into the root app by default. State and attribute names are optional, but this is just an example.

  • local

Local state. Each state must also be an object, not injected by default, and must be injected in the parent component using the reg call function. You need to use the return form, just like data. State in Vuex module also needs to use return form. State and attribute names are optional, but this is just an example.

  • init

Function that initializes the global state, optionally unset. When you install a plug-in in main.js, init is called after the global state is injected, and you can assign values to the global state to support asynchronous operations.

Use it in main.js

This is the same as Vuex: main.js

import { createApp } from 'vue'
import store from './store-ds' // Lightweight state

createApp(App)
  .use(store) // Lightweight state

Copy the code

I will try it out in my personal blog later. When I use it, I will find out whether there is any problem and how to improve it.

FAQ

  • What about the legendary stalking?

I didn’t understand the tracing issue very well, because dev-tool was always not installed, and when it was installed, it didn’t work. So skip that feature for now.

  • Vuex supports plugins. What about yours?

This is a bit complicated to say, to put it simply, there is no such demand at present, so we will skip it first, if we need it later, we can add it again. To make a plug-in is also simple, use watch to do deep monitoring of the state, and then call the plug-in hook. Why else would I want to take the state apart and make reactive?

  • Don’t change the state directly.

This is also complicated. I can make the status read-only, readonlyReactive, and then design something like mutations to change the status. But what’s the point? There is no trace, no plugin, and no idea how to get it into dev-tool. These are all ancillary projects, and it always feels weird to just do a read only project without them.

  • Why do we need a local state?

It all started with a discussion. One day I had a chat with the god of Zhihu, and he said that he wanted to create a shared state within the module. I didn’t understand it at first, but after a long discussion, I felt what the god said was really reasonable. So I started to try it, and finally found that it really did smell good. The details will be introduced in the blog project later.

  • Is the Option API supported?

I forgot about this at first, but then I remembered that it was designed specifically for the Composition API, so it should not be supported.

The source code

Gitee.com/naturefw/vu…

The online demo

Naturefw. Gitee. IO/vue – data – st…