Vuex是什么?
Vuex是一个专为Vue.js应用程序开发的状态管理模式
。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
什么是状态管理模式
?
让我们从一个简单的Vue计数器应用开始:
new Vue({
//state
data(){
return{
count:0,
}
},
//view
template:`
<div>{{count}}</div>
`,
//actions
methods:{
increment(){
this.count++
},
},
})
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源
- view,以声明方式将state映射到视图
- actions,相应在view上的用户输入导致的状态变化
以下是一个表示单向数据流
理念的简单示意:
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏: - 多个视图依赖同一状态
- 来自不同视图的行为需要变更同一状态
对于问题1,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题2,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。
以上的这些模式非常脆弱,通常会导致无法维护的代码。
我们可以把组件的共享状态抽离出来,以一个全局单例模式管理,通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且容易维护。
vuex的store插件是什么?
Vuex的store接受plugins选项,这个选项暴露出每次mutation的钩子。Vuex插件就是一个函数,它接收store作为唯一参数:
const myPlugin = store => {
//当store初始化后调用
store.subscribe((mutation,state) = {
//每次mutation之后调用
//mutation的格式为{type,payload}
})
}
const store = new Vuex.Store({
state:{},
getters:{},
mutations:{},
actions:{},
modules:{},
plugins:[myPlugin],
})
Vuex怎么监听组件中提交mutation和action?
- 用Vuex.Store的实例方法
subscribe
监听组件中提交mutation - 用Vuex.Store的实例方法
subscribeAction
监听组件中提交action
function createPlugin(param){
//???为什么这样写啊,直接执行不好吗?
return store => {
store.subscribe((mutation,state) => {
console.log(mutation.type)//是哪个mutation
console.log(mutation.payload)
console.log(state)
})
store.subscribeAction({
//提交action之前
before:(action,state) => {
console.log(`before action ${action.type}`)
},
//提交action之后
after:(action,state){
console.log(`after action ${action.type}`)
}
})
}
}
在插件内提交Mutation
在插件中不允许直接修改状态,类似于组件,只能通过提交mutation来触发变化。
通过提交mutation,插件可以用来同步数据源到store。
生成State快照
有时候插件需要获得状态的快照,比如改变的前后状态。想要实现这项功能,你需要对状态对象进行深拷贝。
内置Logger插件
Vuex自带一个日志插件用于一般的调试
import createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
plugins:[createLogger()],
})
vuex的module是什么?主要使用场景?
由于使用单一状态树,应用的所有状态会集中到一个比较大的状态。当应用变得非常复杂时,store对象有可能变得相当臃肿。
为了解决这个问题,Vuex允许我们将store分割成模块(module),每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块
在模块中,getter和mutation和action中怎么访问全局的state和getter?
- 在getter中,可以通过第3个参数rootState和第4个参数rootGetters
- 在mutation中,不可以访问全局的state和getter
- 在action中,第一个参数context中的context.rootState访问到全局的state,context.rootGetters访问到全局的getter
Vuex的命名空间是什么?
默认情况下,模块内部的action,mutation和getter是注册在全局命名空间的,这样使得多个模块能够对同一mutation或action作出相应。
如果希望你的模块具有更高的封装度和复用性,你可以添加namespace:true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有getter,action及mutation都会自动根据模块注册的路径调整命名。
const store = new Vuex.Store({
modules:{
account:{
namespace:true,
state:() => ({...})//模块内的状态已经是嵌套的了,使用namespace属性不会对其产生影响
getters:{
isAdmin(){...}// -> getters['account/isAdmin']
},
actions:{
login(){},//->dispatch('account/login')
},
mutations:{
login(){},// -> commit('account/login')
},
modules:{
//继承父模块的命名空间
myPage:{
state:()=>({}),
getters:{
profile(){}//->getters['account/profile']
},
},
//进一步嵌套命名空间
posts:{
namespace:true,
state:() => ({}),
getters:{
popular(){}//->getters['account/posts/popular']
},
},
},
},
},
})
启用了命名空间的getter和action会收到局部化的getter,dispatch和commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加控件名前缀。更改namespace属性后不需要修改模块内的代码。
在带命名空间的模块内访问全局内容(Global Assets)
如果你希望使用全局state和getter,rootState和rootGetters会作为第3和第4参数传入getter,也会通过context对象的属性传入action
若需要在全局命名空间内分发action或提交mutation,将{root:true}作为第三参数传给dispatch或commit即可。
modules:{
foo:{
namespace:true,
getters:{
//在这个模块的getter中,getters被局部化了
//你可以使用getter的第四个参数来调用rootGetters
someGetter(state,getters,rootState,rootGetters){
getters.someOtherGetter //-> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter:state => {},
},
actions:{
//在这个模块中,dispatch和commit也被局部化了
//他们可以接受'root'属性以访问根dispatch或commit
someAction({dispatch,commit,getters,rootGetters}){
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction')//->'foo/someOtherAction'
dispatch('someOtherAction',null,{root:true})//-> 'someOtherAction'
commit('someMutation')//->'foo/someMutation'
commit('someMutation',null,{root,true})//->'someMutation'
},
someOtherAction(context,payload){},
},
},
},
在带命名空间的模块注册全局action
若需要在带命名空间的模块注册全局action,你可添加root:true,并将这个action的定义放在函数handler中
{
actions:{
someOtherAction({dispatch}){
dispatch('someAction')
},
},
modules:{
foo:{
namespace:true,
actions:{
someAction:{
root:true,
handler(namespacedContext,payload){}//->'someAction'
},
},
},
},
}
Vuex中actions和mutations有什么区别?
mutation
:可以直接修改state中的状态,只能包含同步操作,通过提交commit
调用,(尽量通过action或mapMutation调用而非直接在组件中通过this.$store.commit()提交)
actions
: 可以包含异步操作,通过store.dispatch()触发
Vuex使用actions时,如何传递参数?
- this.$store.dispatch(‘xxx’,{name:xxx})
- this.$store.dispatch({type:xxx,name:xxx})
Vuex的缺点
由于Vuex中的数据存放在内存中,因此刷新页面时,数据会丢失
不用Vuex的话带来哪些问题?
组件之间传值不方便
Vuex怎么知道state是通过mutation修改还是外部直接修改的?
- Vuex可以设置严格模式,严格模式下,直接修改会报错
- ???通过$watch监听mutation的commit函数中_committing是否为true
请求数据是写在组件的methods中还是写在Vuex的actions中?
如果请求的数据是多个组件共享的,为了方便只写一份,就写在vuex里面。如果是组件独用的,就写在组件里面
怎么监听vuex数据的变化
先用计算属性,然后再监听
页面刷新后vuex的state数据丢失怎么解决?
vuex-presistedstate的createPersistedState()方法
Vuex的优势?
vue是单向数据流,有一个vuex来建一个全局仓库,可以减少很多时候的传参地狱。
Vuex中action通常是异步的,那么如何知道action什么时候结束呢?
在action函数中返回Promise,然后再提交时用then处理
Vuex中有2个action,分别是actionA和actionB,其内都是异步操作,在actionB要提交actionA,需在actionA处理结束再处理其它操作,怎么实现?
利用ES6的async和await来实现
actions:{
async actionA({commit}){
//...
},
async actionB({dispatch}){
await dispatch('actionA')//等待actionA完成
},
}
在v-model上怎么用Vuex中state的值?
<input v-model='message'>
computed:{
message:{
get(){
return this.$store.state.message
},
set(value){
this.$store.commit('updateMessage',value)
},
}
}
在执行dispatch触发action(commit同理)的时候,只需要传入(type,payload),action执行函数中第一个参数store从哪里获取的?
store初始化时,所有配置的action和mutation,getters均被封装过。在执行如dispatch(‘submitOrder’,payload)的时候,action中type为submitOrder的所有处理办法都是被封装后的,其第一个参数为当前的store对象