前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue之vuex2.0使用详解

vue之vuex2.0使用详解

作者头像
山行AI
发布2019-07-30 18:55:03
1.6K0
发布2019-07-30 18:55:03
举报
文章被收录于专栏:山行AI

本文是我在学习vuex的时候看到的比较好的示例整理而成,需要看原文的可以看看文末参考部分的链接。

Vuex 的核心是 store, 它是一个通过 Vuex.Store 构造函数生成的对象。为什么它会是核心呢?因为我们调用这个构造函数创建store 对象的时候,给它传递参数中包装了state, mutation , action 等核心内容。Vuex 的思想是 当我们在页面上点击一个按钮,它会处发(dispatch)一个action, action 随后会执行(commit)一个mutation, mutation 立即会改变state,state改变以后我们的页面会从state获取数据从而改变页面。Store对象包含了我们谈到的所有内容:action, state, mutation,所以是核心了。

示例

使用vue-cli webpack-simple

  1. vue init webpack-simple learn:其中learn为项目名称。
  2. cd learn && cnpm install && npm run dev
  3. cnpm install vuex –save: 安装vuex 附: 在webstorm中新建项目:
  1. 在src下新建display.vue和increment.vue: display.vue:
代码语言:javascript
复制
<template>
    <div>
        <h3>Count is 0</h3>
    </div>
</template>

<script>

</script>

<style scoped>
    h3 {
        font-size: 30px;
    }
</style>

increment.vue:

代码语言:javascript
复制
<template>
    <div>
        <button>+1</button>
        <button>-1</button>
    </div>
</template>

<script>

</script>

<style scoped>
    button  {
        width: 100px;
        height: 100px;
        font-size: 30px;
    }
</style>
  1. 改造App.vue
代码语言:javascript
复制
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <display></display>
    <increment></increment>
  </div>
</template>

<script>
import display from "./display.vue";
import increment from "./increment.vue";

export default {
  data () {
    return {

    }
  },
  components: {
    display,
    increment
  }
}
</script>

<style>
  #app {
    text-align: center;
  }
</style>

运行起来结果如下图:

state

When we say state we are talking about a group of dozens of variables to store data。

state就是页面上用到的各种变量。在写vue页面中,我们经常用{{mes}}取值, mes就是vuex的一个状态。这个状态变化时,页面就会重新渲染。

  1. 用vuex进行状态管理,在src目录下新建store.js文件。我们要在display.vue上添加一个变量count,并对这个变量进行管理。所以在创建store对象的时候,给构造函数传入count参数,且初始化为0。
代码语言:javascript
复制
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

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

export default store
  1. 在App.vue中使用store对象,vue 提供了注入机制,就是把我们的store 对象注入到根实例中。vue的根实例就是new Vue构造函数,然后在所有的子组件中,this.$store来指向store对象。在store.js中我们export store把store暴露出去了,new Vue() 在main.js中,所以直接在main.js中引入store并注入即可。main.js:
代码语言:javascript
复制
import Vue from 'vue'
import App from './App.vue'

import store from "./store.js"  // 引入store 对象

new Vue({
  el: '#app',
  store,  // 注入到根实例中
  render: h => h(App)
})
  1. 改造display.vue,display.vue为App.vue的子组件,所以用this.$store来指向store对象。这样就可以把store.js中的count与display.vue的count绑定了。display.vue:
代码语言:javascript
复制
<template>
    <div>
        <h3>Count is {{count}}</h3>
    </div>
</template>

<script>
    export default {
        computed: {
            count () {
                return this.$store.state.count
            }
        }
    }
</script>
  1. 通过computed属性可以获取到状态值,但是组件中每一个属性(如:count)都是函数,如果有10个,那么就要写10个函数,且重复写10遍return this.$store.state,不是很方便。vue 提供了 mapState 函数,它把state 直接映射到我们的组件中。当然使用mapState 之前要先引入它。它两种用法,或接受一个对象,或接受一个数组。还是在display.vue 组件下。
代码语言:javascript
复制
<template>
    <div>
        <h3>Count is {{count}}</h3>
    </div>
</template>

