How do I use the Vuex

The outline

This article is more about using Vuex, so it will cover the following points:

Q1: Why would I want to use VUEX to manage data state interactions?

Q2: What are the disadvantages or side effects of using the Vuex framework?

Q3: How did I use VUex in my project?

I met vuex

Some people like vuex, some people hate it

People who like it think it can be a good solution to complex data interaction scenarios

Those who dislike it find it to have a variety of drawbacks: cumbersome code writing, poor maintainable string variable injection, and a heavy reliance on the VUE framework resulting in poor asynchronous extension scenarios

There is a very vague point. There is no yardstick for complex data interaction scenarios, and everyone has their own opinion

In addition, different people have different project experiences, which often leads to interesting phenomena: you can’t understand why I have to use VUex, he can’t understand why vuex is necessary for this kind of scene, and I can’t explain why I choose VUex

Borrow a sentence from the official website document:

You’ll know when you need it

It’s a mystery. In more general terms, it means to explore more holes, encounter more pain points, and when the last straw breaks down, you will naturally look for a better solution

I have never been fond of VUEX, because I think the variables and methods injected into VUE by mapMutations or mapState are strings, which greatly damages the readability and maintenance of the code, and it is impossible to quickly jump to the variable definition through IDEA

Of course, you can also define static variables to replace these strings to solve the jump problem, but at the cost of more tedious code, which is already required to write a lot of code when using Vuex

Another reason why I don’t want to use VUex is that the business logic of my project is quite complicated. In addition to the vUE single file, the project also divides JS layer codes responsible for business logic or asynchronous tasks, while VUex is designed for the VUE framework. Variables stored in the Vuex data center can be injected into computed properties of vUE components for direct use through its utility methods, such as

import { mapState } from 'vuex'

export default {
  // Map this.count to store.state.count
  computed: mapState({
    count: state= > state.count
  })
}
Copy the code

However, if you want to use vuex data in js files, it can be tedious:

import store from 'store'

console.log(store.state.count);
Copy the code

For all the reasons above, I have been slow to use VUex in the project

So why did I end up using Vuex?

First, some of the data interaction scenarios of the project are unbearable using vUE’s native INPUT/output scheme

Second, I have managed to solve several shortcomings of vuex that I can’t stand

Third, this is a new project without complex business scenarios. The project is basically written by a single vUE file

To put it simply, a new project is suitable for vuEX scenarios, and some component interaction scenarios are too complicated to use the native scheme, so VUEX can just solve this problem. Although VUex has a certain cost, I have come up with some solutions to solve or simplify these shortcomings

In this way, vuex can solve my appeal without introducing too many drawbacks that I can’t accept, so I can play it

background

The VUE framework is based on the component mechanism to assemble the page, so the page data is scattered among the components, and the data interaction between the components uses the input (props) output ($emit) mechanism of VUE

This data interaction scheme is characterized by the fact that the data objects are stored inside the components and the interaction needs to be through inputs and outputs

One drawback of the input-output mechanism is that when a page is complex, data often needs to be passed through layers, because interactions between non-parent components can only be mediated by looking for the nearest ancestor component

At the same time, the I/O mechanism has a limitation: it can’t help when components on different pages need to interact with each other

Normal development, this input and output scheme is basically satisfied

But if you have a scenario where you need to interact data across pages or you need to persist the data; And if the page component hierarchy is complex and there are layers of props and $emit passing through, then if you can’t stand the input/output solution, you can explore a new solution

There are many solutions to this scenario, but in essence, they all fall into the same category: data center solutions

This idea is to move the data object from the internal component to external storage and maintenance, and the component that needs to use the data variable goes to the data center to fetch it by itself

Vue also has mechanisms to achieve this effect, such as dependency injection, but use them sparingly because they disrupt the flow of data

We can also use JS to achieve a data center, specifically to build a JS file to store and maintain the data model, the need for data variable components to introduce JS files to read

But every data center has to deal with two problems: data reuse and data pollution, which is generally known as data initialization and reset, or the life cycle of data

The purpose of data reuse is to ensure that different components read the same copy of data from the data center, so that data interaction can be achieved

Data pollution refers to whether the data model can be independent and not affect each other when different modules use the same data center. This is usually the scenario when a function is reused between different modules. If this scenario is hard to understand, consider the scenario where the page is loaded again and the component is created again, and the copies of data read from the data center are independent of each other

If data is stored inside a VUE component, the data life cycle follows the creation and destruction of the component. This is why data is a function that returns an object, because it can use the function scope mechanism of JS to solve the problem of data reuse and contamination

But when the data is moved out of the VUE component and stored in the data center, these processes need to be implemented themselves

Therefore, the data center is not simply a js class, which declares the data object is done

Based on this, I chose to use Vuex

Vuex side effects

Let’s start with a simple example using Vuex:

/ / declare
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // Change the state
      state.count++
    }
  }
})

// use in vue
import { mapMutations } from 'vuex'
import { mapState } from 'vuex'

