VUEX

Time:2021-1-14

What the hell is vuex?

install

I won’t talk about the specific installation of vuex here. The official information is relatively clear,Poke here to enter。 However, two points should be noted:

  • In a modular packaging system, you must explicitly use theVue.use()To install vuex, for example:
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use (vuex) // this function must be called to inject vuex
  • When using the global script tag to refer to vuex, you don’t have to worry about it. Just refer to it directly, but you should pay attention to the sequence of references, as follows:
//After Vue, the introduction of vuex will automatically install
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>

Although script seems to be more automatic, you will understand that modularity is actually our best posture after more contact.

Unveiling vuex

When we get a tool, what we need to understand at the first time is what problems it can help us solve. For example, hammers, broken eggs, hit the phone, such as apples, can not only eat but also play. What about vuex, if you put Vue.js If he wants to go next door to buy a pack of cigarettes, he can just walk there. Driving a car is a burden. But if he wants to go tens of kilometers to school to pick flowers, then Santana will have to be used. Otherwise, when he goes there, all the flowers may wither.

Of course, the analogy is just to tell us the value of vuex. In practical application,What can it do? When do you need to turn its cards?

Let’s take a look at the official Code:

new Vue({
  //State data source
  data () {
    return {
      count: 0
    }
  },
  //View view
  template: `
    <div>{{ count }}</div>
  `,
  //Actions event
  methods: {
    increment () {
      this.count++
    }
  }
})

This is a very simple growth counting function page, andVue.jsIf you have a leg, you should understand. By eventincrement, implementationcountGrow, and then render to the interface.

In fact, this way is just like walking to buy cigarettes. It is a short-distance effect. It is easy to understand that the government calls it “one-way data flow”.

VUEX

Unidirectional data flow

However, the situation has changed. Now there are two pages a and B, and the following two requirements:

  • They are required to be able tocountControl.
  • Request a has been revisedcountB should know immediately after B changes, and a should know immediately after B changes.

What should I do? A little bit of development experience, you can easily think of, the data sourcecountSplit it off and manage it in a global variable or global singleton mode, so that it is not easy to get this state on any page.

Yeah, that’s the idea behind vuex. That’s what it does. Is there a feeling that vuex, a big name, is harming you? It’s the global model. It’s also possible to do without it.

Yes, it can be done, just like you can go to school to see flowers without Santana, but the process is different.

The purpose of vuex is to manage the shared state. In order to achieve this goal, it makes a series of rules, such as modifying the data source state, triggering actions and so on. All of them need to follow its rules, so as to make the project structure clearer and easier to maintain.

Let’s look at the official description

Vuex is a Vue.js State management mode of application development. It uses centralized storage to manage the state of all components of the application, and ensures that the state changes in a predictable way with corresponding rules.

There is no instant clearer.

When do you turn vuex

In fact, after understanding what vuex is going to do, it’s much easier to choose when to turn over the cards. Just like the previous analogy, when you go to the next room to buy a pack of cigarettes and open a Santana, when you look for a parking space, you smoke out.

Therefore, we need to measure the short-term and long-term benefits according to the needs of the project. If we don’t plan to develop large single page applications, vuex may still be a burden for you. For some small and medium-sized projects, if you are lazy to walk and feel troublesome to drive, you can ride a bike.

The bike sharing here refers to a simple official bike sharingStore modeIs actually a simple global object.

The difference between global objects and vuex is relatively easy to understand

Vuex differs from simple global objects in the following two aspects:

  1. Vuex’s state storage is responsive. When Vue components read the state from the store, if the state in the store changes, the corresponding components will be updated efficiently.
  2. You can’t directly change the state in the store. The only way to change the state in a store is to explicitlyCommit mutation。 This makes it easy for us to track every state change, so that we can implement some tools to help us better understand our application.

Simple example

//If you are in a modular build system, make sure you call it at the beginning Vue.use (Vuex)
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

The core of every vuex application is the store. A store is basically a container that contains most of the information in your applicationState

Note: if mutations don’t know what it is, it doesn’t matter. It will be explained later. It can be simply understood that it can only modify the data in the state by using the methods in it.

store.commit ('increment ') // call methods in mutations
console.log(store.state.count) // -> 1

The government also gave specific reasons for the design