<script>
    import {mapState} from "vuex";  // 引入mapState

    export default {
      // 下面这两种写法都可以
        computed: mapState({
            count: state => state.count   // 组件内的每一个属性函数都会获得一个默认参数state, 然后通过state 直接获取它的属性更简洁
            count: 'count'         // 'count' 直接映射到state 对象中的count, 它相当于 this.$store.state.count,
        })
    }
</script>

上面的方式还可以使用更简单的数组方式代替。

代码语言:javascript
复制
<script>
    import {mapState} from "vuex";

    export default {
        computed: mapState([   // 数组
            "count"
        ])
    }
</script>

如果我们组件内部也有computed 属性怎么办?它又不属于mapState 中。那就用到了对象分割,把mapState函数生成的对象再分割成一个个的,就像最开始的时候,我们一个一个罗列计算属性,有10个属性,我们就写10个函数。

es6中的... 就是分割用的,但是只能分割数组。在ECMAScript stage-3 阶段它可以分割对象,所以这时还要用到babel-stage-3; npm install babel-preset-stage-3 --save-dev, 安装完全后,一定不要忘记在babelrc 就是babel 的配置文件中,写入stage-3,

否则一直报错。在页面中添加个p标签,显示我们组件的计算属性。

es6下需要在babel.rc文件中配置:

代码语言:javascript
复制
{
  "presets": [
    ["env", {
      "es2015": { "modules": false }
    }],
    "stage-3"   // 一定不要忘记
  ]
}

display.vue:

代码语言:javascript
复制
<template>
    <div>
        <h3>Count is {{count}}</h3>
        <p>组件自己的内部计算属性 {{ localComputed }}</p>
    </div>
</template>

<script>
    import {mapState} from "vuex";
    export default {
        computed: {
            localComputed () {
                return this.count + 10;
            },
            ...mapState({
                count: "count"
            })
        } 
    }
</script>

这时的效果是修改store.js中state.count的值,在display.vue中会重新渲染。将state.count改成20看下效果:

mutation

The only way to actually change state in a Vuex store is by committing a mutation

在vue 中,只有mutation 才能改变state. mutation 类似事件,每一个mutation都有一个类型和一个处理函数,因为只有mutation才能改变state, 所以处理函数自动会获得一个默认参数 state。所谓的类型其实就是名字,action去commit一个mutation, 它要指定去commit哪个mutation, 所以mutation至少需要一个名字。commit mutation 之后,要做什么事情,那就需要给它指定一个处理函数, 类型(名字) + 处理函数就构成了mutation。现在store.js添加mutation:

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        count:0
    },
    mutations: {
        // 加1
        INCREMENT(state) {
            state.count++;
        },
        // 减1
        DECREMENT(state) {
            state.count--
        }
    }
})

注意: Vue 建议我们mutation 类型用大写常量表示。

action

需要用action去commit mutations,所以在store.js中添加action定义:

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        count:0
    },
    mutations: {
        // 加1
        INCREMENT(state) {
            state.count++;
        },
        // 减1
        DECREMENT(state) {
            state.count--
        }
    },
    actions: {
        increment(context) {
            context.commit("INCREMENT");
        },
        decrement(context) {
            context.commit("DECREMENT");
        }
    }
})

action 和mutions 的定义方法是类似的,我们要dispatch 一个action, 所以actions 肯定有一个名字,dispatch action 之后它要做事情,就是commit mutation, 所以还要给它指定一个函数。因为要commit mutation ,所以 函数也会自动获得一个默认参数context, 它是一个store 实例,通过它可以获取到store 实例的属性和方法。如 context.state 就会获取到 state 属性,context.commit 就会执行commit命令。

actions 还可以简写一下, 因为函数的参数是一个对象,函数中用的是对象中一个方法,我们可以通过对象的解构赋值直接获取到该方法:

代码语言:javascript
复制
actions: {
        increment({commit}){
            commit("INCREMENT")
        },
        decrement({commit}){
            commit("DECREMENT")
        }
    }

dispatch action

什么时候dispatch action 呢?只有当我们点击按钮的时候. 给按钮添加click 事件,在click 事件处理函数的中dispatch action.

