preface

Recently, many problems have been exposed while working with friends, and perhaps the most troubling of these is code organization.

The ability of code organization will be gradually developed with the expansion of knowledge, but in this process, whether you have seen excellent and eye-catching code and gained insights from it will largely affect the strength of the final code organization ability. It’s not just about your experience and how many projects you’ve done, it’s about consciously and actively learning from others by reading good source code.

However, I did not find many similar articles on the Internet, so I decided to write an article of my own. Based on the recent competition management system project, I summarized the application of Vuex in the project and summarized some general things. I can’t say my way is the best, but I believe it can help everyone.

Reprint please attach the original link, support the original

Project introduction

Name: Competition Management System

Functions: add, delete, change and check user information, add, delete, change and check event information, add, delete, change and check competition records, upload attachments, check results, etc.

Technology stack (front end) : Vue family bucket + Ant-Design-Vue

Project address: GitHub

Project organization — Vuex part

Vuex doesn’t limit your code structure. However, it lays down some rules that need to be followed:

  1. Application-level state should be centralized into a single Store object.
  2. Submitting mutation is the only way to change the state, and the process is synchronous.
  3. Asynchronous logic should be wrapped in actions.

As long as you follow these rules, you can organize your code any way you want. If your store file is too large, just split the action, mutation, and getter into separate files.

— from official documents

The so-called application level states in this project are: user information, event information and competition records.

Using state as a standard for application-level state, you can consider whether the state is shared by all or more components, or whether you have spent a lot of effort communicating that state between components. If so, go for it.

Let’s start with Store.

Store

As a management system, add, delete, change and check. Take the administrator as an example. The task of the administrator is to add, delete, change and check users’ information, race information and records. To use Vuex we first need to create a Store directory.

─ ─ store ├ ─ ─ index. Js # we assembled module and export store ├ ─ ─ actions. Js # root level action ├ ─ ─ mutations. Js # root level mutation ├ ─ ─ RACES # RACES module │ ├ ─ ─ index. Js # export module │ ├ ─ ─ mutations. Js # module level of mutations │ └ ─ ─ actions. Js # module level actions ├ ─ ─ The users │ ├ ─ ─ index. Js │ ├ ─ ─ mutations. Js │ └ ─ ─ actions. Js └ ─ ─ records ├ ─ ─ index. The js ├ ─ ─ mutations. Js └ ─ ─ actions. JsCopy the code

Since each kind of data involves adding, deleting, modifying, and checking operations, I choose to use Modules in Vuex to organize them. Each folder exports a Module, which is finally summarized into the external index, and then exports the Store object to register Vuex.

Each module has an action and a mutation. The action is used to send the request, and the mutation changes the state based on the result of the request (I wrote the state inside the module because of the simple structure of the state), and the changes are finally reflected in the view.

Here is an example of race information data (Races) :

Races

The following three codes correspond to the races directory: Races /index.js, races/actions.js, and races/ blocks.js

/** * index.js */
import actions from './actions'
import mutations from './mutations'

// It is everywhere in the races module and registered as module in store/index.js
export default {
  namespaced: true.// The namespace is set only to make the module more independent, see the official documentation for details
  state: {
    races: []
  },
  mutations,
  actions
}
Copy the code
/** * actions.js */
import { SET_RACE_LIST, ADD_RACE, UPDATE_RACE, DELETE_RACE } from '.. /mutation-types'
import { getRaceList, addRace, updateRace, deleteRace } from '.. /.. /api'
import { message } from 'ant-design-vue'

export default {
  [SET_RACE_LIST] ({ commit }, params) {
    return new Promise((resolve, reject) = > {
      getRaceList(params).then(({ data: races }) = > {
        resolve(races)
        commit(SET_RACE_LIST, races)
      }).catch(e= > {
        reject(e)
        message.error('System error, please try again')
      })
    })
  },
  [ADD_RACE] ({ commit }, race) {
    const stopLoading = message.loading('Just a moment')
    return new Promise((resolve, reject) = > {
      addRace(race).then(({ data }) = > {
        resolve(data)
        commit(ADD_RACE, data)
        message.success('Added successfully')
      }).catch(e= > {
        reject(e)
        message.error('System error, please try again')
      }).finally((a)= >{ stopLoading() }) }) }, [UPDATE_RACE] ({ commit }, race) { ... }, [DELETE_RACE] ({ commit }, _id) { ... }}Copy the code
/** * mutations.js */
import { SET_RACE_LIST, ADD_RACE, UPDATE_RACE, DELETE_RACE } from '.. /mutation-types'

export default {
  [SET_RACE_LIST] (state, races) {
    state.races = races
  },
  [ADD_RACE] (state, race) {
    state.races.push(race)
  },
  [UPDATE_RACE] (state, race) {
    state.races = state.races.map(item= > {
      return item._id === race._id ? race : item
    })
  },
  [DELETE_RACE] (state, _id) {
    state.races = state.races.filter(item= >item._id ! == _id) } }Copy the code

