• ④ vuex


    Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。

    1 状态管理模式

    1.1 单向数据流

    1.2 多组件共享时【多组件间的数据通讯:切换-生命周期的销毁】

    1. 多个视图依赖于一个状态

    2. 来自不同视图的行为需要变更同一个状态

    1.3 通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性

    2 store仓库

    每个 vuex 应用的核心就是 storestore 基本上就是一个容器,它包含着应用中大部分的状态(state)

    2.1 vuex 和单纯的全局对象有以下两点不同:

    1. vuex 的状态存储是响应式的。

    2. 不能直接改变 store 中的状态。

    2.2 实例化一个 store -- 一个应用只运行有一个 store

    1. 引入 vuex
    import Vue from 'vue';
    import Vuex from 'vuex';
    
    1. 使用(安装)vuex
    Vue.use(Vuex);
    
    1. 实例化 store
    const store = new Vuex.Store({
        state: {
            goodsList: [{
                id: '',
                name: '',
                price: '',
                qty: ''
            }]   
        },
        getters: {
            totalPrice(state) {
                return state.goodsList.reduce((prev, item)=> prev+item.price*item.qty, 0)
            }
        },
        mutations: {
            removeFromCart(state, id) {
                state.goodsList=state.goodsList.filter(item=>item.id!=id)
            },
            clearCart(state) {
                state.goodsList=[]
            },
            addCart(state, goods) {
                state.goodsList.unshift(goods)
            },
            changeQty(state, payload) {
                state.goodsList.forEach(item=>{
                    if(item.id == payload.id) {
                        item.qty = payload.qty
                    }
                })
            }
        },
        actions: {
            async changeQtyAsync(context, {id, qty}) {
                let {data} = await
                if(qty > data) qty = data
                context.commit('changeQty', {id, qty})
            }
        }
    })
    
    1. 把 store 注入到 vue 实例
    new Vue({
      el: '#app',
      router,
      store
    });
    
    1. 在组件中使用数据 this.$store

    3 Store 存储空间--仓库

    3.1 store

    1. state -- 真正存放数据的位置 -- 类似于组件中的 data

    2. getter -- 类似于组件中的 computed

    3. mutations -- 修改 state 的方法 -- 类似于组件中的 methods

    4. actions -- 异步修改 state 的方法 -- 不能直接修改

    3.2 state

    通过在根实例注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能够通过 this.$store 访问到。

    1. 操作 state
    • 获取 state:this.$store.state.xxx

    • 修改 state:this.$store.commit(mutation)

    2. mapState 辅助函数【映射到 computed 】

    当一个组件需要获得多个状态时,将这些状态都声明为计算属性会有些复杂和冗余。

    • 使用 mapState 辅助函数自动生成计算属性。
    import {mapState} from 'vuex'
    export default {
        computed: mapState({
            count: state => state.count,
            countAlias: 'count', // 等同于 state => state.count
            countPlusLocalState(state) {
                return state.count+this.localCount
            }
        })
    }
    
    • 当映射的计算属性的名称与 state 的子节点的名称相同时,可以给 mapState 传一个字符串数组。
    computed: mapState(['count']) // this.count=store.state.count
    
    3. 对象展开运算符
    • 通过对象展开运算符,可以将 mapState 函数返回的对象与局部计算属性混合使用。
    computed: {
        localComputed() { },
        ...mapState({})
    }
    
    4. 组件仍然保有局部状态

    3.3 Getter

    vuex 允许我们在 store 中定义 getter。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖发生了改变才会被重新计算。

    • Getter 接收 state 作为其第一个参数。
    const store = new Vuex.Store({
        state: {
            todos: [
                {id: 1, text: '...', done: true},
                {id: 2, text: '...', done: false}
            ]
        },
        getters: {
            doneTodos: state=> {
                return state.todos.filters(todo=>todo.done)
            }
        }
    })
    
    • Getter 也接受其他 getter 作为第二个参数。
    getters: {
        doneTodosCount: (state, getters) {
            return getters.doneTodos.length
        }
    }
    
    • getter 在通过属性访问时是作为 vue 的响应式系统的一部分缓存其中的。
    1. mapGetters 辅助函数
    • mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
    import {mapGetters} from 'vuex'
    export default {
        computed: {
            ...mapGetters(['doneTodosCount','anotherGetter'])
        }  
    }   
    
    • 使用对象形式,给一个 getter 属性另取一个名字
    ...mapGetters({
        doneCount: 'doneTodosCount'
        // doneCount: this.$store.getter.doneTodosCount
    })
    

    3.4 Mutation

    • 更改 vuex 中 store 的状态的唯一方法就是提交 mutation

    • vuex 中的 mutation 类似于事件:每个 mutation 都有一个事件类型 type + 回调函数 handler。这个回调函数就是我们实际进行状态更改的地方,并且它会接收 state 作为第一个参数。

    const store = new Vuex.Store({
        state: {
            count: 1
        },
        mutations: {
            increment(state) {
                state.count++
            }
        }
    })
    // 调用mutation handler
    this.$store.commit('increment')
    
    1. 提交载荷
    • 可以向 store.commit 传入额外的参数,即 mutation 的载荷 payload
    mutations: {
        increment(state,n) {
            state.count += n
        }
    }
    // 调用
    this.$store.commit('increment', 10)
    
    • 多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。
    mutations: {
        increment(state, payload) {
            state.count += payload.amount
        }
    }
    // 调用
    this.$store.commit('increment', {amount: 10})
    
    2. 对象风格的提交方式
    • 提交 mutation 的另一种方式是直接使用包含 type 属性的对象。当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变。
    this.$store.commit({type:'increment', amount: 10})
    
    3. Mutation 必须是同步函数
    • 每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。

    • 任何在回调函数中进行的状态的改变都是不可追踪的。

    mutations: {
        someMutation(state) {
            api.callAsyncMethod(()=>{state.count++})  
        }  
    }
    
    4. 在组件中提交 Mutation
    • 可以在组件中使用 this.$store.commit('xxx') 提交 mutation

    • 可以使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用。

    import {mapMutation} from 'vuex'
    export default {
        methods: {
            ...mapMutations(['increment','incrementBy']),
            ...mapMutations({add: 'increment'}),
            ...mapMutations({
                changeQty2: (commit,payload)=> {
                    commit('changeQty',payload)
                }
            })
        }
    }
    

    3.5 Action

    • Action 类似于 mutation,不同在于:

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

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

    const store = new Vuex.Store({
        state: {
            count: 0
        },
         mutations: {
            increment(state) {
                state.count++
            }
        },
        actions: {
            incrtement(context) {
                context.commit('increment')
            }
        } 
    })
    
    • Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象。

    • 可以调用 context.commit 提交一个 mutation ,或者通过 context.statecontext.getters 来获取 state 和 getters 。

    actions: {
        increment({commit}) {
            commit('increment')
        }
    }
    
    1. 分发 Actions
    • Actions 通过 store.dispatch 方法触发。
    actions: {
        incrementAsync({commit}) {
            setTimeout(()=> commit('increment'), 1000)
        }
    }
    this.$store.dispatch('incrementAsync')
    
    • Actions 同样支持的载荷方式和对象方式进行发布。
    this.$store.dispatch('incrementAsync', {amount: 10})
    this.$store.dispatch({type: 'incrementAsync',amount: 10})
    
    2. 在组件中分发 Action
    • 在组件中使用 this.$store.dispatch('xxx') 分发 action

    • 可使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用。

    export default {
        methods: {
            ...mapActions(['increment','incrementBy']),
            ...mapActions({add:'increment'}),
            ...mapActions({changeQtyAsync(dispatch, id, qty){
                    dispatch('changeQtyAsync',{id, qty})
                }
            })
        }  
    }
    
    3. 组合 Action
    • store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise

    • 一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有函数触发完成后,返回的 Promise 才会执行。

    actions: {
        actionA({commit}) {
            return new Promise((res, rej)=>{
                setTimeout(()=>{
                    commit('someMutation')
                    res()
                }, 1000)    
            })
        },
       actionB({dispatch, commit}) {
          return dispatch('actionA').then(()=>{
            commit('someOtherMutation')
          })
       }
    }
    
    this.$store.dispatch('actionA').then(()=>{})
    actions: {
        async actionA({commit}) {
            commit('gotData', await getData())
        },
        async actionB({dispatch, commit}) {
            await dispatch('actionA')
            commit('gotOtherData', await getOtherData())
        }
    }
    
    

    应用

    // cart.js
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    export default {
        data(){return {}},
        computed: {
            // goodsList() {
            //     return this.$store.state.goodsList
            // },
            // totalPrice() {
            //    return this.$store.getters.totalPrice
            // },
            
            ...mapState({
                goodsList(state) {
                    return state.goodsList
                }
            }),
            ...mapGetters(['totalPrice'])
        },
        methods: {
            // removeItem(id) {
            //     this.$store.commit('removeFromCart', id)
            // },
            // clearCart() {
            //     this.$store.commit('clearCart')
            // },
            // changeQty(id, qty) {
            //     this.$store.dispatch('changeQtyAsync', {id, qty})
            // }
            ...mapMutations({
                removeItem: 'removeFromCart',
                changeQty:(commit, payload)=>{
                    commit('changeQty', payload)
                }
            }),
            ...mapMutations(['clearCart']),
            ...mapActions({
                changeQtyAsync(dispatch, id, qty) {
                    dispatch('changeQtyAsync', {id, qty})
                }
            })
        }
    }    
    
  • 相关阅读:
    邮件系列3 --- 安全传输
    配置postfix支持虚拟域和虚拟用户
    Postfix+Sasl+Courier-authlib+Dovecot+MySQL+extmail 邮件系统部署
    在switch中的case语句中声明变量编译出错的解决方案
    关于自控力
    PUTTY使用Ctrl+s僵死的问题
    sqlite3里类似top的用法
    二维数组、行指针、指针数组、二级指针
    sqlite3_exec函数的使用
    基于s5pv210嵌入式linux使用其他动态、静态库文件程序的交叉编译
  • 原文地址:https://www.cnblogs.com/pleaseAnswer/p/12512131.html
Copyright © 2020-2023  润新知