打开increment.vue 组件,给两个按钮添加click 事件。

代码语言:javascript
复制
<template>
    <div>
        <button @click="increment">+1</button>
        <button @click="decrement">-1</button>
    </div>
</template>

<script>
    export default {
        methods: {
            increment(){
                this.$store.dispatch("increment");
            },
            decrement() {
                this.$store.dispatch("decrement")
            }
        }
    }
</script>

其实像上面dispatch action 比较麻烦,如果有10 个按钮,我们要写10 个函数,且存在大量的重复,并且我们的事件处理函数名字和action的名字是一样的。vue 提供了mapAction 函数,它和mapState 是一样的,把我们的 action 直接映射到store 里面的action中。像这种组件中的事件处理函数名字和action的名字是相同的,直接把 事件处理函数名字放到一个数组中。组件中的methods 修改如下:

代码语言:javascript
复制
<script>
    import {mapActions} from "vuex";
    export default {
        methods: {
            ...mapActions(["increment", "decrement"])
        }
    }
</script>

如果事件处理函数名字和action的名字不同,给mapActions 提供一个对象,对象的属性是事件处理函数名字, 属性值是 对应的dispatch 的action 的名字。

我们把 +1 按钮的事件处理函数变改为 add,代码如下:

代码语言:javascript
复制
<template>
    <div>
        <button @click="add">+1</button>    <!-- 事件处理函数变为add -->
        <button @click="decrement">-1</button>
    </div>
</template>

<script>
    import {mapActions} from "vuex";
    export default {
        methods: {
            ...mapActions(["decrement"]),

       // mapActions 对应做出改变
            ...mapActions({
                add: "increment"
            })
        }
    }

这时候我们单击按钮,就可以看到count 发生变化。

通过vuex 传递参数

很多时候,组件和组件之间还要传递参数,这些都要经过vuex。在increment 组件内部增加一个输入框和一个按钮,点击按钮的时候,count 增加输入框内的值。

代码语言:javascript
复制
<template>
    <div>
        <div>
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
    // 增加内容
        <div>
            <input type="text" v-model="incrementValue">
            <button @click="incrementWithValue">increment</button>
        </div>
    </div>
</template>

在组件内部dispatch action 的时候,它可以自定义参数,只要参数在它dispatch 的action名称 后面,依次列出来就可以了。在这里,我们点击按钮的时候,触发一个incrementWithValue action, 并且带一个参数,就可以这样写 this.$store.dispatch(“incrementWithValue”, value), 整个increment.vue 组件如下:

代码语言:javascript
复制
<template>
    <div>
        <div>
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
        <div>
            <input type="text" v-model="incrementValue">
            <button @click="incrementWithValue">increment</button>
        </div>
    </div>
</template>

<script>
    import {mapActions} from "vuex";
    export default {
        data() {
            return {
                incrementValue: 0
            }
        },
        methods: {
            ...mapActions(["increment","decrement"]),
            incrementWithValue() {
                this.$store.dispatch("incrementWithValue", this.incrementValue)
            }
        }
    }
</script>

同样,actions 和mutations 中的处理函数也是一样,它除了可以得到默认参数外,还可以接受自定义的参数,我们自定义的参数,依次在默认参数后面列出来就可以了。在store.js中分加增加incrementWithValue action和 INCREMENTWITHVALUE mutation:

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        count:0
    },
    mutations: {
        // 加1
        INCREMENT(state) {
            state.count++;
        },
        // 减1
        DECREMENT(state) {
            state.count--
        },
        INCREMENT_WITH_VALUE(state, value){
            state.count +=value;
        }
    },
    actions: {
        increment({commit}){
            commit("INCREMENT")
        },
        decrement({commit}){
            commit("DECREMENT")
        },
        incrementWithValue({commit}, value){
            commit("INCREMENT_WITH_VALUE",  parseInt(value))
        }
    }
})

错误处理: 当我们给vuex 传参的时候,我们要检测参数的正确性,如果有错误需要进行处理

action 中如果是同步操作,就用try..catch 语句,组件中使用try…catch, 捕获action中抛出的错误。Increment.vue 组件中,incrementWithValue() 方法中修改如下:

