Vuex 基础教程系列 🎉
Getters
Vuex 的 getters
选项可以看做是 Store 的计算属性 (computed),它可以基于一个或多个状态派生出全新的状态。getter
也具有缓存的特性,只有当它的依赖值发生改变时才会重新计算。
当多个组件共享同一个派生状态时,使用 getter
将是最有效的,无需让每个组件单独定义计算属性来过滤或组合出派生的状态,也无需通过导入模块的方式使用共享函数进行过滤或组合。
首先,让我们通过 getter
来组合一个新的状态。
export default {
state: {
firstName: 'Eich',
lastName: 'Brendan'
},
getters: {
fullName: (state) => state.lastName + state.firstName
}
}
接着,使用 getter
对状态进行过滤,并且通过第二个参数我们还可以访问 getters
选项。
const store = new Vuex.Store({
state: {
firstName: 'Brendan ',
lastName: 'Eich',
names: ['James Gosling', 'Tim Berners-Lee'],
},
getters: {
fullName: state => state.firstName + state.lastName,
namesStartWithT: (state, **getters**) => {
const combines = state.names.concat([getters.fullName]);
return combines.filter(item => item.toLowerCase().startsWith('t'));
}
}
});
getter
还可以返回一个函数,这对 Store 中的状态进行更灵活的过滤非常有用。
const store = new Vuex.Store({
state: {
firstName: 'Brendan ',
lastName: 'Eich',
names: ['James Gosling', 'Tim Berners-Lee'],
},
getters: {
fullName: state => state.firstName + state.lastName,
findNames: (state, getters) => **(name)** **=>** **{**
const combines = state.names.concat([getters.fullName]);
return combines.filter(item => item.indexOf(name) !== -1);
**}**
}
});
//Use
store.getters.findNames('a');
注意的是,若
getter
返回的是一个函数,那么计算的结果并不会被缓存。
在 Vue 组件访问 Getter
- 导入
store
实例或者通过根组件注入的$store
来访问getters
选项。 - 使用
mapGetters
工具方法。
mapGetters 工具方法
使用 mapGetters
方法可以很方便的将 getter
映射为组件的计算属性。
使用数组的取值方式,可以快速的映射与 getter
同名的计算属性
{
computed: Vuex.mapGetters(['fullName', 'names'])
}
如需自定义计算属性的名称,可以使用对象取值格式。
{
computed: Vuex.mapGetters({ name: 'fullName' })
}
最后千万别忘记了可以使用 ES6 展开运算符将映射的计算属性与其它计算属性进行合并。
Actions
actions
是对 mutations
的再次包装,原因在于后者不仅被项目代码所依赖还会被 Vuex 自身的状态变更记录所依赖,因此只能纯粹的进行同步状态变更操作。而 actions
则是专门用来服务业务代码,它同时支持同步与异步的状态变更操作。
action
不能直接变更状态,必须要在其中提交 mutation
的方式来间接变更状态。
下图是一个异步状态变更的具体流程示意图:
上图中的 Backend API 表示的是在 action
中发起接口请求获取后端数据,而 Devtools 则是 vue-devtools 在调试 Vuex 应用时需要同步的从 mutation
中获取状态数据以及变更记录。
下面是实现上图的基本示例:
<div id="app">
<p>{{age}} / {{name}}</p>
<button @click="updateInfo">UpdateInfo</button>
</div>
const store = new Vuex.Store({
state: {
age: 1,
name: 'Sam'
},
mutations: {
updateAge(state) {
state.age = 18;
},
updateName(state) {
state.name = 'Bob';
}
},
actions: {
asyncUpdateAge(context) {
context.commit('updateAge');
},
asyncUpdateName({ commit }) {
commit('updateName');
}
}
});
new Vue({
el: '#app',
store,
computed: Vuex.mapState(['age', 'name']),
methods: {
updateInfo() {
this.$store.dispatch('asyncUpdateAge');
this.$store.dispatch('asyncUpdateName');
}
}
})
actions
的使用形式与 mutations
完全相同, 细微的差异体现于:
action
使用dispatch
方法进行分发,muation
则是使用commit
方法进行提交。action
的事件处理方法 (handler) 接收一个与store
具有相同属性和方法的context
对象,(但两者并非真的是同一个对象),因此你可以通过context.commit
来提交mutation
;通过context.getters
来获取getter
;通过context.state
来获取state
;通过context.dispatch
来分发其它action
。借助 ES6 的解构运算符,还可以简化参数的获取({ commit, dispatch, state, getters })
。
分发与载荷 (Payload)
action
通过 dispatch
方法进行触发,它与 muation
的 commit
方法具有相同形式的载荷。
const store = new Vuex.Store({
state: {
age: 0,
name: 'Sam'
},
mutations: {
updateAge(state, payload) {
state.age = payload;
},
updateName(state, payload) {
state.name = payload.name;
}
},
actions: {
asyncUpdateAge(context, payload) {
context.commit('updateAge', payload);
},
asyncUpdateName({ commit }, payload) {
commit('updateName', payload);
}
}
});
dispatch
方法分发 action
并传递载荷。
this.$store.dispatch('asyncUpdateAge', 20);
this.$store.dispatch('asyncUpdateName', { name: 'Bob' });
//or
this.$store.dispatch({
type:'asyncUpdateName',
name:'Bob'
})
💡 我们更推荐以对象的形式来提交载荷,因为它能传递更多的参数并且可阅读性更强。
Actions + Promise
在 action
中返回一个 Promise
对象这可以让我们监听异步状态变更的完成时机。
const store = new Vuex.Store({
state: {
age: 0,
name: 'Sam'
},
mutations: {
updateAge(state, payload) {
state.age = payload.age;
},
updateName(state, payload) {
state.name = payload.name;
}
},
actions: {
asyncUpdateAge(context, payload) {
return new Promise(resolve => {
setTimeout(() => {
context.commit('updateAge', payload);
resolve();
}, 1000);
})
},
asyncUpdateName({ commit, dispatch }, payload) {
return new Promise(resolve => {
setTimeout(() => {
commit('updateName', payload);
resolve()
}, 1500);
});
},
asyncUpdateInfo({ dispatch }, payload) {
const updateAge = dispatch('asyncUpdateAge', payload);
const updateName = dispatch('asyncUpdateName', payload);
return Promise.all([updateAge, updateName])
}
}
});
通过 dispatch
方法中返回的 Promise
状态来执行对应的回调。
this.$store.dispatch({
type: 'asyncUpdateInfo',
name: 'Bob',
age: 18
}).then(() => {
alert('success!!!')
})
最后,为了避免陷入回调地狱我们还可以使用最新的 async
与 await
语法来替代 .then
回调。
async updateInfo() {
await this.$store.dispatch({...})
}
在 Vue 组件中分发 Action
- 通过导入
store.dispatch
的方式或使用根组件注入的$store
来分发action
。 - 使用
mapActions
工具方法将action
映射为组件实例的方法。
mapActions 工具方法
使用 mapActions
工具方法我们可以很方便的将 Store 的 action
映射为组件的方法。以实现更方便的调用。
使用数组格式可以很快的将 actions
映射为组件实例同名的方法。
{
methods: Vuex.mapActions(['asyncUpdateInfo'])
}
如果需要重新命名方法则可以使用对象格式
{
methods: Vuex.mapActions({
updateInfo : 'asyncUpdateInfo'
})
}