We submit the mutations instead of changing them directly store.state.count Because we want to track state changes more clearly. This simple convention can make your intention more obvious, so that when you read the code, you can more easily understand the state changes inside the application. In addition, this also gives us the opportunity to implement some debugging tools that can record every state change and save the state snapshot. With it, we can even achieve a debugging experience like time travel.

Because the state in store is responsive, the state in calling store in components is simple, and only needs to be returned in the computed property. Triggering a change is only to submit a mutation in the methods of the component.

Vuex’s state and getter

Similarly, we have learned that vuex, like a global administrator, helps us to manage the shared data of projects in a unified way. What kind of way is it managed? How can we communicate with the administrator in order to access and operate the shared data effectively?

One more word

The viscera of vuex consists of five parts: state, getter, mutation, action and module. As for these five parts, I will divide them into several chapters to elaborate in detail. In this lecture, I will work with you to thoroughly deal with state and getter.

Of course, in practical application, these five parts are not necessary. You can add whatever you need. But generally, no matter how simple vuex is, it will at least consist of state and mutation. Otherwise, you should consider whether vuex is necessary.

Finally, warm tips, the document sample code uses the syntax of es2015, if you have not yet understood, firstKnow more about this

Single state tree

Vuex usesSingle state treeAccording to the official description, it may be a little confused, but it doesn’t matter. Here we’ll learn more about what isSingle state tree。 Let’s take a look at the meaning of trees. “

VUEX

organizational structure

As shown in the figure above, the organizational structure of a company belongs to a tree structure. The general manager is the trunk of the tree, and other departments or occupations belong to the branches of the tree.

Generally speaking, a company will only have such a tree structure. If there are two equal general managers, there may be conflicts in the management of the company. Who will listen to the people below, right!

OK, now let’s take a look at the official narrativeSingle state tree

1. Use an object(trunk)That’s all(Branch)Application level status. “
2. Per app(company)Only one store instance object will be included(trunk)

Single state tree allows us to locate any specific state fragment directly, and can easily get a snapshot of the current application state during debugging.

State

Let’s go back to the simple store sample code:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  }
})

So how do we present state in Vue components? Because the state storage of vuex is responsive, the easiest way to read the state from the store instance is in theCalculation propertiesReturns a status as follows:

//Create a counter component
const Counter = {
  data() { return {} },
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

wheneverstore.state.countWhen it changes, it will retrieve the calculated properties and refresh the interface.

It’s important to note that if youstore.state.countIn data,store.state.countThe change of the interface will not trigger the refresh of the interface. Of course, it cannot be done directly<div>{{ store.state.count }}</div>Because you can’t directly access the store object in the template, you will undoubtedly report an error if you write in this way.

This mode depends on the global administrator store. If there are too many modules, it means that every module or page needs to introduce the store as long as it uses the data in this state. This kind of operation is really a bit uncomfortable. Of course, the government certainly does not allow such a crazy operation:

Through the store option, vuex provides a mechanism to “inject” the state from the root component into each sub component (to be called) Vue.use (Vuex)):