代码语言:javascript
复制
methods: {
            ...mapActions(["increment","decrement"]),
            incrementWithValue() {
                 try {
                     this.$store.dispatch("incrementWithValue", this.incrementValue)
                 }catch(error) {
                     alert(error)
                 }
            }
        }

同时store.js 中的action 也进行如下修改:

代码语言:javascript
复制
incrementWithValue({commit}, value){
    let intValue = parseInt(value);
    if(isNaN(intValue)){
        throw "Not an Interger"
    }else {
        commit("INCREMENT_WITH_VALUE",  intValue)
    }
}

如果action 中进行的是异步操作,那就需要在回调函数中进行错误处理。

代码语言:javascript
复制
incrementWithValue({commit}, value){
      let intValue = parseInt(value)
            setTimeout(function() {

                if(isNaN(intValue)) {
                    alert("Not an Interger")
                }else {    
                    commit("INCREMENT_WITH_VALUE", intValue)
                }
            }, 2000)
        }

异步操作给出用户提示信息: 首先,在我们的increment.vue中添加一个提示信息,简单给一个div 进行提示。用户提示信息的显示和隐藏,又涉及到一个状态,我们设为waiting, 需要在state 中进行添加。默认为false, 同时我们组件需要从state 中获取到初始状态。increment.vue 组件修改如下:

代码语言:javascript
复制
<template>
    <div>
        <div>
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
        <div>
            <input type="text" v-model="incrementValue">
            <button @click="incrementWithValue">increment</button>
        </div>
        <!-- 展示信息 -->
        <div v-if ="show">
            waiting
        </div>
    </div>
</template>

<script>
    import {mapActions} from "vuex";
    export default {
        data() {
            return {
                incrementValue: 0
            }
        },
    // computed 从state 中获取初始状态
        computed: {
            show: function() {
                return this.$store.state.waiting;
            }
        },
        methods: {
            ...mapActions(["increment","decrement"]),
            incrementWithValue() {
                this.$store.dispatch("incrementWithValue", this.incrementValue)
            }
        }
    }
</script>

mutation 去操作状态,所以增加两个muatation, 用于显示和隐藏waiting. action 中去触发这两个mutation. 整个state 如下:

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        count:0,
        // 新增waiting  状态
        waiting: false
    },
    mutations: {
        // 加1
        INCREMENT(state) {
            state.count++;
        },
        // 减1
        DECREMENT(state) {
            state.count--
        },
        INCREMENT_WITH_VALUE(state, value){
            state.count +=value;
        },
        // 显示和隐藏waiting
        SHOW_WAITING_MESSAGE(state){
            state.waiting = true;
        },
        HIDE_WAITING_MESSAGE(state){
            state.waiting = false;
        }
    },
    actions: {
        increment({commit}){
            commit("INCREMENT")
        },
        decrement({commit}){
            commit("DECREMENT")
        },
        incrementWithValue({commit}, value){
            commit("SHOW_WAITING_MESSAGE");
            let intValue = parseInt(value)
            setTimeout(function() {
                if(isNaN(intValue)) {
                    alert("Not an Interger")
                }else {    
                    commit("HIDE_WAITING_MESSAGE");
                    commit("INCREMENT_WITH_VALUE", intValue)
                }
            }, 2000)
        }
    }
})

注意: mutation是同步的,只要comit muation, 它就会立即改变state , 这有利于我们追踪 状态的改变。如果 mution 之后,五分钟才改变state, 那就真不知道state 到底是哪个state了。action 是异步的,还有的错误处理也都在这里操作。

结果: 可以看到将input中的值改成5时,点increment按钮时,上面的display.vue中的值会按input中的步长进行变化。

getters

的组件中获取到 store 中的state, 需要对进行加工才能使用,computed 属性中就需要写操作函数,如果有多个组件中都需要进行这个操作,那么在各个组件中都写相同的函数,那就非常麻烦,这时可以把这个相同的操作写到store 中的getters, 每个组件只要引用getter 就可以了,非常方便。Getter 就是把组件中共有的对state 的操作进行了提取,它就相当于 对state 的computed. 所以它会获得state 作为第一个参数。