export default {
  // ...
  computed: {
     ...mapState({
         // Map 'this.count' to 'this.$store.state.count'
         count: state= > state.count
     })   
  },
  methods: {
    ...mapMutations([
      'increment'.// Map 'this.increment()' to 'this.store.com MIT ('increment')'])}}Copy the code

To simply define a data object, we need to declare the object model state, declare the object operation method mutation, and then inject the read method of the variable through mapState and the write method of the variable through mapMutations in the corresponding VUE component

However, in the original VUE mechanism, so many complicated codes are simply declared variables in data. In contrast, vuEX has a lot of complexity and complexity, resulting in a certain use cost

So many people don’t like to use it, and officials say there is no need to use it for simple pages

This is the first drawback, or side effect, I see with Vuex: tedious and redundant code writing

The second disadvantage of VUEX is that variables injected by mapState or mapMutation are all strings

The string means that when you use these variables elsewhere in the vue single file via this. XXX, idea does not recognize when you want to see the variable declaration!

This is particularly unacceptable to me, which reduces my maintenance and development efficiency

This can vary from person to person. Some people think it’s not a problem, or replacing a string with a static variable can work, but I personally can’t accept that

However, vUE’s native input and output data interaction is not enough to support some of my demand scenarios, so I would like to use JS to implement a data center, but I am worried that there is no mandatory specification, not well handled, and it is more difficult to maintain the late deviation, so I will try to solve the two shortcomings of VUex

How to use VUex more easily

By the way, I used Vuex in a way that allowed me to achieve what I wanted without introducing too many side effects

But in fact, this method may deviate from vuex’s official recommendation, and others may not accept my use of this method

Therefore, this article is more to share some of my thoughts and ideas, just say one, not general, welcome to clap brick

Personally, I can’t accept the two disadvantages of Vuex:

  • Tedious and redundant code writing
  • String variable injection with poor maintenance and poor readability

So, we have to figure out how to solve these two problems. Let’s start with the first one

Encapsulation automatically generates code to solve the tedious problem of using VUEX

Using Vuex requires a lot of tedious code, which is indispensable, since it is indispensable, then change the idea, don’t need me to write it

Find a way to extract commonalities, encapsulate a tool method, and make it easy to use by generating the tedious code that vuex uses every time

The data object model declared in state cannot be generated automatically because the data model is not the same

The mutation code of the data variable can be automatically generated by means of modification

/** * Automatically generate mutations to update attributes based on the attributes of the state object, for example: * State: {* projectId: '', * searchParams: {* batchId: '' * } * } * * ===> * * { * updateProjectId: (state, payload) => { state.projectId = payLoad } * updateSearchParams: (state, payload) => { state.searchParams = {... state.searchParams, ... payload} } * updateBatchId: (state, content = > {state. SearchParams. BatchId = payload} *} * * not directly generated for like a type of attribute assignment operation, The object-type attributes are regenerated by extending the operator * and the attributes of the internal object are recursively processed, and the flattened generation of updateXXX method is mounted on the Mutations object *@param {Object} stateTemplate* /
export function generateMutationsByState(stateTemplate) {
  let handleInnerObjState = (parentKeyPath, innerState, obj) = > {
    Object.keys(innerState).forEach(key= > {
      let value = innerState[key];
      let updateKey = `update${key[0].toUpperCase()}${key.substr(1)}`;
      if (typeof value === 'object'&& value ! =null&&!Array.isArray(value)) {
        obj[updateKey] = (state, payload) = > {
          let target = state;
          for (let i = 0; i < parentKeyPath.length; i++) { target = target[parentKeyPath[i]]; } target[key] = { ... target[key], ... payload }; }; handleInnerObjState([...parentKeyPath, key], value, obj); }else {
        obj[updateKey] = (state, payload) = > {
          let target = state;
          for (let i = 0; i < parentKeyPath.length; i++) { target = target[parentKeyPath[i]]; } target[key] = payload; }; }}); };let mutations = {};
  Object.keys(stateTemplate).forEach(key= > {
    let obj = {};
    let value = stateTemplate[key];
    let updateKey = `update${key[0].toUpperCase()}${key.substr(1)}`;
    if (typeof value === 'object'&& value ! =null&&!Array.isArray(value)) {
      obj[updateKey] = (state, payload) = >{ state[key] = { ... state[key], ... payload }; }; handleInnerObjState([key], value, obj); }else {
      obj[updateKey] = (state, payload) = > {
        state[key] = payload;
      };
    }
    Object.assign(mutations, obj);
  });
  return mutations;
}

Copy the code

The code that mapState and mapMutation inject into the VUE component can also be generated automatically using computed properties, which makes it easier to use. After all, using computed properties is the same as using variables declared in data. It makes no difference

import store from './index';
/** * Convert the specified state in store to a calculated property. In computed set() get() * vue, you can use state directly like the data property@param {String} ModuleName State The module name of the store to which it belongs *@param {Array} States LLDB States to be handled: ['project', 'searchparams.projectName '] where, * attribute names mounted on computed are equal to state by default, and when the state structure is multi-layered, take the attribute name of the last layer * * ps: The mutation corresponding to state must be named as updateXXX */
export function storeToComputed(moduleName, states) {
  if(! store) {throw new TypeError('store is null');
  }
  if(! moduleName) {throw new TypeError("state's module name is null");
  }
  if(! states || !Array.isArray(states) || states.length === 0) {
    throw new TypeError('states is null or not array');
  }
  let computed = {};
  states.forEach(state= > {
    if (state.indexOf('. ')! = = -1) {
      let _states = state.split('. ');
      let _key = _states[_states.length - 1];
      computed[_key] = {
        get() {
          let res = store.state[moduleName];
          for (let i = 0; i < _states.length; i++) {
            res = res[_states[i]];
          }
          return res;
        },
        set(value) {
          store.commit(
            `${moduleName}/update${_key[0].toUpperCase()}${_key.substr(1)}`, value ); }}; }else {
      computed[state] = {
        get() {
          return store.state[moduleName][state];
        },
        set(value) {
          store.commit(
            `${moduleName}/update${state[0].toUpperCase()}${state.substr(1)}`, value ); }}; }});return computed;
}
Copy the code

The final result is:

  • Simply declare the state data variable in the Store file
  • Then inject into the VUE component that needs to be injected
/ / declare
import { generateMutationsByState } from './helper';

const global = {
    state: {
        count: 0}}global.mutations = generateMutationsByState(global.state);

const store = new Vuex.Store({
    modules: {
        global}})Copy the code
// use in vue
import { storeToComputed } from '@/store/storeToComputed';

export default {
  // ...
  computed: {
      // map this.$store.state.global.count to this.count. storeToComputed('global'['count'])}}Copy the code

My use of vuex is simply to use it as a data center, without writing logic or using action in store files

The benefits of this use, I think, will be similar to the original use of variables declared in vue’s data. Because it takes a variable defined in data to be defined in a special store file, and then takes one more step to inject the variable into computed in Vue through utility methods, and then any scenario that uses variables, where to assign values, where to value values, where to handle asynchronous requests, and so on, how to write the code, How do I write it now? It doesn’t matter at all. Right

This means that if this scheme has defects or is not used well in the future, it is very convenient to switch to vUE’s native input and output scheme, with fewer impact points and changes. In other words, change the variables injected into storeToComputed to data

It will even be easier to replace Vuex later, as it is simply used as a data center

Then cooperate vuex dynamic mount and unload on usage, the data center can be precise control of data objects like presents framework, scope and life cycle within the global sharing, sharing between modules, page sharing, Shared within components can easily do it, so that the data interaction is afraid of the complex scenarios

This is why I use Vuex this way

Custom vscode plugin solves string variable jump problem

Now that we’re done with the tedious coding, let’s see how to solve the jump problem of string variable injection

First of all, why do I care if the variable support doesn’t support jumping directly to the declared place using IDEA

This is because some pages are complicated and have many data variables, or it is easy to forget the names and meanings of some variables after a long time

We usually just add a few comments to the declaration

Therefore, use IDEA to jump directly to the declared place quickly. First, annotations can quickly help recall and clarify the meaning of variables. Second, forgetting the full name of a variable can be quickly copied and used; Third, it makes it easier for me to look at other data variables

So, how to solve this problem?

Naturally is their own extension to develop a vscode plug-in to support, for baidu, vscode plug-in development is not difficult, see a few tutorials, clear plug-in life cycle and some API can be used

The key is, how do you identify these variables injected by VUEX? How do I jump to the state location of the declared data variable in the Store file?

If you want to make it a universal plug-in, it might take a little more effort

However, it is much easier to solve this problem based on your current project, because the project has certain specifications, such as the directory address of vuex store files, such as the use of injection into vue components, these are standardized and regular

Here’s my idea:

  1. First, scan the files under the project Store directory, identify the files with data model (STATE), parse and store the variable names and locations of the data model
  2. Register vscode’s variable action response to respond to our plug-in when holding down CTRL and moving the mouse over the variable
  3. Determine whether the variables for the current focus operation are injected through computed variables, and go to the next step to find the file location of the variable declaration
  4. Match variables in store by variable name and module name. After matching, record variable declaration information and file location. When clicking the left button, respond to jump behavior

Github address: vuex-peek

conclusion

Finally, it is not necessary to use VUEX in the project. The scenes solved by VUEX can also be solved by using vUE’s native input and output mechanism. The difference may be the difference in code readability and maintenance, and whether the direction of data flow is clear, etc

As a tripartite library, VUex is naturally an optional service. Whether to use it or not and how to use it varies from person to person. Consider your appeal, compare the influence points before and after the introduction, and weigh the points you can accept

For example, I use Vuex in a way that is ugly and a bit nondescribable. After all, I don’t use vuex according to the official example, but create a set of usage specifications by myself, which also increases the cost for others to get started

Therefore, this article is not to urge the use of VUex, but to share my mental process and thoughts of using some tripartite libraries from my own experience

A lot of the time, when you start teasing the so-and-so solution, when you start having trouble with the use of so-and-so, that means it’s a great opportunity to explore

After poking fun, think of ways to optimize and find new schemes; When you can’t accept it, try to find a way to study and see if you can solve these pain points

People, always step into the pit again and again, and then climb out