const app = new Vue({
  el: '#app',
  //Provide the store object to the "store" option,
  //This can inject an instance of the store into all the subcomponents
  store,
  //Subcomponents
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

By registering the store option in the root instance, the store instance will be injected into all the subcomponents under the root component, and the subcomponents can be accessed through this. $store. Let’s update the implementation of counter

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

Vuex is easy to use, but don’t abuse it

Using vuex doesn’t mean you need to put all the states in vuex. Although putting all the states into vuex makes the state changes more explicit and easier to debug, it also makes the code lengthy and non intuitive. If some states strictly belong to a single component, it’s better to be a local state of the component. You should make trade-offs and decisions based on your application development needs.

Getter

Sometimes, we will find that the data in the state is not what we want directly, but needs to be processed to meet our needs.

For example, in a component, we need to change the date in the statedateConvert to the day of the week to show:

computed: {
  weekDate () {
    return moment(this.$store.state.date).format('dddd'); 
  }
}

Note: Here’s themomentIs a third-party date processing class library, need to import before using.

If only one component needs to do this, it’s OK, but if many components need to do this transformation, then you need to copy this function in each component. What’s more, once the product manager is in a bad mood and doesn’t want to use the day of the week to show it, he wants to use it directly2018-10-30 11:12:23If you want to display the date in this way, you have to change the date formatting method in all the components that use it. Even if you extract it separately as a public function, all kinds of import is also troublesome. The most important thing is that it is not well managed.

So, at this time, vuex introduced another awesome thing,Getter。 We can think of it as a computed property in the store.

Just like calculating a property, the return value of a getter is cached according to its dependency, and is recalculated only when its dependency value changes.

Let’s take a look at these two examples and focus on the following notes:

const store = new Vuex.Store({
  state: {
    date: new Date()
  },
  getters: {
    //Getter takes state as its first parameter
    weekDate: state => {
      return moment(state.date).format('dddd'); 
    }
  }
})
getters: {
  //Getter can also receive getters as a second parameter
  dateLength: (state, getters) => {
    return getters.weekDate.length;
  }
}

Not only that, getter will store.getters Objects are exposed, and you can access these values in the form of attributes:

console.log(store.getters.weekDate)

We can easily use it in any component:

computed: {
  weekDate () {
    return this.$store.getters.weekDate
  }
}

Now the demand has changed again. The format of weekdate to be displayed in each module is different. Some need to display all the dates, and some need to display the day of the week. What should I do?

It’s easy to do, so you can send it to getter, but how?

Since getter is cached as part of Vue’s responsive system when accessed through attributes, it cannot be accessed directlystore.getters.weekDate('MM Do YY')Because weekdate is not a function, it’s just a property.

Now that the attribute can’t pass parameters, what should I do? Let’s try to turn this property into a function.

getters: {
  //Return a function, you can pass parameters
  weekDate: (state) => (fm) => {
    return moment(state.date).format(fm ? fm : 'dddd'); 
  }
}

The usage is as follows:

store.getters.weekDate('MM Do YY')

Maybe children’s shoes who have read the official documents will wonder why they didn’t explain those auxiliary functions, such asmapStatemapGetters。 Don’t worry, there will be a special chapter to explain it later, because I found that these auxiliary functions (including the followingmapMutationsandmapActions)They are all created to solve the same problem, only in different forms, so it’s better to talk about it together, maybe the effect will be better.

Vuex’s station

Last lectureVuex’s state and getter, which tells us how to use the state data in the warehouse. Of course, it’s not enough to use it alone. Most application scenarios still have to control these states. How to control them is the key point of this lecture.

Only mutation state

The only way to change the state in vuex’s store is to submit a mutation.Mutations in vuex are very similar to events: each mutation has a string event type and a callback function. This callback function is where we actually change the state, and it will accept state as the first parameter

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    //The event type is increment
    increment (state) {
      //Change status
      state.count++
    }
  }
})

Note that we can’t directlystore.mutations.increment()Vuex specifies that thestore.commitTo trigger the corresponding type:

store.commit('increment')

Chuanshen

We can also ask store.commit Pass in additional parameters:

mutations: {
  increment (state, n) {
    state.count += n
  }
}

//Call
store.commit('increment', 10)

For this extra parameter in the mutation, the official gave it the name of “tall”Load (payload)。 To be honest, I saw this title in the document for the first time“Submit load”I really don’t want to look down.

We are often not defeated by these unsophisticated concepts, but by our own inner fear.

In most cases, the load is an object that makes it easier to read:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

There are two ways to submit:

//1. Submit load and type separately
store.commit('increment', {
  amount: 10
})

//2. The whole object is transferred to the mutation function as a load
store.commit({
  type: 'increment',
  amount: 10
})

Of course, there is no absolute limit to which way to use. It depends on my own preferences. As far as I am concerned, I prefer to use the second posture, which is more realistic together.

Modify the rules

It’s easy to modify the state data of the basic type. There are no restrictions, but if you modify the object, you should pay attention to it. For example:

const store = new Vuex.Store({
  state: {
    student: {
      Name: 'Xiao Ming',
      Sex: 'female'
    }
  }
})

At this time, if we want to givestudentAdd an ageage: 18What should we do?

Yes, directlysexNow, just add this field. It’s best to do this. But what if we want to change it dynamically? Then you have to follow Vue’s rules. As follows:

mutations: {
  addAge (state) {
    Vue.set(state.student, 'age', 18)
    //Or:
    // state.student = { ...state.student, age: 18 }
  }
}

The above are two ways to add attributes to an object. Of course, if you want to modify the specific value of an added object, you can change it directly, such asstate.student.age=20That’s it.

As for why, we have learned before, because the state in the store is responsive. When we change the state data, the Vue component that monitors the state will also be updated automatically. Therefore, the mutation in vuex also needs to follow these rules as well as using Vue.

Use constants

This is to replace the name of the mutation event with a constant.

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    //Use the es2015 style computational property naming function to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

Some people may have doubts. What’s the use of doing this? You have to create more type files and import them when you use them. Isn’t it troublesome!

Let’s see how the mutation is calledstore.commit('increment')You can find that here is the method of commit submissionincrementIn the form of a string. If the project is small and developed by one person, it’s OK. However, if the project is large and there are more people writing code, it’s troublesome. Because there are many ways to commit, it will be particularly confusing. Moreover, if it is substituted in the form of string, it’s very difficult to check if something goes wrong.

Therefore, for large projects with multi person cooperation, it’s better to use the constant form to deal with the mutation. For small projects, it doesn’t matter, just want to be lazy.

Must be a synchronous function

It’s important to remember,Mutation must be a synchronous function.Why?

As mentioned earlier, the reason why we want to change the state data by submitting a mutation is that we want to track the change of state more clearly. If it is asynchronous as follows:

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

We don’t know when the state will change, so we can’t track it. This is contrary to the original design of the station, so it is mandatory that it must be a synchronous function.

store.commit('increment')
//Any state changes caused by "increment" should be completed at this point.

Action under vuex

Through the previous lectureVuex’s station, we know how to modify the data of a state, and we can only submit the modification through the mutation. In addition, we also know that the mutation must be a synchronous function. What if the asynchronous function must be used in the requirement?

Easy to do, then it’s action’s turn.

Brief introduction

Action is similar to mutation, except that:

1. Action submits a mutation, not a direct change of state. “
2. An action can contain any asynchronous operation.

Let’s look at a simple example of action

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

As you can see, the action function takes acontextParameter. Note that this parameter is not general. It has the same methods and properties as the store instance, but they are not the same instance. I will explain why they are different when I learn modules later.

So you can use it herecontext.commitTo submit a mutation, or throughcontext.stateandcontext.gettersTo get the state and getters.

Of course, to simplify the code, we can use theParametric deconstructionIt’s easy to expandcommitstateAnd so on. As follows:

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

Distribute action

store.dispatch('increment')

Station passedstore.commitTrigger, then actionstore.dispatchMethod trigger.

At first glance, it seems unnecessary. Isn’t it more convenient for us to distribute the mutation directly? In fact, this is not the case. Remember that mutation must execute this restriction synchronously? Action is not bound! We can perform asynchronous operations inside the action

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

It’s the same as the way of distribution of musicdispatch

//Distributed as load
store.dispatch('incrementAsync', {
  amount: 10
})

//Distribute as objects
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

Let’s take a more practical shopping cart example, which involves calling asynchronous API and distributing multiple mutations

