Open FrankKai opened 5 years ago
我们都知道,这是我们从vuex查询数据的两种方式,那这两者到底有什么区别呢? 直接看二者的定义:
// https://vuex.vuejs.org/api/#mapstate
mapState(namespace?: string, map: Array<string> | Object<string | function>): Object
// https://vuex.vuejs.org/api/#mapgetters
mapGetters(namespace?: string, map: Array<string> | Object<string>): Object
如果看定义看不懂的话,可以移步TypeScript入门,或者到TypeScript官网自己学习,了解TypeScript中的Optional Property和strictNullChecks。 二者的第一个参数都是可选的命名空间,第二个是数组或者对象的map。
mapState
一方面获取全局状态,一方面辅助我们生成计算属性。mapGetters
仅仅是获取全局状态,通过将store中的getter映射过来。mapState:
mapState(['bar','baz']) // 相当于state.bar
mapState({
foo: 'foo',
bar: state => state.bar,
baz(state) {
return state.baz + 'hhh'; // 用做计算
}
})
mapState('foo',['bar','baz']) // 这种形式不常用
mapGetters:
mapGetters(['bar', 'baz']) // 直接映射
mapGetters({
barBuddy: 'bar', // 重命名映射
bazBuddy: 'baz',
})
其实mapState和mapGetters的真正区别在于:
暴露每个mutation的hooks,只接收一个store(初始化好的store)为参数。
最简单的开发和注入:
const myPlugin = store => {
store.subscribe((mutation, state) => { })
}
const store = new Vuex.Store({
plugins: [myPlugin]
})
又见subscribe,这仍然是基于事件的"发布订阅者"编程模型,关于发布订阅者模型,可以参考webhook到底是个啥?
Plugins不能直接修改状态--这和我们的组件一样,可以通过commit mutation触发更改。 通过commit mutation,插件可以同步数据到store。例如sync一个websocket数据源到store,这个contrived example里, 函数里可以执行很多更加复杂的任务:
export default function createWebSocketPlugin (socket) {
return store => {
socket.on('data', data => {
store.commit('receiveData', data)
})
store.subscribe(mutation => {
if (mutation.type === 'UPDATE_DATA') {
socket.emit('update', mutation.payload)
}
})
}
}
const plugin = createWebSocketPlugin(socket)
const store = new Vuex.Store({
state,
mutations,
plugins: [plugin]
})
state的"snapshots"指的是,对mutation的状态做保留,例如当前的post-mutation state和pre-mutation state做对比。可以通过深拷贝实现:
const myPluginWithSnapshot = store => {
let prevState = _.cloneDeep(store.state)
store.subscribe((mutation, state) => {
let nextState = _.cloneDeep(state)
// compare `prevState` and `nextState`...
// save state for next mutation
prevState = nextState
})
}
snapshot适合在development阶段使用。当结合webpack或者Browserify,我们可以让构建工具处理:
const store = new Vuex.Store({
plugins: process.env.NODE_ENV !=='production' ? [myPluginWithSnapshot] : []
})
此处的环境变量的获取,需要用到webpack的DefinePlugin 这个webpack内置插件。关于DefinePlugin,可以参考Webpack Plugin那些事儿
用于在控制台输出store的mutation信息,其实也是一种state snapshot。 信息内容包括:
相比vue-devtools,有以下优势:
/store/logger.js
import createLogger from 'vuex/dist/logger';
const subscribedMutations = ['USER_INIT', 'DEVICE_EXPEND_STATUS'];
const logger = createLogger({
collapsed: false,
filter: (mutation) => subscribedMutations.includes(mutation.type),
transformer: (state) => state,
mutationTransformer: (mutation) => mutation,
logger: console,
});
export default logger;
/store/index.js
import logger from './logger';
export default new Vuex.Store({
plugins: [logger],
})
日志输出就像下面这样:
注意:logger plugin仅仅用于development环境,因为它也本身属于state snapshot。
当然是mapMutation,可以统一查看当前组件有多少mutation类型,但是需要注意命名不能和组件方法冲突。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 不带payload映射,map `this.increment()` to `this.$store.commit('increment')`
'incrementBy' // 带payload映射,map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 重命名: map `this.add()` to `this.$store.commit('increment')`
})
}
}
直接使用this.$store.commit():
methods: {
foo() {
this.$store.commit('USER_INIT');
}
...
...
bar() {
this.$store.commit('USER_UPDATE');
}
}
当业务逐渐变复杂时,foo和bar之间会相隔很远,很难一目了然看清当前组件用到了哪些mutation。 使用mapMutatinos:
methods: {
...mapMutations([
'USER_INIT',
'USER_UPDATE',
]),
foo() {
this['USER_INIT']();
}
...
bar() {
this['USER_UPDATE']();
}
}
很明显,这样很清晰。
当然,加个判断就好了。
modules: {
account: {
namespaced: true,
// module assets
state: { ... }, // module state is already nested and not affected by namespace option
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
},
}
获取全局的state,getter,mutation,action。getters可以通过rootState, rootGetters获得;mutation和action可以通过dispatch,commit的{root: true}
去获取全局的mutation和action。
modules: {
foo: {
namespaced: true,
getters: {
// `getters` is localized to this module's getters
// you can use rootGetters via 4th argument of getters
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// dispatch and commit are also localized for this module
// they will accept `root` option for the 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 (ctx, payload) { ... }
}
}
}
{root: true}
注册global action。(这种骚操作应用场景少吧。)import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// look up in `some/nested/module`
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// look up in `some/nested/module`
...mapActions([
'foo',
'bar'
])
}
}
一个组件可能需要多个module的state,mutation和action,可以使用多个mapState么?亲测可以。
...mapState({
user: 'user',
}),
...mapState({
Foo: 'device',
}),
上述内容完全可以满足需求,其他内容暂时不用涉猎。
组件内的方法 commit (1.判断是否当前类型的mutation2.若有触发mutation)
使用了Vue实例作为全局状态树_vm($$state) deep watch所有属性
用vuex也有一段时间了,但仅仅是很基础的用,没有学习高级特性,更没有深入过源码 由于手上负责的聊天项目较为复杂,深入学习vuex更显得很有必要,从而优化聊天部分的前端实现
基于以上目的,我将在这里记录自己在实践过程中的一些总结。