先不说原理,直接说使用吧,用着用着就 明白了
A: 需求:
在项目中,组件之间是独立的,组件之间要想实现通信,父子之间通信可以用props选项,但是兄弟之间的通信就比较复杂
B: 简单理解:
如果在state中定义一个数据之后,我们可以在项目中的任何一个组件里获取和修改,并且我们的修改可以得到全局的相应变更 比如
let state = {
userList: [], // 用户列表
}
这个userList里的数据,可以在全局使用
C: vuex种的五个基本属性
1, 我们可以通过this.$store在vue的组件中获取vuex的实例
2, State:vuex中的数据源,可以通过thsi.$store.state获取在vuex中声明的全局变量的值
3, Getter:相当于vue中的computed(计算属性),可以用于监听/计算state中值的变化
4, Mutation: vuex中提交更改数据的方法(只能同步执行)
5, Action:Action 类似于 mutation
- Action 提交的是 mutation,而不是直接变更状态Action
- Action 可以包含任意异步操作
6, Module: 模块化vuex
1,先开始使用,在项目中新建一个store文件夹,相当于一个仓库,在store文件夹下面新建index.js文件
import Vue from 'vue'
import Vuex from 'vuex' // 引入vuex
Vue.use(Vuex)
const store = new Vuex.Store(); // 实例化
export default store
2, 在main.js里面引入store,然后全局注册一下,这样就可以在任何组件使用this.$store了
import store from './store' //引入store
new Vue({
el: '#app',
router,
store, //使用store
template: '<App/>',
components: { App }
})
3, 回到store文件夹的index.js文件,我们声明一个state变量,赋值给一个对象,里面定义两个初始的属性,然后在实例化的Vuex.Store里面传入一个空对象,把声明的state放进去
说明: vuex使用单一状态树,state是唯一数据源,所以每个应用仅包含一个store实例,
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = { //要设置的全局访问的state对象
showHello: true,
changeNum: 0
//要设置的初始属性值
}
const store = new Vuex.Store({
state
})
export default store
说明: 做完这个之后,我们就可以在任何组件里面使用
this.$store.showHello和this.$store.changeNum来获取定义的值了
mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性
// 在单独构建的版本中辅助函数为 Vuex.mapState
// 在需要的组件里面引入
import { mapState } from 'vuex'
computed: {
...mapState({
showHello:state => state
})
}
4,vuex官方提供了一个getter(可以认为是store的计算属性),像计算属性一样,getter的返回值会根据它的依赖被缓存起来,只有当它的依赖值发生改变才会被重新计算,把他也扔到state里面
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state={ //要设置的全局访问的state对象
showHello: true,
changeNum: 0
//要设置的初始属性值
}
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //方法名随意,主要是来承载变化的showHellor的值
return state.showHello
},
getChangedNum() { //方法名随意,主要是用来承载变化的changeNum的值
return state.changeNum
}
}
const store = new Vuex.Store({
state,
getters
});
export default store;
5, 现在只有定义的state初始值,但是我们需要改变初始值,接下来就是mutation了
mutation也是一个对象,这个对象里面放可以改变state初始值的方法
具体的就是:给里面的方法传入参数或者额外的其他参数,理由vue的双向数据驱动进行值得改变
把定义好的mutation也扔进Vuex.Store里面
更改vuex的store中的状态的唯一方法就是提交mutation
vuex中的mutation非常类似事件,就是每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler),这个回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state={ //要设置的全局访问的state对象
showHellor: true,
changeNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showHellor的值
return state.showHellor
},
getChangedNum(){ //承载变化的changebleNum的值
return state.changeNum
}
};
const mutations = {
//
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showHellor = true;
},
hide(state) { //同上
state.showHellor = false;
},
newNum(state, sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changeNum += sum;
}
};
6, 这个时候就可以用
this.$store.commit('show') 或
this.$store.commit('hide') 以及
this.$store.commit('newNum',6)在别的组件里面进行改变showHello和changeNum的值了
// 提交 mutation 的另一种方式是直接使用包含 type 属性的对象
store.commit({
type: 'increment',
amount: 10
})
store.commit 传入额外的参数,即 mutation 的 载荷(payload)
Mutation 需遵守 Vue 的响应规则
1,最好提前在你的 store 中初始化好所有所需属性
2,当需要在对象上添加新属性时,你应该 使用
Vue.set(obj, 'newProp', 123)
3,或者 以新对象替换老对象。例如,利用对象展开运算符我们可以这样写
state.obj = { ...state.obj, newProp: 123 }
mapMutations 辅助函数将组件中的 methods 映射为 store.commit
import { mapMutations } from 'vuex'
methods: {
...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
7, 但是在vuex中,mutation里的方法都是同步,意思就是,比如这个this.$store.commit('newNum',sum)方法,
两个组件里用执行得到的值,每次都是一样的,显然不是我们需要的
vuex还提供了actions,把action也放入Vuex.Store里面,这个actions也是对象变量,里面的Action方法,可以包含任意异步操作
这里的异步就是用异步发出mutation里面的方法,action里面的自定义函数接受一个context参数和要变化的形参
context和store实例具有相同的属性和方法,可以调用context.commit()提交一个mutation,或者通过context.state和context.getter来偶去state和getter
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要设置的全局访问的state对象
showHello: true,
changeNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showHello的值
return state.showHello
},
getChangedNum(){ //承载变化的changebleNum的值
return state.changeNum
}
};
const mutations = {
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showHello = true;
},
hide(state) { //同上
state.showHello = false;
},
newNum(state,sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changeNum += sum;
}
};
const actions = {
hideHello(context) { //自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
context.commit('hide');
},
showHello(context) { //同上注释
context.commit('show');
},
getNewNum(context, num){ //同上注释,num为要变化的形参
context.commit('newNum', num)
}
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions
});
export default store;
实践中我们可以用es6参数解构来简化代码
actions: {
increment ({ commit }) {
commit('increment')
},
getDep({ commit }) {
const res = 调用方法之类的 // 拿到值
if (!res) return;
commit(TYPES.SET_DEP, res);
return res;
},
}
action 通过 store.dispatch 方法触发, dispatch分发action
在外部组件进行全局执行actions里面的方法的时候,你只需要执行
this.$store.dispatch('hideHello')
或this.$store.dispatch('showHello')
以及this.$store.dispatch('getNewNum', 6) //6要变化的实参
mapActions 辅助函数
import { mapActions } from 'vuex'
methods: {
...mapActions([ 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` // `mapActions` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
5.使用常量替代 Mutation 事件类型
- 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式
- 同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// mutations.js
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
},
// [SOME_MUTATION]和 show一样,只不过上面用的是常量表示的
show(state) {
// mutate state
// state.showHellor = true;
},