actions: {
  checkout ({ commit, state }, products) {
    //Back up the items in the current shopping cart
    const savedCartItems = [...state.cart.added]
    //Make a checkout request and then empty the cart optimistically
    commit(types.CHECKOUT_REQUEST)
    //The shopping API accepts a success callback and a failure callback
    shop.buyProducts(
      products,
      //Successful operation
      () => commit(types.CHECKOUT_SUCCESS),
      //Failed operation
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

Note that a series of asynchronous operations are in progress in the example, and the side effects of the action (i.e. state changes) are recorded by submitting a mutation.

Combined action

Action is usually asynchronous, so how do you know when an action ends? More importantly, how can we combine multiple actions to handle more complex asynchronous processes?

First, you need to understandstore.dispatchCan handle the promise returned by the handler of the triggered action, andstore.dispatchStill return promise:

I don’t know what promise isLet’s get to know

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

Call:

store.dispatch('actionA').then(() => {
  // ...
})

Of course, it can also be like this:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

We can also use itasync / awaitHow to combine actions:

//Suppose getdata() and getotherdata() return promise
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    Await dispatch ('actiona ') // wait for actiona to complete
    commit('gotOtherData', await getOtherData())
  }
}

Onestore.dispatchMultiple action functions can be triggered in different modules. In this case, the return promise is executed only after all trigger functions have completed.

We often encounter this kind of situation in actual projects. For example, you want to handle B event now, but B event needs a kind of resource, and this kind of resource must be obtained through a event. At this time, we need to combine actions to handle these events.

Vuex’s little helper

I’ve talked about the four carriages of state, getter, mutation and action under vuex. I don’t know if you have understood them. Of course, if you want to really master it, you still need constant practice and hands-on practice.

In fact, as long as you master the four carriages, you will have no problem in dealing with some small and medium-sized projects. The ultimate carriage of module is actually to deal with those slightly large and complex projects and avoid store There are too many data in it, so it’s difficult to manage and design. This cart is a little more abstract and not easy to control. Let’s explain it in detail next time.

Many of the supporting facilities in Vue have been pursuing simplicity and perfection in the use experience, so this is a very important reason why Vue has won the hearts of the people for a long time. In this lecture, let’s talk about some common auxiliary functions of vuex.

mapState

Through the previous learning, we know that the easiest way to read the state from the store instance is toCalculation propertiesReturns a state in the.

So, what happens when a component needs to get multiple states? Is that right

export default {
  ...
  computed: {
      a () {
        return store.state.a
      },
      b () {
        return store.state.b
      },
      c () {
        return store.state.c
      },
      ...
   }
}

Of course, this is no problem, but always feel very uncomfortable to write, it looks even more uncomfortable, right! Since it’s so easy for us to feel it, can vuex not feel it, can we bear it?

Absolutely not, somapStateAuxiliary functions have been created to deal with the pain points of this person’s gnashing teeth.

//In the separately built version, the auxiliary function is Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    //Arrow functions make the code more concise
    a: state => state.a,
    b: state => state.b,
    c: state => state.c,

    //Pass string parameter 'B'
    //Equivalent to ` state = > state. B`
    bAlias: 'b',

    //To be able to use 'this' to get the local state
    //Regular functions must be used
    cInfo (state) {
      return state.c + this.info
    }
  })
}

From the above example, we can see that we can directly store all the needed states in themapStateThere is a unified management, but also can take alias, do additional operations and so on.

If the name of the mapped calculation attribute is the same as the name of the child node of the state, we can simplify it and give themapStatePass a string array:

computed: mapState([
  //Map this. A to store.state .a
  'a',
  'b',
  'c'
])

becausecomputedThis calculation property receives an object, so you can see from the example code above,mapStateThe function returns an object. If you want to mix it with local calculation properties, you can use ES6 syntax to simplify it

computed: {
  localComputed () { 
    ...
  },
  //Use the object expansion operator to blend this object into an external object
  ...mapState({
    // ...
  })
}

I seemapStateAfter the auxiliary function, the usage of the following auxiliary functions is almost the same. Let’s go on.

mapGetters

This andmapStateBasically, there is no difference. Just look at the official examples

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

To get an alias, use the form of an object. The following example means tothis.doneCountMap tothis.$store.getters.doneTodosCount

mapGetters({
  doneCount: 'doneTodosCount'
})

mapMutations

Look directly at the sample code:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      //Will` this.increment () 'mapped to 
      // `this.$store.commit('increment')`
      'increment', 
      //'mapmutations' also supports loads:
      //Will` this.incrementBy (amount) ` map to 
      // `this.$store.commit('incrementBy', amount)`
      'incrementBy' 
    ]),
    ...mapMutations({
      //Will` this.add () 'mapped to 
      // `this.$store.commit('increment')`
      add: 'increment' 
    })
  }
}

It’s not easy to use. Serial loads can be directly supported.

mapActions

andmapMutationsAs like as two peas, change your name.

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      //Will` this.increment () 'mapped to 
      // `this.$store. dispatch('increment')`
      'increment', 
      //Mapactions also supports payload:
      //Will` this.incrementBy (amount) ` map to 
      // `this.$store. dispatch('incrementBy', amount)`
      'incrementBy' 
    ]),
    ...mapActions({
      //Will` this.add () 'mapped to 
      // `this.$store. dispatch('increment')`
      add: 'increment' 
    })
  }
}

