vuex实现组件全局通信,不用像以前那样子组件要不断找父组件,找爷爷,找祖先..... 通过vuex可实现数据跨组件共享,防止数据意外修改,调试方便 摘自官方的图片
State:数据 Mutations:操作 Actions:调用 Actions->Mutaions->State
我们不能直接对state进行操作,必须定义对应的Mutaions才能对State进行操作 而Devtools则是监控Mutations,比如谁调用了Multaion传递了什么参数等 但是组件又不能直接调用Mutations,这里就要通过Actions去调用Mutaions。 当State发生改变时,会通过组件调用Actions,Actions在调用Mutations,通过Mutations修改State。
需要注意的是Mutations是同步操作,Actions可以是异步操作,也就是当要进行ajax、读写文件等异步操作时应该在Actions执行,当异步操作完毕后在将结果同步的方式给Mutations
1.下载引入
cnpm i vuex -D
//vuex
import Vuex from 'vuex';
2.添加到vue对象
//添加到vue
Vue.use(Vuex);
3.声明store对象
//声明store对象
const store = new Vuex.Store({
// strict:true,//严格模式,防止直接修改state(开发阶段)
strict:process.env.NODE_ENV!='production',
state:{a:12,b:4,c:45},
mutations:{},//mutaion
actions:{},//调用mutaions
getters:{},
modules:{}//模块
})
4.挂载到vm对象
new Vue({
el: '#app',
router,
store,//挂到vm
components: { App },
template: '<App/>'
})
接下来在任意地方访问 {{$store.state}}
输出如下
{ "a": 12, "b": 4, "c": 45 }
当我们尝试对state直接修改时
fn(){
this.$store.state.a+=4
}
抛出如下异常
Error: [vuex] do not mutate vuex store state outside mutation handlers."
我们修改数据应该是 Actions->Mutations->State 我们在store对象添加mutaions和actions
//声明store对象
const store = new Vuex.Store({
// strict:true,//严格模式,防止直接修改state(开发阶段)
strict:process.env.NODE_ENV!='production',
state:{a:12,b:4,c:45},
mutations:{
add(state,n){
state.a+=n
}
},//mutaion
actions:{
add(context,n){
// context当前store对象
context.commit('add',n)//调用mutaions
}
}
getters:{},
modules:{}//模块
})
调用
this.$store.commit('add',5)//直接调用mutaions
//调用actions->muataions
this.$store.dispatch('add',5)//一个action可以有多个mutation
在涉及到state里面的数据计算时我们可以使用getters,相当于computed
...
actions:{
add(context,n){
// context当前store对象
context.commit('add',n)
}
},//调用mutaions
getters:{
count(state){
return state.a+state.b
}
},//类似于computed
modules:{}//模块
当访问时,我们需要{{$store.getters.count}}
才能获取到count,但是这样太麻烦了,还不如直接用state 因此我们可以利用计算属性
computed:{
//可读可写
count(){
return this.$store.getters.count
}
}
这样我们就可以直接使用count了,到这好像要搞复杂了,多搞了层computed,但是这样这样做有好处,computed可以读取和设置,设置时我们可以直接调用actions进行操作,而不用绑定method在调用actions
count:{
get(){
return this.$store.getters.count
},
set(value){
this.$store.dispatch.....
}
}
但是这样也有点麻烦,如果getters很多,我们就要不断进行get,set,而且每个页面都要如此
如何解决这个问题? 通过vuex里面的辅助方法
mapState:将State映射成computed mapActions:将Actions映射成methods mapGetters:将Getters映射computed
这样问题不就简单了吗,我们直接调用方法即可触发actions,并且直接放在state的值 示例如下
import {mapState,mapActions,mapGetters} from 'vuex';
methods:{
del(id){
this.datas = this.datas.filter(data=>data.id!=id)
},
...mapActions(['setA','setB'])
},
computed:{
...mapState(['a','b','c','count']),
...mapGetters(['count'])
}
在store里面我们对state,mutations,actions进行了定义
new Vuex.Store({
// strict:true,//严格模式,防止直接修改state(开发阶段)
strict:process.env.NODE_ENV!='production',
state:{a:12,b:4,c:45},
mutations:{
setA(state,n){
state.a+=n
},
setB(state,n){
state.b+=n
}
},//mutaion
actions:{
setA({commit},n){
commit('setA',n)
},
setB({commit},n){
commit('setB',n)
}
},//调用mutaions
getters:{
count(state){
//不能修改
return state.a+state.b
}
},//类似于computed
modules:{}//模块
我们直接调用对应属性,方法即可
<div class="hello">
a:{{a}}
b:{{b}}
count:{{count}}
<input type="button" value="a+5" @click='setA(5)'>
<input type="button" value="b+5" @click='setB(5)'>
</div>
mutations:{
setUsers(state,users){
state.users = users
}
},
actions:{
async readUsers({commit}){
let res = await fetch('http://127.0.0.1:3000/users.txt');
let users = await res.json();
commit('setUsers',users)
}
}
//调用actions
...
index.vue
created(){
this.readUsers();
},
methods:{
del(id){
this.datas = this.datas.filter(data=>data.id!=id)
},
...mapActions(['setA','setB','readUsers'])
},
....
modules帮我们拆分store有时候store较大时我们需要将store拆分成多个子store 如下例
import Vue from 'vue';
import Vuex, { Store } from 'vuex';
//添加到vue
Vue.use(Vuex);
//子store
let store_a = {
state:{
str:'store_a'
}
}
let store_b = {
state:{
str:'store_b'
}
}
//声明store对象
let store = new Vuex.Store({
// strict:true,//严格模式,防止直接修改state(开发阶段)
strict:process.env.NODE_ENV!='production',
state:{a:12,b:4,c:45,users:null},
mutations:{
setA(state,n){
state.a+=n
},
setB(state,n){
state.b+=n
},
setUsers(state,users){
state.users = users
}
},//mutaion
actions:{
setA({commit},n){
commit('setA',n)
},
setB({commit},n){
commit('setB',n)
},
async readUsers({commit}){
let res = await fetch('http://127.0.0.1:3000/users.txt');
let users = await res.json();
commit('setUsers',users)
}
},//调用mutaions
getters:{
count(state){
//不能修改
return state.a+state.b
},
async onlineUsers(state,getters,context){
//当数据发生变化时执行
if(!state.users){
await store.dispatch('readUsers');
}
//getters写出异步不能直接配合computed computed不能异步
return state.users.filter(user=>user.online)
}
},//类似于computed
modules:{
store_a,
store_b
}//模块
})
export default store;
我们添加了两个子模块,这两个子模块也可以有父store的成员如state,mutaions等、 当访问子模块时
a_str:{{$store.state.store_a.str}}<br />
b_str:{{$store.state.store_b.str}}
有点麻烦,我们可以通过computed解决
...
computed:{
...mapState(['a','b','c','count','users']),
...mapState({
str_a:state=>state.store_a.str,
str_b:state=>state.store_b.str,
}),
...mapGetters(['count'])
}
...
直接输出
{{str_a}}
{{str_b}}
actions和mutation没有自己独立的空间
也就是子store的actions/mutaions名字重复时会被同时触发
我们也可以在子模块添加namespaced:true
时action和mutations有自己的作用域
需要注意的是加了这个属性后我们要使用命名空间来调用
...mapActions(['a/update'])//调用a模块的update
有时候业务逻辑需要我们可能要按需加载某个模块,这个时候可以使用vuex提供的动态加载模块功能
//index.js
store.registerModule('c',{
state:{
text::3
}
})
//解绑
store.unregisterModule('c')
监听 第一个参数为一个方法,返回要监听的值 第二个参数为要监听的值发生变化时执行的逻辑
store.watch((state)=>state.count,(newCount)=>{
console.log('new count watched'.newCount)
})
订阅 拿到所有mutations的变化,每次调用mutation执行相关逻辑
store.subscribe((mutation,state)=>{
console.log(mutation.type)//调用了哪个mutation
console.log(mutation.payload)//mutation接收的参数
})
监听action
store.subscribeAction((action,state)=>{
console.log(action.type)//调用了哪个action
console.log(action.payload)//接收的参数
})
...
const store = new Vuex.Store({
state:defalutState,
mutations,
getters,
plugins:[
//每一个方法都是一个插件接收store对象
(store)=>{
}
]
})
...