A simple example to teach you how to write Vuex with Typescript

Case Source: github.com/danielhuoo/...

Foreword

I believe many people like me, learning to use the vuexpost, trying to rewrite the project Typescript. But the official tutorial either obscure or lack vivid example. I spent a day, summed up some experience. In this sharing out.

This tutorial by writing a simple demo to explain the vueximplementation of, and how based on vue2.xexisting projects were Typescript reconstruction.

Project Initialization

9012 now, so we used directly vue-cli 3.x quickly build systems.

# 搭建项目
vue create vue2.x-vuex-typescript-demo

cd vue2.x-vuex-typescript-demo
# 引入vuex
vue add vuex
# 由于我实在不想写任何样式,所以我又加一个element
vue add element
复制代码

Module Description

In order to explain the actual code vuexis how to build, as well as communication between modules, I used a very simple example (the example should be a lot clearer than the official)

scene

Boy to girl flowers.

  1. Each boy took out 10 girls express gratitude.
  2. Girls will increase thanks to the courage of the boy's value.
  3. The boy can buy flowers to the flower shop.

Directory Structure

You will find the default directory structure is as follows:

.
├── README.md
├── babel.config.js
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── main.js
│   ├── plugins
│   │   └── element.js
│   └── store.js
└── yarn.lock

But we want vuex become modular. So we changed to the following structure:

.
├── README.md
├── babel.config.js
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── main.js
│   ├── plugins
│   │   └── element.js
│   └── store
│       ├── index.js
│       └── module
│           ├── boy.js
│           └── girl.js
└── yarn.lock
  1. index.jsIt is the storemain file
  2. /moduleUnder storage module file. boy.jsModule is a boy, girl.jsa girl Module

Module Definition

boy.js

The module defines three actionmethods. actionPopular, what you want to do things module, they can be asynchronous or synchronous. All of the stateadditions and deletions to change the logic of the investigation should be here, but mutationmerely responsible for implementing the additions and deletions to change search.

import { Message } from 'element-ui';
export default {
    namespaced: true,
    // state 的属性只能通过 mutation的方法进行修改
    state: {
        currentFlower: 50,
        braveScore: 0
    },
    mutations: {
        // 修改 state 的 currentFlower 的值
        updateCurrentFlower(state, payload) {
            state.currentFlower = state.currentFlower + payload
        },
        // 修改 state 的 braveScore 的值
        updateBraveScore(state, payload) {
            state.braveScore = state.braveScore + payload.score
        }
    },
    actions: {
        // 送花
        // 方法里 调用了 commit 和 state,需要在传参时声明
        sendFlower({ commit, state }, params) {
            if (!state.currentFlower) {
                Message({
                    showClose: true,
                    message: "没花可送了",
                    type: "warning"
                });
            } else {
                // 送出一朵花,自己的库存减 1
                commit('updateCurrentFlower', -params.sendNumber)
                // 女孩收到一朵花,女孩库存加 1。
                // 注意这里是跨模块调用,所以需要加上模块前缀 'girl/',并且 传入参数 {root:true} 表明通过根路径寻找目标函数。
                commit('girl/updateCurrentFlower', params.sendNumber, { root: true })
            }
        },
        // 受到鼓励
        beEncouraged({ commit }) {
            commit('updateBraveScore', { score: 10 })
        },
        // 买花
        // 方法里调用了 commit, dispatch。 dispatch跨模块调用根store的action,跟送花的commit一样,需要加上前缀和传入{root:true}
        buyFlower({ commit, dispatch }, params) {
            setTimeout(() => {
                dispatch('sellFlower', null, { root: true }).then(() => {
                    commit('updateCurrentFlower', params.buyNumber)
                }).catch(() => {
                    Message({
                        showClose: true,
                        message: "库存不足",
                        type: "warning"
                    });
                })
            }, 100)
        }
    }
}
复制代码

girl.js