Want to invoke in component, directlythis.xxxIt’s over.

Vuex administrator module

This is the last and most complex lecture of vuex foundation. It may be a bit difficult for beginners to accept if they come according to the official rules, so after thinking about it, I decided to spend more time to explain it with a simple example, and review the previous knowledge by the way.

First of all, we need to understand the background of module. We know that vuex uses a single state tree, and all the states of the application are centralized into one object. If the project is relatively large, the corresponding state data will certainly be more. In this way, the store object will become very bloated and difficult to manage.

It’s like a company is run by the boss alone. If the small company is OK, if the company is a little bigger, it will be in trouble. At this time, the boss will set up major departments and arrange a supervisor for each department to assign the management tasks. If there is anything to deal with, he only needs to communicate with these supervisors, and then the supervisor will assign the tasks. This will greatly improve the work efficiency and reduce the burden of the boss.

In the same way, the module actually assumes the role of department administrator, and the store is the boss. After understanding this level, it will be much easier to do. Next, let’s start to practice step by step.

1、 Preparation

Here we use the officialvue-cliTo build a project called “vuex test.”. Of course, you have to install Vue cli first:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

After the installation is complete, you can use the following command to create the project:

vue create vuex-test

You can also use a graphical interface to create:

vue ui

The specific usage of Vue cli can be viewed officially,Poke here to enter

After the project is created, find the directory where the project is installed, and open the console to execute:

//First navigate to the project directory
cd vuex-test

//Then install vuex
npm install vuex --save

//Run it
npm run serve

After running, it can be openedhttp://localhost:8080/Let’s see the effect.

Finally, we find a favorite ide to open this project, which is convenient for viewing and editing. I personally prefer to use webstore, and I recommend it here.

2、 Simple start

Default structure diagram for the project

Here, we just looksrcDirectory, the rest of the time. assemblycomponentsIt’s not within the scope of this, so we can also ignore resourcesassetsThere’s nothing to say. If there are pictures or videos, just put them in this folder.

We open itApp.vueFile, remove the component related code, and write a little simple Vue code. It is amended as follows:

<template>
    <div id="app">
        <img src="./assets/logo.png" />
        <h1>{{name}}</h1>
        < button @ Click = "modifynameaction" > Modify name < / button >
    </div>
</template>

<script>
    export default {
        data() {
            return {
                name: 'Lucy'
            }
        },

        methods: {
            modifyNameAction() {
                this.name = "bighone"
            }
        }
    }
</script>

Now let’s introduce vuex and use it to manage state data, such as herename。 First of allsrcCreate a new one instore.jsFile, and write the following familiar Code:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        name: 'Lucy',
    },
    mutations: {
        setName(state, newName) {
            state.name = newName;
        }
    },
    actions: {
        modifyName({commit}, newName) {
            commit('setName', newName);
        }
    }
});

Then, in themain.jsImport fromstoreAnd global injection:

import store from './store';
// ...
new Vue({
    store,
    render: h => h(App),
}).$mount('#app')

Final revisionApp.vueThe code in is as follows:

<script>
    import {mapState, mapActions} from 'vuex';
    export default {
        computed: {
            ...mapState(['name'])
        },

        methods: {
            ...mapActions(['modifyName']),

            modifyNameAction() {
                this.modifyName('bighone');
            }
        },
    }
</script>

I think it’s no problem to understand these codes, because these are very basic knowledge points of vuex. Here’s a brief review of the practical operation to deepen the impression. If you don’t understand, it proves that you haven’t mastered the basic knowledge before.

3、 Introduction of module

In the foreword, we have explained the basic responsibilities of the module, so how to use it?

Vuex allows us to divide the store into large and small objects. Each object also has its own state, getter, mutation and action. This object is called a module, in which sub modules and sub modules can be embedded

Now insrcThere’s a folder inside, namedmoduleAnd then create a new one insidemoduleA.jsFile, and write the following code:

export default {
    state: {
        text: 'moduleA'
    },
    getters: {},
    mutations: {},
    actions: {}
}

As above, build another onemoduleB.jsThe document is not repeated here.

Then open itstore.jsFile, import the two modules:

import moduleA from './module/moduleA';
import moduleB from './module/moduleB';