假设我们在点击increment按钮的时候,再增加一个额外的数,那么就需要在display.vue 中显示我们多增加了哪个数,同时在increment.vue 中要获得这个数进行相加。state 状态中增加 anotherIncrement: 5.要获取到state 中的这个值, 在每个组件中都要写computed: this.$store.state. countAnother, 这时就可以用getters, 然后在每个组件中computed 中使用getter.

display.vue 修改如下:

代码语言:javascript
复制
<template>
    <div>
        <h3>Count is {{count}}</h3>
        <h3>AnoterIncrement is {{countAnother}}</h3>
    </div>
</template>

<script>
    import {mapState} from "vuex";
    export default {
        computed: {
            ...mapState(["count"]),
            countAnother: function () {  // 获取state
                return this.$store.getters.countAnother;
            }
        }    
    }
</script>

increment.vue 修改如下,这里dispatch中,只能接受一个参数,如果我们要传多个参数的话,需要把所有的参数组装成对象。

代码语言:javascript
复制
<script>
    import {mapActions} from "vuex";
    export default {
        data() {
            return {
                incrementValue: 0
            }
        },
        computed: {
            show: function() {
                return this.$store.state.waiting;
            },
            countAnother: function () { // 获取getters
                return this.$store.getters.countAnother;
            }
        },
        methods: {
            ...mapActions(["increment","decrement"]),
            incrementWithValue() {
          // dispatch 只能接受一个参数,需要传对象参数
                this.$store.dispatch("incrementWithValue", { value:this.incrementValue, anotherValue: this.countAnother})
            }
        }
    }
</script>

store.js 修改如下:

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        count:0,
        // 新增waiting  状态
        waiting: false,
        // 额外需要增加的数字
        anotherIncrement: 5
    },
    mutations: {
        // 加1
        INCREMENT(state) {
            state.count++;
        },
        // 减1
        DECREMENT(state) {
            state.count--
        },
        INCREMENT_WITH_VALUE(state, value){
            state.count = state.count + value.intValue + value.anotherValue;
        },
        // 显示和隐藏waiting
        SHOW_WAITING_MESSAGE(state){
            state.waiting = true;
        },
        HIDE_WAITING_MESSAGE(state){
            state.waiting = false;
        }
    },
    actions: {
        increment({commit}){
            commit("INCREMENT")
        },
        decrement({commit}){
            commit("DECREMENT")
        },
        incrementWithValue({commit}, value){
            commit("SHOW_WAITING_MESSAGE");
            let intValue = parseInt(value.value)
            let anotherValue = value.anotherValue
            setTimeout(function() {

                if(isNaN(intValue)) {
                    alert("Not an Interger")
                }else {    
                    commit("HIDE_WAITING_MESSAGE");
                    commit("INCREMENT_WITH_VALUE", {intValue, anotherValue})
                }
            }, 2000)
        }
    },
    getters: {  // getters
        countAnother: function (state) {
            return state.anotherIncrement
        }
    }
})

vuex 也提供了mapGetters 方法,和其的mapState,mapActions 是一样的,如果组件中使用的getters 和store 里面的getters 相同,那就用 数组形式,如果不相同,那就要用对象形式。

increment.vue 修改一下:

代码语言:javascript
复制
computed: {
            show: function() {
                return this.$store.state.waiting;
            },
           ...mapGetters{["countAnother"]} 
}

到这里,vuex 中的 state, action,mutation, getter 等重要概念就介绍完了,但是,我们把所有的getters, actions, mutations 都写到的store 中,如果有很多的话,代码可读性太差,所以就需要 action 创建一个actions.js 文件,mutations 创建一个mutation.js文件,getters 也创建一个getters.js文件,state 作为主要的入口文件命名为index.js,把这四个js文件放到store 文件夹中。

state所在的文件命名为index.js 还和 nodejs 加载模块有关。如果不命名为index.js , 那假设命名为store.js.

