1.定义一个store
// store.ts
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'
import moduleA from '@/store/module/moduleA';
import moduleB from '@/store/module/moduleB';
export interface State {
count: number,
name:string,
arr:number[]
}
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
state() {
return {
count: 0,
name: 'lgp',
arr: [1, 2, 3, 4, 5, 6],
};
},
getters: {
sortArr(state) {
return state.arr.sort((a, b) => b - a);
},
getNum(state, getters) {
const max = Math.max(...state.arr);
const arr1 = getters.sortArr as number[];
return arr1.map((item) => item * max);
},
},
mutation:{
add(state,payload){
state.count+=payload
}
},
actions:{
addAction(ctx,payload){
ctx.commit('add',payload);
}
},
modules: {
produce: moduleA,
car: moduleB,
},
})
// 定义自己的 `useStore` 组合式函数
export function useMyStore () {
return baseUseStore(key)
}
2.安装store
// main.ts
import { createApp } from 'vue'
import { store, key } from './store'
const app = createApp({ ... })
// 传入 injection key
app.use(store, key)
app.mount('#app')
3.使用store
// vue 组件
import { useMyStore } from './store'
export default {
setup () {
const store = useMyStore(key)
store.state.count // 类型为 number
}
}
4核心概念
Strore
1.定义store
store可以是一个对象,或者是一个函数返回一个对象
export interface State {
count: number,
name:string,
arr:number[]
}
export const store = createStore<State>({
state() {
return {
count: 0,
name: 'lgp',
arr: [1, 2, 3, 4, 5, 6],
};
},
2.组件内获取store的state数据
//单个获取数据
const count =computed(()=>{
return store.state.count
])
//批量获取数据mapstate
const storeState = mapState(['count', 'name']);
const result = {};
Object.keys(storeState).forEach((key) => {
result[key] = computed(storeState[key].bind({ $store: store }));
});
const { count, name } = { ...result };
watchEffect(() => {
console.log(count.value, name.value);
});
Getters
1.定义getters
Getter 接受 state 作为其第一个参数:
Getter 也可以接受其他 getter 作为第二个参数:
也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查 询时非常有用。
注意区别:
getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。
本质上getter的结果缓存其实是computed来实现的本身不具备缓存的效果
getters: {
sortArr(state) {
return state.arr.sort((a, b) => b - a);
},
getNum:(state, getters)=>(maxNum:number)=> {
const max =maxNum || Math.max(...state.arr);
const arr1 = getters.sortArr as number[];
return arr1.map((item) => item * max);
},
},
2.组件内使用getters
import { useMyStore } from '@/store';
const store = useMyStore();
const getNum = computed(() => store.getters.getNum(10));
console.log(`getNum${getNum.value}`);
Mutation
1.定义一个mutation
*更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
*接受 state 作为第一个参数:
*你可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload)
*一条重要的原则就是要记住 mutation 必须是同步函数:原因为了让devtools 捕捉状态,方便调试
mutation:{
add(state,payload){
state.count+=payload
}
},
2.组件中使用mutation
store.commit('add',1)
Action
1.定义action
*Action 提交的是 mutation,而不是直接变更状态。
*Action 可以包含任意异步操作。
*接受一个与 store 实例具有相同方法和属性的 context 对象
actions:{
addAction(ctx,payload){
return new Promise((resolve, reject) => {
setTimeout(()=>{
ctx.commit('add',payload);
resolve()
},3000)
})
},
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
},
2使用action
store.dispatch('addAction',1).then(()=>{}).catch(()=>{})
Module
1定义
const moduleA = {
namespaced: true, //true表示开启命名空间
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
**2命名空间 **
namespaced: true 开启命名空间
默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间,但是目前这并非出于功能上的目的(仅仅是维持现状来避免非兼容性变更)。必须注意,不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。
如果你希望使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
例如:
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 (ctx, payload) { ... }
}
3.组件内使用
可以通过打印出store的结构这样怎么获取store等就一目了然了
// store.dispatch(namespace + 'actionName')
//获取命名空间内的store
console.log(store.state.a.name);
console.log(store.state.b.name);
//提交命名空间内的mutation
store.commit('a/add',10)
//获取getters内容
const a=computed(()=>{
return store.getters["a/getNum"]
})
//提交actions
store.dispatch('a/addAction','payload')
store.commit('b/add','payload')
const a=computed(()=>{
return store.getters["b/getX"]
})
store.dispatch('b/addAction','payload')
命名空间内核心函数的参数
Getters
getters接受四个参数 state, getters, rootState, rootGetters
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
Actions
action 接受两个参数 ctx ,payload
ctx包含{ dispatch, commit, getters, rootGetters }
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
actions: {
someOtherAction (ctx, payload) { ... },
// 在这个模块中, 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'
}
}
}
插件功能
Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:
const myPlugin = (store) => {
// 当 store 初始化后调用
store.subscribe((mutation, state) => {
// 每次 mutation 之后调用
// mutation 的格式为 { type, payload }
})
}
const store = createStore({
// ...
plugins: [myPlugin]
})
Vuex 自带一个日志插件用于一般的调试:
import createLogger from 'vuex/dist/logger'
const logger = createLogger({
collapsed: false, // 自动展开记录的 mutation
filter (mutation, stateBefore, stateAfter) {
// 若 mutation 需要被记录,就让它返回 true 即可
// 顺便,`mutation` 是个 { type, payload } 对象
return mutation.type !== "aBlocklistedMutation"
},
actionFilter (action, state) {
// 和 `filter` 一样,但是是针对 action 的
// `action` 的格式是 `{ type, payload }`
return action.type !== "aBlocklistedAction"
},
transformer (state) {
// 在开始记录之前转换状态
// 例如,只返回指定的子树
return state.subTree
},
mutationTransformer (mutation) {
// mutation 按照 { type, payload } 格式记录
// 我们可以按任意方式格式化
return mutation.type
},
actionTransformer (action) {
// 和 `mutationTransformer` 一样,但是是针对 action 的
return action.type
},
logActions: true, // 记录 action 日志
logMutations: true, // 记录 mutation 日志
logger: console, // 自定义 console 实现,默认为 `console`
})
const store = new Vuex.Store({
plugins: [logger]
})
开启严格模式
*在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到
*严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
const store = createStore({
// ...
strict: process.env.NODE_ENV !== 'production'
})