export default new Vuex.Store({
    modules: {
        moduleA, moduleB,
    },
    // ...
}

At this time, two sub modules have been injected into the storemoduleA moduleB, we canApp.vuePassed inthis.$store.state.moduleA.textThis way to directly access the state data in the module. As follows:

// ...
computed: {
    ...mapState({
        name: state => state.moduleA.text
    }),
},
// ...

Therefore, the internal state of the module is local and only belongs to the module itself, so the external state must be accessed through the corresponding module name.

howeverNote:

Action, mutation and getter in the module can be registered in theGlobal Namespace This enables multiple modules to respond to the same mutation or action.

Here, take the response of mutation as an example, add a mutation to ModuleA and a mutation to moduleb, as follows:

mutations: {
    setText(state) {
        state.text = 'A'
    }
},

Module B is the same as above. Just change the name of the text. There will be no repetition here. And then backApp.vueIn the text, it is amended as follows:

<script>
    import {mapState, mapMutations} from 'vuex';
    export default {
        computed: {
            ...mapState({
                name: state => ( state.moduleA.text  +'and'+ state.moduleB.text )
            }),
        },
        methods: {
            ...mapMutations(['setText']),
            modifyNameAction() {
                this.setText();
            }
        },
    }
</script>

Run it and click Modify, and we will find thetextThe values have changed. Of course, as like as two peas of action, you can try it.

If there is an intersection of data between modules, we can update the data between modules synchronously in this way. Although it seems very convenient, we must be careful when using it. Once this method is not used well and errors are encountered, it is difficult to check.

4、 Access the root node

We already know that the internal state of the module is local and belongs only to the module itself. So what if we want to access the data state of the store root node in the module?

It’s very simple. We can get it through the parameter rootstate in the getter and action inside the module. Next, let’s give it to youmodelA.jsAdd a bit of code to the file.

export default {
    // ...
    getters: {
        //Note: rootstate must be the third parameter
        detail(state, getters, rootState) {
            return state.text + '-' + rootState.name;
        }
    },
    actions: {
        callAction({state, rootState}) {
            alert(state.text + '-' + rootState.name);
        }
    }
}

Then modify itApp.vue

<script>
    import {mapActions, mapGetters} from 'vuex';
    export default {
        computed: {
            ...mapGetters({
                name: 'detail'
            }),
        },
        methods: {
            ...mapActions(['callAction']),
            modifyNameAction() {
                this.callAction();
            }
        },
    }
</script>

Then run it and you will find that the data of the root node has been obtained by us. It should be noted that in getters, rootstate is exposed by the third parameter. In addition, there is a fourth parameter, rootgetters, which is used to obtain the getters information of the root node. I won’t show it here. If you are interested, you can try it. The only thing to emphasize is that you must not mistake the position of parameters.

Of course, rootgetters can also be received in action, but in action, because all the data it receives is wrapped in thecontextObject, so there is no restriction on the order of unpacking.

5、 Namespace

As we already know, action, mutation and getter in the module are registered in the global namespace by default. What if we just want them to work in the current module?

By addingnamespaced: trueMake it a module with a namespace.When a module is registered, all its getters, actions and mutations will automatically be named according to the registered path of the module.

We are heremoduleA.jsAdd innamespaced: true

export default {
    namespaced: true,
    // ...
}

If you run the code at this time, you will find the following error:

[vuex] unknown getter: detail

Not found in global getterdetailBecause its road strength has changed, it no longer belongs to the global, only belongs to module a. So, at this time, if we want to visit it, we must take the road. modifyApp.vueAs follows:

<script>
    import {mapActions, mapGetters} from 'vuex';
    export default {
        computed: {
            ...mapGetters({
                name: 'moduleA/detail'
            }),
        },
        methods: {
            ...mapActions({
                call: 'moduleA/callAction'
            }),
            modifyNameAction() {
                this.call();
            }
        },
    }
</script>

The first mock exam is that if a module is enabled, the getter, dispatch and commit in getter and action are localized, and there is no need to add space name prefix in the same module. That is to say, changenamespacedProperty does not need to modify any code in the module.

So what do we doAccessing global content within a module with a namespaceWhat about it?

Through the previous study, we have learned that:

If you want to use global state and getter, rootstate and rootgetter will be passed into getter as the third and fourth parameters, and action will also be passed in through the properties of context object.

Now if you want to distribute an action or submit a mutation in the global namespace, we just need to{ root: true }As the third parameter, it can be passed to dispatch or commit.

export default {
    namespaced: true,
    // ...
    actions: {
        callAction({state, commit, rootState}) {
            Commit ('setname ','change', {root: true});
            alert(state.text + '-' + rootState.name);
        }
    }
}

Now let’s see howRegister global action in module with namespace

If you need to register global actions in a module with a namespace, you can addroot: trueAnd put the definition of this action in the function handler.

There is a slight change in the writing. Let’s take a look and revise itmoduleA.js, as follows:

export default {
    namespaced: true,
    // ...
    actions: {
        callAction: {
            root: true,
            handler (namespacedContext, payload) {
                let {state, commit} = namespacedContext;
                commit('setText');
                alert(state.text);
            }
        }
    }
}

In a nutshell, here’s thenamespacedContextIt is equivalent to the context object of the current module,payloadIs the parameter passed in when calling, also called load of course.

That’s all for the example. Let’s take a lookBinding function with namespace

aboutmapState, mapGetters, mapActionsandmapMutationsHow to bind these functions to modules with a namespace has already been written in the above example code. Let’s take a look at other easier ways to write them. Let’s take a look at the previous way.

Here we use the official example code to illustrate:

computed: {
    ...mapState({
        a: state => state.some.nested.module.a,
        b: state => state.some.nested.module.b
    })
},
methods: {
    ...mapActions([
        // -> this['some/nested/module/foo']()
        'some/nested/module/foo', 
        // -> this['some/nested/module/bar']()
        'some/nested/module/bar' 
    ])
}

More elegant writing:

computed: {
    ...mapState('some/nested/module', {
        a: state => state.a,
        b: state => state.b
    })
},
methods: {
    ...mapActions('some/nested/module', [
        'foo', // -> this.foo()
        'bar' // -> this.bar()
    ])
}

Pass the space name string of the module as the first parameter to the above function so that all bindings will automatically use the module as the context.

We can also usecreateNamespacedHelpersCreate helper functions based on a namespace. It returns an object with a new component binding auxiliary function bound to a given namespace value

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    //Search in 'some / nested / module'
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    //Search in 'some / nested / module'
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

6、 Dynamic registration of modules

This chapter is quite clear on the official website, so it’s directly moved here.

After the store is created, you can use thestore.registerModuleMethod dynamic registration module:

//Register module ` mymodule`
store.registerModule('myModule', {
  // ...
})
//Register nested modules ` nested / mymodule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

And then you can go through itstore.state.myModuleandstore.state.nested.myModuleAccess the status of the module.

Module dynamic registration enables other Vue plug-ins to use vuex to manage state by adding new modules to the store. For example,vuex-router-syncPlug in combines Vue router and vuex through dynamic registration module to realize routing state management of application.

You can also use itstore.unregisterModule(moduleName)To dynamically unload the module. Note that you cannot use this method to unload static modules (that is, modules declared when the store was created).

When registering a new module, you are likely to want to keep the past state, for example, from a server-side rendering application. You can go throughpreserveStateOption to archive it:store.registerModule('a', module, { preserveState: true })

7、 Module reuse

For one thing, reuse will pollute the data state in the module, so just like data in Vue, you can use a function to declare the state.

const MyReusableModule = {
  state () {
    return {
      foo: 'bar'
    }
  },
  //...
}

vue

This work adoptsCC agreementReprint must indicate the author and the link of this article

You’re far from it!

Recommended Today

JavaScript animation example: rotated equilateral triangle

Given that the coordinates of the center of gravity of an equilateral triangle are (x0, Y0) and the height is h, you can draw an equilateral triangle with horizontal bottom edge with the following statement.          ctx.beginPath();          ctx.moveTo(x0,y0-h*2/3);          ctx.lineTo(x0+h/Math.sqrt(3), y0+h/3);           ctx.lineTo(x0-h/Math.sqrt(3), y0+h/3);          ctx.lineTo(x0,y0-h*2/3);          ctx.closePath();          ctx.stroke(); Given the number of […]