在store.js, 我们暴露出通过 new Vuex.Store 构造函数生成的store 对象(export default new Vuex.Store({...})), 这个store 对象需要在 main.js 中引入,然后注入到vue 根实例中。所以在 main.js 中需要写入 import store from './store/store.js', 后面的路径就比较长了。如果我们命名为 index.js, 我们可以直接写 import store from './store', 后面的路径直接到文件夹名就可以了,index.js 可以省略。node 在加载文件夹模块的时候,有如下规定:

var mode = require(“./moduleDir”);

如果moduleDir 是一个文件夹名,Node 就会在指定的文件夹下查找模块。Node 会假定该文件夹是一个包,并试验查找包定义。包定义在名为 package.json 文件中。如果文件夹中没有package.json, 那么就会查找index.js文件,相当于加载 var mode = require(“./moduleDir/index.js”). 如果有package.json 文件,就会查找文件中的 main 属性,如下package.json文件, 相当于加载 var mode = require(“./moduleDir/lib/mymodldule.js”):

代码语言:javascript
复制
{ “name”: “myModule”,  “main” : “./lib/myModule.js”}

在src 目录下,新建store.js 文件夹,里面新建getters.js, actions.js, mutations.js, index.js 文件。

getters.js 文件如下:

代码语言:javascript
复制
export default {
    countAnother: function (state) {
        return state.anotherIncrement
    }
}

actions.js 文件如下:

代码语言:javascript
复制
export default {
    increment({commit}){
        commit("INCREMENT")
    },
    decrement({commit}){
        commit("DECREMENT")
    },
    incrementWithValue({commit}, value){
        commit("SHOW_WAITING_MESSAGE");
        let intValue = parseInt(value.value)
        let anotherValue = value.anotherValue
        setTimeout(function() {

            if(isNaN(intValue)) {
                alert("Not an Interger")
            }else {    
                commit("HIDE_WAITING_MESSAGE");
                commit("INCREMENT_WITH_VALUE", {intValue, anotherValue})
            }
        }, 2000)
    }
}

muations 文件如下:

代码语言:javascript
复制
export default {
    // 加1
    INCREMENT(state) {
        state.count++;
    },
    // 减1
    DECREMENT(state) {
        state.count--
    },
    INCREMENT_WITH_VALUE(state, value){
        state.count = state.count + value.intValue + value.anotherValue;
    },
    // 显示和隐藏waiting
    SHOW_WAITING_MESSAGE(state){
        state.waiting = true;
    },
    HIDE_WAITING_MESSAGE(state){
        state.waiting = false;
    }
}

index.js 文件如下:

代码语言:javascript
复制
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

// 引入actions, mutations, getters
import actions from "./actions.js"
import mutations from "./mutations.js"
import getters from "./getters.js"


const state = {
    count:0,
    // 新增waiting  状态
    waiting: false,
    // 额外需要增加的数字
    anotherIncrement: 5
}

export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters
})

modules

vue 使用的是单一状态树对整个应用的状态进行管理,也就是说,应用中的所有状态都放到store中,如果是一个大型应用,状态非常多, store 就会非常庞大,不太好管理。这时vuex 提供了另外一种方式,可以把整个store 分成几个大的模块,如登录模块,用户模块等,每一个模块都有自己的state, mutation, actions ,getters , 它就相当于是一个小的store,然后我们的根store(通过new Vuex.Store 生成的store) 通过它的modules属性引入这些模块,从而我们的组件就可以使用这些modules 中状态(state).

  1. 新建一个项目体验一下,通过vue –cli新建一个项目vuemodule, 不要忘记安装vuex.
  2. 在src 目录下新一个login文件夹,在里面新建index.js 用于存放login 模块的状态。为了简单起见,我把模块下的state, actions,mutations, getters 全放在index.js文件中。先简单给它增加一个状态,userName: “sam”:
代码语言:javascript
复制
const state = {
    useName: "sam"
};
const mutations = {

};
const actions = {

};
const getters = {

};

// 不要忘记把state, mutations等暴露出去。
export default {
    state,
    mutations,
    actions,
    getters
}
  1. 在src 目录下,再新建一个 store.js 这是根store, 它通过modules 属性引入 login模块。
代码语言:javascript
复制
import Vue from "vue";
import Vuex from "Vuex";

