vuex数据管理详细总结以及项目示例

哈喽 大家好啊 最近遇到一个问题,关于多个子表单进行多选,然后请求发给后端,然后请教了下同事方案,可以用vuex,由于自己vuex用得少,也不是很熟,今天就从头学习下(申明:以下都是通过vuex官网学习总结的,所以很多都有相同的,只供学习,不用做商业用途。)

1.首先官网:Vuex 是什么? | Vuex (vuejs.org)

1.vuex是一个大型仓库

2.Vuex得状态存储是响应式得。当vuex组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会发生更新

3.不能直接更改store中的状态,改变store中的状态就是commit(提交) mutation

import {createApp} from 'vue'
import { createStore}  from 'vuex'

// 创建一个新的store实例
const store = createStore({
    state()  {
        return {
            count: 0
        }

    },
    mutations: {
        increment(state) {
            state.count++
        }
    }

})

 const app = createApp({/*根组件*/})
app.use(store)

然后通过store.state来获取状态对象,通过store.commit方法触发状态变更

store.commit('increment')
console.log(store.state.count)

在vuex组件中,可以通过this.$store访问store实例

在组件component.vue中

methods: {
    increment() {
        this.$store.commit('increment');
        console.log(this.$store.state.count)
    }
}

因为store中的状态是响应式的,所以调用store的状态只需要在计算属性中返回就可以了

触发变化也只需要在methods中提交mutation

1.State

import {mapState}  form 'vuex';
 export default {
    computed: mapState({
        // 箭头函数可以使代码更简洁
        const: state => state.count,
       
        // 传字符参数'count',等同于state=> state.count
        countAlias: 'count',
        为了能够使用this获取局部状态,必须使用常规函数
        countPlusLocalState(state) {
            return state.count + this.localCount
        }
    })
}

对象展开运算符

mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:

computed: {
   localComputed() {....}
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({

    })
}

Getter:

const store = createStore ({
    state: {
        todos: [
            {id: 1,text: '...',done: true},
            {id: 2,text: '...',done: false}
        ]    
    },
    // getters是对state里面的数据进行直接计算能得到的
    getters: {
    
        doneTodos(state) {
        
            return state.todos.fillter(todo => todo.done)
        },
        doneTodosCount(state,getters) {
        
          return getters.doneTodos.length
        },
        // 可以通过getter返回一个函数,来实现给getter传参,在你对store里面的数组进行查询
        getTodoById:(state) => (id) => {
            return state.todos.find(todo => todo.id === id)
        }
    }
})

我们在其他组件中使用:
computed: {
    doneTodosCount() {
        return this.$store.getters.doneTodosCount
    }
// 注意: getter在通过属性访问时是作为Vue的响应式系统的一部分缓存其中的
}

mapGetters辅助函数

// mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性:
 
import {mapGetters} from 'vuex'

export default {
    computed: {
        // 使用对象展开运算符将getter混入computed对象中
        ...mapGetters([
          'doneTodoCount',
          'anotherGetter',
            // 如果想要将getter属性取其他名字,使用对象形式
            doneCount: 'doneTodoCount'
        ])
    }

}

Mutation

// 更改vuex中的store的状态就是提交mutation,每个mutation都有一个字符串的事件类型type和一个回调函数handler
 
const store = createStore({
    state: {
      count: 1
    },
    mutations: {
        increment(state,payload) {
        // 变更状态
        state.count += payload.amount
        }
    }
})

组件,提交mutation的另一种方式是直接使用包含type属性的对象
store.commit('increment', {
    amount: 10
})

store.commit({
    type: 'increment',
    amount: 10
})

使用常量替代Mutation事件类型

// mutation-types.js
 export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
 import {createStore} from 'vuex'
 import {SOME_MUTATION} from './mutation-types'

 const store = createStore({
    state:{...},
    // 我们可以用ES6风格计算属性命名功能
    // 使用一个常量作为一个函数名
    [SOME_MUTATION](state) {
        // 修改state
    }
})

// Mutation必须是同步函数
mutations: {
    someMutation(state) {
        api.callAsyncMethod(() => {
            state.count++
        })
    }
}