Looking at the code above, you may have the following questions

What is mutation-types?

/** * mutation-types.js -- basically defining some constants */
export const LOGIN = 'LOGIN'
export const LOGOUT = 'LOGOUT'
export const REFRESH_TOKEN = 'REFRESH_TOKEN'

/ / RACES module
export const SET_RACE_LIST = 'SET_RACE_LIST'
export const ADD_RACE = 'ADD_RACE'
export const UPDATE_RACE = 'UPDATE_RACE'
export const DELETE_RACE = 'DELETE_RACE'. .Copy the code

Mutation types specifies the names of the action and mutation methods, which are specified in the following syntax:

It doesn’t really matter what the values of these constants are, as long as they’re not duplicated, they’re usually the same as the name of the variable. You can also do this using symbols in ES6.

There are two main objectives:

  1. Make vuex’s function calls more semantic
  2. Reduce bugs caused by misspelled names

You might ask, well, the name of a variable is the same as the value of a variable, so why not just use a string? Examples are as follows:

import { ADD_RECORD } from 'mutation-types'
store.dispatch(ADD_RECORD) // If you write something wrong, the compiler will report an error
store.dispatch('ADD_RECORD') // The compiler does not report errors, and the runtime will find them if they are written wrong
Copy the code

In fact, if you use strings directly, even if you find errors, you don’t necessarily know where you wrote them, because you’ll end up with strings all over your code, and who can guarantee that you’ll write them right? And it’s officially recommended to maintain a growing number of projects, both graphic and factual:

What? You’re just a little demo? Ok, no problem, next 👌.

In large projects, in order to facilitate the later maintenance of the project, repeated constants must be extracted and defined separately. Help yourself to the little demo.

What does action say?

Action is similar to mutation, except that:

  • The Action commits mutation instead of directly changing state.
  • Actions can contain any asynchronous operation

That’s the official definition. In the official sample documentation, we can see many different ways to use it. It’s worth noting how you combine actions, which return a Promise object and are free to combine with business logic. In this way, we can customize a lot of logic in the component, such as customizing the loading state:

import { GET_RACE_LIST } from 'mutation-types'
export default {
	mounted () {
        this.loading = true
    	this.$store.dispatch(GET_RACE_LIST).finally((a)= > {
            this.loading = false}}})Copy the code

In the code above races/action.js, the Message API in Ant-Design-vue is introduced. Make different prompts, success feedback, error handling and so on according to different request results. In this way, the component only needs to call store.dispatch() to make a request and do something related to the component based on the result of the request. Any data related tasks are handled automatically by action and mutation. Just focus on the logic of the component itself.

API

In races/actions.js there is a line of code that looks like this

import { getRaceList, addRace, updateRace, deleteRace } from '.. /.. /api'
Copy the code

The API is packaged separately for the simple reason of reuse and constant references like mutation-types. If you have to write the requested address in its entirety every time you send a request, you’ll go crazy if the requested address changes, forcing you to use a global search to replace it, which is inefficient.

This is my API directory structure

API ├ ─ ─ index. # js export API ├ ─ ─ handle404. Js # cooperate axios. Js treatment response to intercept └ ─ ─ axios. # js export axios objects, do some global configuration, such as requests to intercept, response to intercept, baseUrl, etcCopy the code

Here’s what index.js looks like:

/** * index.js defines the interface */. . exportconst getRaceList = params= > axios.get('/race/list', { params })
export const addRace = data= > axios.post('/race/add', data)
export const updateRace = data= > axios.put('/race/update', data)
export const deleteRace = _id= > {
  return axios.delete('/race/delete', { data: { _id } }) } ... .Copy the code

For more information on axios.js and handle404.js, which cover global interception and token stateless refreshing, check out my other article — Login Validation Practices — Token & Refresh Token.

Afterword.

Once upon a time, when asked about project experience, there was a question in my heart: what is a project?

I think of myself in my freshman year. At that time, I thought that a project was a very lofty thing, which was composed of many documents with complicated relationships among them. It must be made by many people working together. But now I understand that a project is nothing more than a collection of features. The separate files eventually come together to achieve a complete function, and are separated for ease of maintenance. In this sense, a project can be called a project regardless of its size if it is a complete feature, even if it is a simple index.html file.

Of course, this article focuses on the way code is organized in large projects, so make small demos as comfortable as possible. After all, code style is different from person to person, and we can’t say that beautiful code is always better. In general, a good code style is good for troubleshooting, good for teamwork, good for self-cultivation, good for… All in all, the benefits are not small.

Finally, I hope to be of help to you. Please attach the original link for reprinting

reference

Vuex official document