我这篇文章也放在我的GitHub上,里面还有相应的代码。建议移步GitHub阅读本文章。
本文代码
前导
结合代码案例学习,本文的知识点会依附在这个代码里讲解,下面先来看一下这个代码的介绍,主要就是一个父组件Index.vue,两个子组件AddStudent.vue和StudentList.vue,之间有组件通讯。
父组件 Index.vue
1 | <template> |
子组件 AddStudent.vue
1 | <template> |
子组件 Student List.vue
1 | <template> |
state
store.js
1 | import Vue from 'vue' |
$store.state.xxx
vuex 里的 state 相当于一个公共数据池。
在 AddStudent.vue 中通过如下方式可以获取到:
使用 $store.state.name 获取到
然后我修改一下add方法使得点击button按钮后修改 $store.state.name ,如下:
1
2
3
4add() {
// this.$emit("add", this.name);
this.$store.state.name += " lin";
}但是
显示的部分并没有成功修改只有
才有被修改,这是因为
storeName
是在data中被定义的,之后$store.state.name
和它是没关系的,影响不到。所以应该用computed
来获取 state 的数据。computed 获取 this.$store.state.name
这样,只要 state 里的数据发生改变,通过 computed 获取到的就能相应其改变。
mapState
背景
像上述那样拿到 state 中的 name ,继续获取到 state 中的其他数据:
如果同时另一个组件也要同样的这些数据,那么就要在 StudentList 组件中重复上述的操作,这样是可行的,但是如果还有七八九十个组件也要这些数据,那么这样的操作很繁琐,冗余很大。
这就需要用到 mapState方法 了。这个方法的返回是一个对象,对象的每个key对应的value都是一个函数,有一点点像下面这个:
用法
先引入才能使用。
1 | import {mapState} from 'vuex' |
然后结合 computed ,将 state 中的数据作为参数传入,
1 | computed: mapState(["name", "age", "look"]), |
而在当前组件中获得的数据名称和 state 中的一样。
但是结果只显示了 age 和 look ,没有显示 name ,因为在 data 中定义了一个 name 属性,而 data 的优先级比 computed 高,所以无法正常获取到。
所以需要我们在使用 mapState 的时候重命名,如下:
1 | computed: mapState({ |
但是除了 mapState ,我们在 computed 中也需要定义其他的计算属性,咋整?
像下面这样写是会报错的!
上面说了 mapState 返回的是一个对象,所以我们可以使用扩展运算符,如下:
1 | computed: { |
应用(一)
原案例中的两个子组件之间的传值是通过父组件来实现的,下面我们利用 vuex 的 state 来存储他们的公共资源。
父组件 Index.vue
1 | <template> |
子组件 AddStudent.vue
1 | <template> |
子组件 StudentList.vue
1 | <template> |
getters
应用(二)
在应用(一)的基础上,要求输出:
索引为0的: **姓名
索引为1和2的:姓名**
索引大于2的:++姓名++
效果图:
不用getters的方案
在 StudentList.vue中的 computed 中进行数据处理
1 | computed: { |
使用getters的方案
现在 store.js 中写 getters 代码:
1 | getters: { |
在 StudentList 中直接使用 newstudentlist:
1 | <template> |
mapGetters
和 mapState 类似的便捷方法
StudentList 中先引入 mapGetters :
1 | import { mapState, mapGetters } from "vuex"; |
也可以使用对象的方式:
1 | import { mapState, mapGetters } from "vuex"; |
补充一点:getters自身可以作为参数传入getters属性的方法中
1 | getters: { |
mutations
关于严格模式
上述应用程序代码写的是不规范的,当我们开启严格模式时就会报警告了。
报错信息提示不要在 mutation 处理程序之外对vuex存储状态state进行更改。而我们恰恰就在 Addstudent.vue 的 add 方法中对 $store.state.studentlist 进行了更改:
【记住:凡是要修改 state 的都要通过 mutation 来修改。 】
应用(三)
先在 store.js 中编写 mutations :
1 | mutations: { |
第二个参数接受传入的外部参数。
在 AddStudent.vue 中:
1 | methods: { |
注意写法,对象是 $store ,使用了 commit。
在 mutation 属性中智能传递两个参数,如果我们想传递多个参数,可以把参数写进一个对象,然后这个对象作为参数写入 mutation 中。
1 | mutations: { |
在 AddStudent.vue 中:
1 | methods: { |
【注意:注意对象参数里面的元素命名形参和实参必须保持一致】
mapMutations
和 mapGetters 一样, mutations 也有便捷操作。
在 AddStudent.vue 中,先引入 mapMutations
然后在 methods 定义:
1 | methods: { |
这样在 AddStudent.vue 中就有了 changeStudent 这一方法,直接使用即可。
1 | <template> |
如果想要重命名,如下操作:
1 | methods: { |
然后在标签里触发这个 newChangeStudent 即可。
actions
异步操作下的情况
我们尝试在 mutations 中模拟一下异步操作,实现点击后过了1秒再执行 mutations 中的动作:
1 | mutations: { |
然后发现控制台报错:
还是之前见过的报错信息,提示不要在 mutation 处理程序之外对vuex存储状态state进行更改。而我们在 mutations 里面的 setTimeout 下对 state 进行了修改,作用域不在 mutations 下,因此报错。
还有一点非常糟糕的是:如果我们这样写了,在点击按钮的时候用极端的时间点击了很多次,当后来我们想要调试的时候,回不到正确的 state 。我们通过 vue Devtool 调试,当我想要看第三次点击时的 state 时,结果左边跳到了初试的 state 状态,因为我的第三次点击时,第一次点击还不到1秒,state 还尚未被修改!如下图所示
于是我们可以使用 vuex 中的异步操作 actions。
mutations 是用来执行修改 state 中数据的操作,actions 也是,但是特别的是,actions 专门用来进行异步的修改操作。
应用(四)
在 store.js 中:
1 | mutations: { |
在 AddStudent.vue 中,通过 dispatch 来触发 actions :
1 | methods: { |
在这个例子中,使用了 actions ,每次点击按钮的时候它都不是立即就执行 changeStudent 方法,而是每次都等候1秒再执行了,这就和用 mutations 内部 setTimeout 不一样,不是楞头地立即执行,导致 state 混乱。
mapActions
同样的,actions 也有便捷操作。mapActions 返回的也是一个对象,里面都是函数。
在 AddStudent.vue 的 methods 中创建 mapActions:
1 | methods: { |
这样 AddStudent.vue 中就有了 changeStudent 这样一个方法,可以像普通方法那样使用:
1 | <template> |
如果要重命名,如下操作:
1 | methods: { |
然后在标签中触发 newChangeStudent 方法即可。
Modules
未完待续