现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。---这段其实我现在看不太懂,等我后续研究了继续看。

在组件中提交Mutation

你可以在组件中使用this.$store.commit('xxx')提交mutation,或者使用mapMutations辅助函数

将组件中的methods映射为store.commit调用

import {mapMutations} from 'vuex'
export default {
    methods: {
        ...mapMutations([
               'increment',// 将this.increment()映射为this.$store.commit('increment')
                // mapMutation 也支持载荷
                'incrementBy'//将this.incrementBy(amount)映射为this.$store.commit('incrementBy',amount)

        ])
        ...mapMutations({
            add: 'increment';// 将this.add()映射为this.$store.commit('increment')
        })
    }
}

Action

在mutation中混合异步调用会导致程序很难调试,如果在mutation中包含有2个异步回调函数,但是怎么能知道哪个回调函数先调用呢,在mutation中都是同步事务

Action类似于mutation 不同点:

1.Action提交的是mutation,而不是直接变更状态

2.Action可以包含任意异步操作

const store = createStore({
    state: {
        count: 0
    },
    mutations: {
        increment(state) {
            state.count++
        }
    },
    actions: {
        increment(context) {
            context.commit('increment')
        }
        // 用es6
        increment({ commit }) {
            commit('incremnent')
        }
    }
})


// 分发Action

// Action通过store.dispatch方法触发
store.dispatch方法触发
store.dispatch('increment')

action: {
    incrementAsync({commit}) {
        setTimeout(() => {
            commit('increment')
        },1000)
    }
}

//Action支持同样的载荷方式和对象方式进行分发

// 以载荷形式分发
store.dispatch('incrementAsync', {
    amount: 10
})

// 以对象形式分发
store.dispatch({
    type: 'incrementAsync',
    amount: 10
})

以实际购物车为例子,调用异步API和分发多重mutation

actions: {
    checkout({commit, state},products) {
        // 把当前购物车的物品备份起来
           const savedCartItems = [...state.cart.added]
        // 发出结账请求
        // 然后乐观地清空购物车
        commit(types.CHECKOUT_REQUEST)
        // 购物API接受一个成功回调和一个失败回调
        shop.buyProducts(
            products,
            // 成功操作
            () => commit(types.CHECKOUT_SUCCESS) 
            // 失败操作
            () => commit(types.CHECKOUT_FAILURE,savedCartItems)   
        )
      }
}


// 在组件中分发Action

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

import {mapActions} from 'vuex'

export default {
    methods: {
        ...mapActions([
            'increment',// 将this.increment()映射为this.$store.dispatch('inrement')
            'incrementBy'//  将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
        ])

        ...mapActions({
              add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
          })
    }
}

组合Actions

actions: {
  actionA({commit}) {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                commit('someMutation')
                resolve()
            },1000)
        })
    }
}

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}


// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

Module:

原文:Module | Vuex (vuejs.org)

为了解决项目中有太多的状态,写在一起非常臃肿

所以vuex提供了模块module

const moduleA = {
    state: () => ({...}),
    mutations: {...},
    actions: {...},
    getters: {...}
}

const moduleB = {
    state: () => ({...}),
    mutations: {...},
    actions: {...},
    getters: {...}
}

const store = createStore({
    modules: {
        a: moduleA,
        b: moduleB
    }
})

store.state.a  // moduleA的状态
store.state.b  // moduleB的状态

模块的局部状态

对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态对象

const moduleA = {
    state:() => ({
        count: 0
    }),
    mutations: {
        increment(state) {
        // 这里的state对象是模块的局部状态
        state.count++
        }
    },
    getters: {
        doubleCount(state) {
            return state.count*2
        },
        sumWithRootCount(state,getters,rootState) {
            return state.count = rootState.count
        }
    },
    actions: {
        incrementIfOddOnRootSum({state,commit,rootState}) {
            if((state.count + rootState.count) %2 === 1) {
                commit('increment')
            }
        }
    }
}

你可能感兴趣的:(前端,javascript,开发语言)