Vuex简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
安装
npm install vuex
使用
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
//main.js
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
})
五大核心概念
state
用来存放共享数据的
const store = new Vuex.Store({
state: {
count: 10,
like:["篮球","足球"],
movie:{"name":"速度与激情",actor:"保罗沃克"}
}
})
组件中使用
需要在computed写(让数据具有响应式)
使用数据:this.$store.state.like(获取like数组)
getters
类似于计算属性 ,默认接受第一个参数,该参数是所有的state,其他的用法跟computed是一模一样的
第二个参数是所有的getters
1.基本用法
index.js
const store = new Vuex.Store({
state: {
count: 10
},
getters:{
money(state){
return state.count+"元"
}
}
})
组件中
mounted(){
console.log(this.$store.getters.money) //10元
},
2.传参
返回函数,使用该函数时可以传参数
getters:{
meiyuan:state=>(rate)=>{
return state.rmb/rate
}
},
3.第二个参数
第二个参数存放所有getters,打点可以调用
getters:{
money(state,getters){
return getters.info+"元"
},
info(state){
return state.count*2
}
}
mutations
1.基本用法
1.修改state数据的唯一办法就是提交mutation
2.mutations中修改的数据如果是引用类型,一定注意要完整的赋值(不能单独修改某一项)
mutation要通过commit提交
调用const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
export default store
//调用
this.$store.commit('increment')
2.载荷(payload)
可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload):
mutations: {
increment (state, n) {
state.count += n
}
}
//调用
store.commit('increment', 10)
大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
//调用
store.comit(“increment”,{amount:10})
3.对象风格的提交方式
type是store的mutations对应名字方法,固定的
mutations: {
increment (state, payload) { //此时payload是包含着type的
state.count += payload.amount
}
}
//调用
store.commit({
type: 'increment',
amount: 10
})
4.Mutation 必须是同步函数
为何mutation不能包含异步操作?
使用层面:代码更高效易维护, 逻辑清晰(规范,而不是逻辑的不允许);
具体原因:为了让devtools 工具能够追踪数据变化;
具体原因详解
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来(每次状
态的改变都会生产一个全新的 state 对象),然后就可以实现 “time-travel” 了。如果mutation支持异
步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
注: vue-devtools 的时间旅行 - time travel
Vuex 借鉴 Flux 单向数据流思想,采用集中式统一管理保存状态。但是这些状态不会随时间改变而变
化。为了使状态,可以被捕获、重播或重现。vue-devtools工具,可以帮助我们的 Vue 应用可以实现这
种时间旅行!
每次状态的改变都会生产一个全新的 state 对象,当你想展现什么时间段的状态,只需要切换到那个时
间段的 state 对象,所以vuex原则上只能通过mutation 并且非异步更改状态,否则无法实现state修改
的记录保留或无法正确记录。
actions
1.使用
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
传入的是context(不是state,store对象)
例子:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,这个属性中包括如下:
context:{
state, 等同于store.$state,若在模块中则为局部状态
rootState, 等同于store.$state,只存在模块中
commit, 等同于store.$commit
dispatch, 等同于store.$dispatch
getters 等同于store.$getters
}
常规写法调用的时候会使用context.commit,但更多的是使用es6的变量解构赋值,也就是直接在参数的位置写自己想要的属性,如:{commit}。
2.组件中调用
分发action(dispatch):
store.dispatch('increment')
//带参数的形式
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
modules
1.基本用法
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter
单独定义一个模块跟我们之前定义store是一样的,导出对象就可以了
moduleA.js
export default{
state:{
text:"我是moduleA的数据"
},
getters:{},
mutations:{},
actions:{}
}
moduleB.js
export default{
state:{
text:"我是moduleB的数据"
},
getters:{},
mutations:{},
actions:{}
}
store-index.js
import Vue from "vue";
import Vuex from "vuex";
import moduleA from "./moduleA"
import moduleB from "./moduleB"
Vue.use(Vuex);
const store=new Vuex.Store({
state:{
},
modules:{
// 模块名:模块对象
moduleA,moduleB
}
})
export default store
读取数据语法:
this.$store.state.模块名.数据名
//例如
this.$store.state.moduleB.text
2.多层嵌套
每一个子模块依旧可以有自己的子模块,比如moduleA里添加一个moduleC
moduleC.js
export default{
state:{
text:"我是moduleC的数据"
},
getters:{},
mutations:{},
actions:{}
}
moduleA.js
import moduleC from "./moduleC"
export default{
state:{
text:"我是moduleA的数据"
},
getters:{},
mutations:{},
actions:{},
modules:{
moduleC
}
}
访问:
this.$store.state.模块名.子模块名.数据名
//例如
this.$store.state.moduleA.moduleC.text
3.命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的,所以直接提交mutation不需要加模块名字,A和B的mutation都能触发
getters也是同理,多个模块都有同一个getter(一样的名字,不建议,一般也不允许这样写),默认读取的是根store的getters(只能用一个),如果根store中没有,则按照模块注册顺序读取
例子
moduleA.js
export default{
state:{
text:"我是moduleA的数据"
},
mutations:{
changeText(state){
state.text="moduleA的数据改了"
}
}
}
moduleB.js
export default{
state:{
text:"我是moduleB的数据"
},
mutations:{
changeText(state){
state.text="moduleB的数据改了"
}
}
}
MyHome.vue
computed:{
textA(){
console.log(this.$store.state)
return this.$store.state.moduleA.text
},
textB(){
console.log(this.$store.state)
return this.$store.state.moduleB.text
}
},
methods:{
//我们会发现我们直接提交mutation不需要加模块名字,A和B的mutation都能触发
changeText(){
this.$store.commit("changeText")
}
}
开启命名空间
可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。
当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名
例子
moduleA.js
import moduleC from "./moduleC"
export default{
namespaced: true, //开启命名空间
state:{
text:"我是moduleA的数据"
},
getters:{
info(){
return "哈哈"
}
},
mutations:{
changeText(state){
state.text="moduleA的数据改了"
}
},
modules:{
moduleC
}
}
此时修改数据需要加上模块名
this.$store.commit("moduleA/changeText")//代表提交moduleA里的changeText
//对于嵌套模块
this.$store.commit("moduleA/moduleC/changeText")//提交moduleA里的moduleC里的
changeText
//对于getters
this.$store.getters["moduleA/info"]
//对于actions
this.$store.dispatch("moduleA/actionA")
4.访问全局内容
在开启了命名空间的模块内部,也是可以访问到全局数据的
getters中可以拿到全局的state和getters
rootState拿到所有模块的数据,rootGetters拿到所有模块的
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
actions中可以拿到所有的state和getters
context可以拿到所有模块的
actions:{
actionA(context){
console.log(context)
}
},
在模块内部提交全局(根store)的action或者mutation
将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
辅助函数
mapState函数返回值是一个对象
其他的:mapGetters;mapMutations;mapActions
使用辅助函数之后,组件可以直接在模板用(方法也是),不用再写this.store这种引入
使用:
import { mapState } from 'vuex'
//使用扩展运算符将辅助函数和其他计算属性混合到一起,组成一个对象
//mapState
computed:{
...mapState(["money","a","b","c"]),
sex(){
}
},
带命名空间的辅助函数
...mapMutations("moduleA",["changeText"])
...mapState("moduleA/moduleC",["text"]),
还可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数(一般不用)。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
import { createNamespacedHelpers } from 'vuex'
const { mapState } = createNamespacedHelpers('moduleA/moduleC')
computed:{
...mapState(["text"]),//这样text默认就是在moduleA/moduleC下的
}