zoukankan      html  css  js  c++  java
  • 深入学习-Vuex 文档解读

    Vuex 管理状态

    state 

    单一状态树,意思是一个对象包含了全部应用层级状态,Store将作为唯一数据源。

    每个应用,仅仅有且只有一个 store 实例!

    mapState

    当一个组件组件组件需要多个状态值时,可以调用 mapState函数赋值给 computed 返回是对象。

    // mapState 基本用法,3种: 箭头函数, 字符串, 函数.

    computed:mapState({

          //1,箭头函数 countFromStore:state=>state.count

          //2,字符串  countFromStore: 'count'     等价于   state=>state.count

          //3,函数  需要使用组件内数据访问this时,还可以用函数

           countFromStore(state) {

                 return state.count+this.localCount;

           }

    }),


    // 如果同名取Store中的值,可以取count字符串放在数组中传递给 mapState.

    computed:mapState([

          'count'    // 可以访问多个状态

    ]),

    // mapState返回对象所以可以扩展computed对象.

    computed: {

           ...mapState([     

                  'count'

           ]),

           localCount() {return this.localCount + this.$store.state.count;}

    }

    getters

    可以吧 getters 当作是 state store的计算属性,传参是state .

    Vue.use(Vuex);

    conststore=newVuex.Store({

    state: {

       count:1,

       name:'NewStation',

       todos: [

          { id:1, text:'todos -1', done:true },

          { id:2, text:'todos -2', done:false },

       ]

    },

    mutations: {       // this.$store.commit('changeName') 触发commit更新store

        increment(state) {

            state.count++

        },

        changeName(state) {

             state.name='NewPlace'

         }

    },

    getters: {           //  this.$store.getters.doneTodos  获取getters编译后的值

        doneTodos:state=> {

             returnstate.todos.filter(todo=>todo.done)[0].done;

        },

        doneState: (state, getter)=> {

             return  getter.doneTodos + 'getter 作为其他 getter 的传参';

        }

    }

    });

    mapGetters

    辅助函数,,支持2种方式转换,把store中的getters 可以支持传参,映射到局部计算属性。

    computed: {

    ...mapGetters([    数组

          'doneTodos',                               // 可以直接调用

    ]),

    ...mapGetters({    对象

           aliasFillname:'getFillName'  //给 getter 加别名。

    }),

    }

    mapMutation 

     

    更新 store状态 的唯一方法是提交mutation。 Vuex中的 mutation 类似于事件:每个 mutation 都有自己的事件类型和回调函数,这个回调函数就是我们状态更改的地方,并接受 state 作为第一个参数

    const store = new Vuex.Store({

     state: {

       count: 1

     },

     mutations: {

       increment (state) {

         // 变更状态

         state.count++

       }

     }

    })

    store.commit('increment')   只能用这个方式修改状态!

    触发mutation更新状态,触发一个叫 increment 类型的事件,并回调执行 这个类型的注册函数

    提交负载Payload

    可以给commit传参,称为 Payload负载,通常是一个对象。

    this.$store.commit('increment',   { account: 'local' })

    对象的风格提交

    只给mutation传递对象,表示整个对象被当做 Payload,被解析,mutation中获取的方式是不变的。

    this.$store.commit({

         type: 'increment',

         account: 'local'

    })

    Mutation需遵守vue响应规则

    1,提前在store中初始化所有有声明的属性值

    2,获取对象值之后,如需修改推荐调用 Vue.set (obj, 'newkey', 'newvalue') 或者 这样 {...obj, newProp: 123}

    store的对象放在前。

    3,推荐使用常量const方式命名mutation中事件的类型名,也可以不用。

    Mutation必须是同步函数

    并不是异步调用mutation不会触发状态变化,而是会造成状态无法追踪,不好调试。

    在组件中提交mutation

    methods: {

    ...mapMutations([    数组

          'doneTodos',                               // 可以直接调用 this.doneTodos('参数') 触发doneTodos事假类型 映射                                                                  // 映射 this.$store.commit

    ]),

    ...mapMutations({    对象

           aliasFillname:'getFillName'  //给 getter 加别名。 使用别名调用

    }),

    }

    Action

    Action类似于mutation,不同之处是

    1,Action是提交mutation,不直接修改store的值。

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

    接受一个和mutation一样的对象属性,可以使用解构

    actions: {

       incrementPlus ({ commit }) {

            commit('increment');

            setTimeout(() => {

                 commit('actionOther');   // 可以触发多个commit

            }, 0)

       }

    }


    this.$store.dispatch('incrementPlus')  可以直接使用消息派发

    dispatch 的调用修改状态的方式,和 mutation 一致,可以接受Payload参数,可以接受对象更新状态。

    好处可以在Action中定义任意多的 异步处理。

    methods: {

    ...mapActions([    数组

          'doneTodos',                               // 可以直接调用 this.doneTodos('参数') 触发doneTodos事假类型

    ]),

    ...mapActions({    对象

           aliasFillname:'getFillName'  //给 getter 加别名。 使用别名调用

    }),

    }

     

    组合Action

    1,Action可被其他Action调用

    2,Action内部可以是用 Promise 或 时间事件

    actionIncrement ({commit}) {

          return new Promise((resolve) => {

                setTimeout(() => {

                      commit('increment');

                      resolve()

                });

          })

    },

    actionB ({ dispatch, commit }) {

          return dispatch('actionIncrement').then(() => {

                commit({

                      type: 'changeName',

                      account: '1000'

                })

          })

    }

    dispatch actionB 会执行被应用的Action动作 触发联动包括异步的处理和请求。

    actions: {

         async actionA ({ commit }) {

              commit('gotData', await getData())

         },

         async actionB ({ dispatch, commit }) {

               await dispatch('actionA')        // 等待 actionA 完成

               commit('gotOtherData', await getOtherData())

         }

    }

    module 

    由于我们使用一个单一的状态树,因此会导致会有一个很大的对象需要维护。

    那怎么维护呢? Vuex可以使用 module 分割模块,每个模块都有自己独立的 state mutation getter Action 甚至是嵌套子module,自上而下一样的方式分割。

    modules

     

    modules: {      丰富在 Vuex.Store 中,和 state 同级

         a:moduleA,

         b:moduleB,

    }

    store.state.a // -> moduleA 的状态     分别访问各自module的状态,getter Action 以此类推。

    store.state.b // -> moduleB 的状态

    局部状态

    module 内部的 mutation getters ,内部函数的首个参数为 当前module的state,有隔离。

    那 如何获取到 根module的 state 呢?(根的commit 和 getter 好像不能访问。)

    根module,可以从第三个参数 rootState 正常获取

    超级无敌神奇,在其他module 访问 rootState ,commit rootState ,其他module会直接值变化。

    命名空间

    以上局部模块,可访问rootState的特性,也使得在组件中访问 state或getter时并不知道,是属于哪个模块的。

    这可怎么办呢? 可以用namespace 命名空间,更能复用,更高的封装。

    命名空间可以访问 getter 不能直接访问 state!那咋办,有两个方法可以

    1,保留像之前一样的访问形式,可以因为vuex提供的api : createNamespacedHelpers 

    import { mapGetters, createNamespacedHelpers} from"vuex";

    const { mapState } =createNamespacedHelpers('account') ;  account就是对应的命名空间名。

    2,在computed 中直接使用,需要指明命名空间名

    ...mapState('account', {userNa: 'userName'}) 添加命名空间 ,还可以加别名。

    因此,getter dispatch commit 会局部化,无需修改代码,只用加上 namespaced 。

    命名空间内访问全局state

    命名空间内的state getter 已经无法直接被访问可以加修饰访问,那还可以和全局state通信吗? 嗯可以。

    1,从全局module获取 在 getters 内访问全局 提供了第三,第四 个参数

    someGetter (state, getters, rootState, rootGetters) {}
    someAction ({ dispatch, commit, getters, rootGetters }) {}

    2,如果从命名空间dispatch 信息到全局呢?

    dispatch('someOtherAction', {count: 999}, { root: true })

    commit('someMutation', null, { root: true })

    可以在命名空间内定义全局可访问的Action吗,嗯可以

    modules: {

          foo: {

               namespaced: true,

               actions: {

                      someAction: {

                            root: true,

                            handler (namespacedContext, payload) { ... } // -> 'someAction'  全局可dispatch

                       }

                 }

            }

     }

     

    模块动态注册

    store 创建之后,store.registerModule  

    动态注册模块,需要在组件实例化之后,所以你在不是组件实例化之外的地方调用,vuex报找不到addChild,坑爹。

    // 注册模块 `myModule`  可以再 created mounted 之后

    store.registerModule('myModule', {

         // ...

    })

    // 注册嵌套模块 `nested/myModule`

    store.registerModule(['nested', 'myModule'], {

     // ...

    })

    访问,通过 store.state.myModule  store.state.nested.myModule (嵌套)  访问模式状态

    动态注册模块,使得在vuex中更灵活的配置状态管理,比如 vuex-router-sync 就是通过在状态中注册动态的module扩展应用

    卸载模块,unregisterModule 卸载动态添加的模块,不能卸载静态的已声明的模块。

    模块添加时不希望将之前的state覆盖,可以保留state

    store.registerModule(a, 'module', { preserveState: true })

    模块重用

    1,创建多个store(ssr尽量不要用)

    2,多次注册同一个模块

    所以,需要纯函数并且保持返回一致的状态,对象被引用共享,导致状态对象被修改,修改store或者模块之间数据

    被污染,解决办法还是通过像 vue data (){} 一样的处理方式

    const MyReusableModule = {

           state () {

                  return {

                         foo: 'bar'

                  }

          },

          // mutation, action 和 getter

    }

    项目结构

    vuex并不限制是什么样的代码结构,但是你会发现如果不给 mutation state getter Action 分分类可能会比较难维护。 vuex 给出了一些建议,关于构建状态管理项目的建议。

    1,应用层级的状态应该集中在单个状态对象

    2,提交 mutation  是修改状态的唯一对象,过程是同步的

    3,异步的逻辑应该在Actions 里面封装

    image.png

    插件扩展

    Vuex的store接受plugins参数,这个选项暴露出每次mutation的钩子,vuex 插件是一个函数,唯一的参数是store

    const myPlugin = store => {

         // 当 store 初始化后调用

         store.subscribe((mutation, state) => {

              // 每次 mutation 之后调用

              // mutation 的格式为 { type, payload } type是事件类型,Payload是载荷传参,。

              // state 值包含根store(全局store)的state值,module值,和registerModule注册的module值!!

         })

    }

    store的存储和更新,可以和其他事件结合使用,比如可以和websocket的onchat事件、onemit提交事件合并使用。反正就是可以在插件里,想干嘛干嘛各种自由。

    State更新前生成快照

    为甚需要生成快照呢,因为你需要对比state的前后变化,有时候是需要的考虑到性能(前端必须考虑性能)。

    实现state快照进行前后对比,一般是这样的代码:  (官网建议快照只在开发阶段使用)

    因为Subscribe 监听的是 store 所以store内变化都会反映。

    内置logger插件

    importcreateLoggerfrom'vuex/dist/logger';

    可以再 createLogger函数传入一些配置项,并把结果传给 plugins。

    严格模式

    这里严格模式是 Store state的严格模式,并非插件或module的严格模式!

    strict: true.

    在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。

    表单处理

    在使用严格模式时,如果state 使用 v-model 会比较棘手。

    (非严格模式下,控制台会有如下提示,console打印 Computed property "count" was assigned to but it has no setter.) 提示警告,computed中的值没有setter属性。

    (严格模式下,和非严格模式下,这种-v-model的方式都会有一样的报错。)

    但是,还是要更新count的值,那咋办呢,可以监听  事件

    <input :value="message" @input="updateMessage">

     

    :value,message,bind绑定message,单向

    message,是state中的值,mapState映射

    updateMessage是input输入方法,触发这个方法,在这个方法内执行 commit 更新state,

    然后,形成一个更新的环。

    这样一来,用不了v-model指令,而且更新起来比较繁琐,得不偿失。

    继续往下看。

    双向绑定

    <inputv-model="message">

    computed: {

          message: {

                get () {

                      return this.count

                },

                set (value) {

                      this.updateMessage({count: value})

                }

          }

    }

    利用 computed 中属性值 getter setter 方法合并出一个和Store双向绑定的值。

    单元测试

    测试mutation,这个很好测试,都是传参的函数。 举个栗子

    使用 mocha  chai 进行测试,该装的插件都装上。

    package.json -> scripts

    "test": "mocha --require babel-register ./src/store/index.spec.js"

    测试Action,

    有点小麻烦,需要一些测试基本知识 

    相当陌生,暂时搁置。。。。

    热重载

    webpack 提供了内置的插件 module.hot 结合 Vuex.hotUpdate .

    Hot Module Replacement

    Vuex 基于这个热重载插件,热重载mutation,module,action,getter,对于mutation 和模块,可以使用

    store.hotUpdate()

    if (module.hot) {

          module.hot.accept([

                './getters',

                './actions',

                './mutations'

          ], () => {

                store.hotUpdate({

                getters:require('./getters'),

                actions:require('./actions'),

                mutations:require('./mutations')

          })

    }

    Store 对生命周期的影响

    初始阶段

    就是组件实例化和mounted的过程,和从data或props中取值并没有区别。

    App                      ---beforeCreate--

    App                      ---created---

    App                      ---beforeMount---

          Helloworld           ---beforeCreate--

          Helloworld           ---created---

          Helloworld           ---beforeMount---

          Helloworld           ---mounted---

    App                      ---mounted---

    store的state更新阶段

    App                      ---beforeUpdate---

          Helloworld              ---beforeUpdate---

          Helloworld              ---updated---

    App                      ---updated---

    时光旅行 - Vuex状态值

    什么是Store 的时光旅行?

    1. 通过vuex的执行的操作会被记录下来
    2. 可以选择操作记录,返回回退到此操作时的状态

     
     
     
  • 相关阅读:
    前台js的复制与粘贴
    idea
    前台 js easyUI datagrid 杂记 验证(disable)
    《命运赋》
    前台
    js 、 java去除字符串中的数字
    【 协议 】 freemodbus的分层结构分析
    王爽 汇编11.10(2)编程用串传送指令,将F000H段中最后的16个字节复制到data段中
    王爽 汇编11.10(1)编程用串传送指令,将data段中的第一个字符串赋值到它后面的空间中
    汇编语搜索言中32位CPU多出的两个FS、GS段寄存器,全称是什么啊?
  • 原文地址:https://www.cnblogs.com/the-last/p/11391731.html
Copyright © 2011-2022 走看看