Vue.use(Vuex);

// 引入login 模块
import login from "./login"

export default new Vuex.Store({
    // 通过modules属性引入login 模块。
    modules: {
        login: login
    }
})
  1. 在main.js中引入store, 并注入到vue 根实例中。
代码语言:javascript
复制
import Vue from 'vue'
import App from './App.vue'

// 引入store
import store from "./store"

new Vue({
  el: '#app',
  store,  // 注入到根实例中。
  render: h => h(App)
})
  1. 在 app.vue 中通过computed属性获取到login下的state. 这里要注意,在没有modules 的情况下,组件中通过this.$store.state.属性名 可以获取到,但是有modules 之后,state 被限制到login 的命名空间(模块)下, 所以属性名前面必须加模块名(命名空间),组件中通过 this.$store.state.模块名.属性名,在这里是 this.$store.state.login.userName
代码语言:javascript
复制
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>
  </div>
</template>

<script>
export default {
  // computed属性,从store 中获取状态state,不要忘记login命名空间。
  computed: {
    useName: function() {
      return this.$store.state.login.useName
    }
  }
}
</script>

组件中成功获取到状态。项目目录和展示如下:

  1. 通过actions, mutations 改变名字, 这就涉及到dispatch action, commit mutations, mutations 改变state. 先在login 文件夹 index.js中添加changeName action 和 CHANGE_NAME mutations.
代码语言:javascript
复制
const mutations = {
    CHANGE_NAME (state, anotherName) {
        state.useName = anotherName;
    }
};

const actions = {
    changeName ({commit},anotherName) {
        commit("CHANGE_NAME", anotherName)
    }
};

在app.vue 中添加一个按钮:change to json, 点击时,dispatch 一个 action. 那在组件中怎么dispatch actions 呢?

在模块中,state 是被限制到模块的命名空间下,需要命名空间才能访问。但actions 和mutations, 其实还有 getters 却没有被限制,在默认情况下,它们是注册到全局命名空间下的,所谓的注册到全局命名空间下,其实就是我们访问它们的方式和原来没有module 的时候是一样的。比如没有module 的时候,this.$store.dispatch(“actions”), 现在有了modules, actions 也写在了module 下面(changeName 写到了login目录下的index.js中),我们仍然可以这么写,this.$store.dispatch(“changeName”), 组件中的getters, 也是通过 this.$store.getters.module中getters 来获取。

代码语言:javascript
复制
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>
    <!-- 添加按钮 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
export default {
  // computed属性,从store 中获取状态state,不要忘记login命名空间。
  computed: {
    useName: function() {
      return this.$store.state.login.useName
    }
  },
  methods: {

  // 和没有modules的时候一样,同样的方式dispatch action
    changeName() {
      this.$store.dispatch("changeName", "Jason")
    }
  }
}
  1. 局部参数 虽然dispatch action和 commit mutations 可以全局使用,但是写在module 中的actions, mutations 和getters, 它们获得的默认参数却不是全局的,都是局部的,被限定在它们所在的模块中的。比如mutations和getters 会获得state 作为第一个默认参数,这个state参数,就是限定在mutations 和getters 所在模块的state对象,login 文件夹下的mutations 和getters 只会获取到当前index.js 中的 state 作为参数 。actions 会获得一个context 对象作为参数,这个context 对象就是当前module 的实例,module 相当于一个小store.

那么怎样才能获取到根store 中的state 和 getters 呢?Vuex 提供了 rootState, rootGetters 作为module 中 getters 中默认参数, actions中context 对象,也会多了两个属性,context.getters, context. rootState, 这些全局的默认参数,都排在局部参数的后面。

我们在store.js中添加 state, getters:

代码语言:javascript
复制
export default new Vuex.Store({
    // 通过modules属性引入login 模块。
    modules: {
        login: login
    },

    // 新增state, getters
    state: {
        job: "web"
    },
    getters: {
        jobTitle (state){
            return state.job + "developer"
        }
    }
})

login 目录下的 index.js