export default {
    namespaced: true,
    state: {
        currentFlower: 0
    },
    mutations: {
        updateCurrentFlower(state, payload) {
            state.currentFlower = state.currentFlower + payload
        }
    },
    actions: {
        // 对男孩进行鼓舞
        encourage({ dispatch }, params) {
            dispatch('boy/beEncouraged', null, { root: true })
        }
    }
}
复制代码

index.js

import Vue from 'vue'
import Vuex from 'vuex'
// 引入模块
import boy from './module/boy'
import girl from './module/girl'
Vue.use(Vuex)

export default new Vuex.Store({
    // 根 state
    state: {
        flowersInStock: 10
    },
    // 根 mutations
    mutations: {
        updateFlowersInStock(state, payload) {
            state.flowersInStock = state.flowersInStock + payload
        }
    },
    // 根 actions
    actions: {
        sellFlower({ commit, state }, params) {
            return new Promise((resolve, reject) => {
                if (state.flowersInStock > 0) {
                    commit('updateFlowersInStock', -1)
                    resolve()
                } else {
                    reject()
                }
            })
        }
    },
    // 注册模块
    modules: {
        boy,
        girl
    }
})
复制代码

Connected to the assembly vue

Now logic warehouse has been written, we can use on the component. In fact vuexthe warehouse as early as main.jsthe introduction of the vueexamples in the. For example, this.$store.state.flowersInStockwhich represents the root stateproperty values. But such an approach is too cumbersome, we introduced the vuexoffer mapState, mapActionsand mapMutationsmapping.

boy.vue

<template>
    <div>
        <div>男孩</div>
        <div>手上有{{currentFlower}}朵花</div>
        <div>
            <el-button @click="sendFlower({sendNumber:1})">送花</el-button>
            <el-button @click="buyFlower({buyNumber:1})">买花</el-button>
        </div>
        <div>勇气值:{{braveScore}}</div>
    </div>
</template>
<script>
import { mapState, mapActions } from "vuex";
export default {
    computed: {
        // 你会发现state的映射放在了computed里面。这么做的好处是由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态。
        // 通过映射,this.$store.state.currentFlower 就可以表示为 this.currentFlower
        ...mapState("boy", {
            currentFlower: state => state.currentFlower,
            braveScore: state => state.braveScore
        })
    },

    methods: {
        // actions 放在了methods里面。这不奇怪,因为actions跟mutations一样,都是vuex里面的方法。
        ...mapActions("boy", ["sendFlower", "buyFlower"])
    }
};
</script>
<style>
</style>
复制代码

A lot of people used in the beginning vuexwill not remember, what state, actionsand mutationswhere to put. In fact, easy to remember:

  • stateIt is a property, put computedin.
  • actionsAnd mutationsis a method, put methodsinside.

girl.vueSimilarly, we do not go into details. Next, we began Typescriptto rewrite the code.

installationTypescript

Before installing, make sure to do first backup. Because the installation App.vuewill be overwritten.

yarn add vuex-class
vue add typescript
? Use class-style component syntax? (Y/n)  Yes
? Use Babel alongside TypeScript for auto-detected polyfills? (Y/n) Yes
复制代码

Rewrite start

You will find all the .jsfiles have been changed to .tsthe suffix a. This time the project is not run up. Command line console broke dozens error. In fact, before you do not have all the parts of the reform changing for the better, the project is not going to run through.

index.ts

Local rewritten:

  • The introduction of modulethe way. To importa property of the object
  • It defines the storecategory.
  • Added a RootState.
import Vue from 'vue'
import Vuex, { StoreOptions } from 'vuex'
import { boy } from './module/boy'
import { girl } from './module/girl'
import { RootState } from './root-types';
Vue.use(Vuex)
const store: StoreOptions<RootState> = {
    // 里面的内容不用修改
    state: {
        flowersInStock: 10
    },
    modules: {
        boy,
        girl
    },
    mutations: {
        updateFlowersInStock(state, payload) {
            state.flowersInStock = state.flowersInStock + payload
        }
    },
    actions: {
        sellFlower({ commit, state }) {
            return new Promise((resolve, reject) => {
                if (state.flowersInStock > 0) {
                    commit('updateFlowersInStock', -1)
                    resolve()
                } else {
                    reject()
                }
            })
        }
    }
}
export default new Vuex.Store<RootState>(store)
复制代码