代码语言:javascript
复制
const actions = {
    // actions 中的context参数对象多了 rootState 参数
    changeName ({commit, rootState},anotherName) {
        if(rootState.job =="web") {
            commit("CHANGE_NAME", anotherName)
        }
    }
};

const getters = {
    // getters 获取到 rootState, rootGetters 作为参数。
    // rootState和 rootGetter参数顺序不要写反,一定是state在前,getter在后面,这是vuex的默认参数传递顺序, 可以打印出来看一下。
    localJobTitle (state,getters,rootState,rootGetters) {  
        console.log(rootState);
        console.log(rootGetters);
        return rootGetters.jobTitle + " aka " + rootState.job
    }
};

app.vue 增加h2 展示 loacaJobTitle, 这个同时证明了getters 也是被注册到全局中的。

代码语言:javascript
复制
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>

    <!-- 增加h2 展示 localJobTitle -->
    <h2>{{localJobTitle}}</h2>
    <!-- 添加按钮 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  // computed属性,从store 中获取状态state,不要忘记login命名空间。
  computed: {
    ...mapState({
      useName: state => state.login.useName
    }),

    // mapGeter 直接获得全局注册的getters
    ...mapGetters(["localJobTitle"])
  },
  methods: {
    changeName() {
      this.$store.dispatch("changeName", "Jason")
    }
  }
}
</script>

结果:

  1. 其实actions, mutations, getters, 也可以限定在当前模块的命名空间中。只要给我们的模块加一个namespaced 属性:
代码语言:javascript
复制
const state = {
    useName: "sam"
};
const mutations = {
    CHANGE_NAME (state, anotherName) {
        state.useName = anotherName;
    }
};
const actions = {
    changeName ({commit, rootState},anotherName) {
        if(rootState.job =="web") {
            commit("CHANGE_NAME", anotherName)
        }
    },
    alertName({state}) {
        alert(state.useName)
    }
};
const getters = {
    localJobTitle (state,getters,rootState,rootGetters) {  
        return rootGetters.jobTitle + " aka " + rootState.job
    }
};
// namespaced 属性,限定命名空间
export default {
    namespaced:true,
    state,
    mutations,
    actions,
    getters
}

当所有的actions, mutations, getters 都被限定到模块的命名空间下,我们dispatch actions, commit mutations 都需要用到命名空间。如 dispacth("changeName"), 就要变成 dispatch("login/changeName"); getters.localJobTitle 就要变成 getters["login/localJobTitle"] app.vue 如下:

代码语言:javascript
复制
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1 @click ="alertName">{{useName}}</h1>

    <!-- 增加h2 展示 localJobTitle -->
    <h2>{{localJobTitle}}</h2>
    <!-- 添加按钮 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  // computed属性,从store 中获取状态state,不要忘记login命名空间。
  computed: {
    ...mapState("login",{
      useName: state => state.useName
    }),

     localJobTitle() {
       return this.$store.getters["login/localJobTitle"]
     }
  },
  methods: {
    changeName() {
      this.$store.dispatch("login/changeName", "Jason")
    },
    alertName() {
      this.$store.dispatch("login/alertName")
    }
  }
}
</script>

有了命名空间之后,mapState, mapGetters, mapActions 函数也都有了一个参数,用于限定命名空间,每二个参数对象或数组中的属性,都映射到了当前命名空间中。

代码语言:javascript
复制
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  computed: {
    // 对象中的state 和数组中的localJobTitle 都是和login中的参数一一对应。
    ...mapState("login",{
      useName: state => state.useName
    }),
    ...mapGetters("login", ["localJobTitle"])
  },
  methods: {
    changeName() {
      this.$store.dispatch("login/changeName", "Jason")
    },
    ...mapActions('login', ['alertName'])
  }
}
</script>

参考

  1. https://www.cnblogs.com/SamWeb/p/6527240.html
  2. https://www.cnblogs.com/SamWeb/p/6543931.html
  3. https://www.cnblogs.com/SamWeb/p/6560971.html
  4. https://www.cnblogs.com/SamWeb/p/6590508.html
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 示例
    • state
      • mutation
        • action
          • dispatch action
            • 通过vuex 传递参数
              • getters
                • modules
                  • 参考
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档