root-types.ts

This is the root of stateconstraints

export interface RootState {
    flowersInStock: number
}
复制代码

boy.ts

The module is a huge change.

  • Added module Stateinterfaces
  • The definition mutationsof classMutationTree
  • The definition actionsof classActionTree
  • Class definition module is Module
import { Message } from 'element-ui';
import { BoyState } from './module-types';
import { MutationTree, ActionTree, Module } from 'vuex';
import { RootState } from '../root-types';
const state: BoyState = {
    currentFlower: 50,
    braveScore: 0
}

// 传入的泛型可以通过查看源代码得知。
const mutations: MutationTree<BoyState> = {
    updateCurrentFlower(state, payload) {
        state.currentFlower = state.currentFlower + payload
    },

    updateBraveScore(state, payload) {
        state.braveScore = state.braveScore + payload.score
    }
}
const actions: ActionTree<BoyState, RootState> = {
    sendFlower({ commit, state }, params) {
        if (!state.currentFlower) {
            Message({
                showClose: true,
                message: "没花可送了",
                type: "warning"
            });
        } else {
            commit('updateCurrentFlower', -params.sendNumber)
            commit('girl/updateCurrentFlower', params.sendNumber, { root: true })
        }

    },
    buyFlower({ commit, dispatch }, params) {
        setTimeout(() => {
            dispatch('sellFlower', null, { root: true }).then(() => {
                commit('updateCurrentFlower', params.buyNumber)
            }).catch(() => {
                Message({
                    showClose: true,
                    message: "库存不足",
                    type: "warning"
                });
            })
        }, 100)
    },
    beEncouraged({ commit }) {
        commit('updateBraveScore', { score: 10 })
    }
}
export const boy: Module<BoyState, RootState> = {
    namespaced: true,
    state,
    mutations,
    actions
}
复制代码

boy.vue

vueFile is a lot of place changes:

  • scriptTag specifies the tslanguage
  • The use of Componentmodified components
  • export Class component from the object becomes
  • Abandoned mapStateand other methods, use State, Action, Mutationdecorator bindingvuex
  • Abandoned computed, methods, dataand other written, using get + 方法expressed computed, methodsin the method of directly extracted, the dataattribute to be extracted directly.
<script lang="ts">
import { Vue, Component, Watch } from "vue-property-decorator";
import { State, Action, Mutation, namespace } from "vuex-class";
import { BoyState } from "../store/module/module-types";
@Component
export default class boyComponent extends Vue {
    @State("boy") 
    // 感叹号不能省略
    boyState!: BoyState;

    @Action("sendFlower", { namespace: "boy" })
    sendFlower: any;

    @Action("buyFlower", { namespace: "boy" })
    buyFlower: any;

    get currentFlower(): number {
        return this.boyState.currentFlower;
    }

    get braveScore(): number {
        return this.boyState.braveScore;
    }
}
</script>
复制代码

Other files are also in a similar way to rewrite. A new name.

Above is Typescriptan example of rewriting. Some places did not explain very clearly, because I am also a white ah, do not know where or not to mislead people. If the logic of your project is more complex than this (sure it), but this project does not cover your doubts, you can change a good look at my other projects Jessic .

  • Author: Daniel Huo
  • Links: danielhuoo.github.io/2019/06/18/...
  • Copyright reserved by the authors. Commercial reprint please contact the author authorized, non-commercial reprint please indicate the source.

Reproduced in: https: //juejin.im/post/5d09682d518825531e0648a7

Guess you like

Origin blog.csdn.net/weixin_33763244/article/details/93169441