Vuex 状态管理

一、为什么使用vuex : https://www.cnblogs.com/goloving/p/9080005.html

  vuex的功能 和 localstorage 的作用是一样,把数据在一个所有页面都可以存取的地方。但是vuex的数据具有响应式(类似数据双向绑定),而 localstorage 的数据是固定的,必须手动设置。

  • 使用场景【不同组件中】:祖孙组件件的数据传递
  • Vuex 的缺点:vuex是存储在内存中的,刷新页面 或 跳转到 项目外的页面就会清除页面内存的数据,导致页面不能正常显示。【
                      app虽然没有刷新功能,但是跳到外联,在回退到页面。也是相当于刷新了页面。
    注意:跳出当前的单页应用,即不同html文件。再次返回时,也是相当于刷新了页面。
  • 解决Vuex的这个缺点:这个问题,只要设置Vuex值时,保存到localstorage 中。在刷新页面后 从localstorage 取出,再次 赋值进去就可以了。
    Vuex数据丢失的解决方案【刷新,跳转外链再返回】:https://blog.csdn.net/liuliuliu_666/article/details/93862880

二、vuex 的使用:

    https://www.cnblogs.com/hermit-gyqy/p/11270315.html(更直观) 或 https://blog.51cto.com/9161018/2351075(说明更详细)   或  https://vuex.vuejs.org/zh/guide/(官网)

 

2.1、State:

存储在 Vuex 中的数据和 Vue 实例中的 data 遵循相同的规则,例如状态对象必须是纯粹 (plain) 的。参考 vue的data

  • 在vue组件中获得Vuex状态:由于 Vuex 的状态存储是响应式的,组件中要保持同步响应式
    // 创建一个 Counter 组件
    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return this.$store.state.count
        }
      }
    }
  • mapState 辅助函数:
    mapState 函数返回的是一个对象,要将它和组件局部计算属性混合使用 就 需要用到 对象扩展运算符。
    // 组件中
    import { mapState } from 'vuex'
    
    computed: {
      // 使用对象展开运算符将此对象混入到外部对象中
      ...mapState({
        // ...
      })
    }

2.2、Getters: 

通常用在数据的二次处理(过滤数据...),可以理解为state的计算属性

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})
  • 通过属性访问:

    store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
  • 通过方法访问:
    这种方法,就是 使 getters 方法返回的是一个函数。这时通过属性访问到的就是一个方法,通过给方法传递参数就可以获取不同的数据。

    getters: {
      // ...
      getTodoById: (state) => (id) => {
        return state.todos.find(todo => todo.id === id)
      }
    }
    
    store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

    注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果
    感悟:实践中,一次性将项目用到的所有字典表拉取过来,就是通过getter方法访问的方式,筛选对应的字典列表数据的。

  • mapGetters 辅助函数:
    mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性.

    import { mapGetters } from 'vuex'
    
    export default {
      // ...
      computed: {
      // 使用对象展开运算符将 getter 混入 computed 对象中
        ...mapGetters([
          'doneTodosCount',
          'anotherGetter',
          // ...
        ])
      }
    }

    防止和 局部计算属性同名,可以给 映射的getter属性重命名

    ...mapGetters({
      // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
      doneCount: 'doneTodosCount'
    })

2.3、mutations:

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})
  • 提交载荷(Payload):
    你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)
    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }

    如上 mutilation,我们有2种提交方式:

    • 载荷方式的提交方式:【推荐
      store.commit('increment', {
        amount: 10
      })
    • 对象风格的提交方式:
      store.commit({
        type: 'increment',
        amount: 10
      })
  • 使用常量替代 Mutation 事件类型
  • 在组件中提交 Mutation:
    使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用
    import { mapMutations } from 'vuex'
    
    export default {
      // ...
      methods: {
        ...mapMutations([
          'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    
          // `mapMutations` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
        ]),
        ...mapMutations({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
        })
      }
    }

2.4、Action:

  • 分发 Action:
    Actions 支持同样的载荷方式和对象方式进行分发
    • 载荷方式:
      store.dispatch('incrementAsync', {
        amount: 10
      })
    • 对象方式:
      store.dispatch({
        type: 'incrementAsync',
        amount: 10
      })
  • 在组件中分发 Action:
    使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用
    import { mapActions } from 'vuex'
    
    export default {
      // ...
      methods: {
        ...mapActions([
          'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
    
          // `mapActions` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
        ]),
        ...mapActions({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
        })
      }
    }
  • 组合 Action:
    需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise。
    actions: {
      async actionA ({ commit }) {
        commit('gotData', await getData())
      },
      async actionB ({ dispatch, commit }) {
        await dispatch('actionA') // 等待 actionA 完成
        commit('gotOtherData', await getOtherData())
      }
    }

2.5、Modules:

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

  • 模块的局部状态:
    对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
    const moduleA = {
      state: () => ({
        count: 0
      }),
      mutations: {
        increment (state) {
          // 这里的 `state` 对象是模块的局部状态
          state.count++
        }
      },
    
      getters: {
        doubleCount (state) {
          return state.count * 2
        }
      }
    }

    同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

    const moduleA = {
      // ...
      actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
          if ((state.count + rootState.count) % 2 === 1) {
            commit('increment')
          }
        }
      }
    }

    对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

    const moduleA = {
      // ...
      getters: {
        sumWithRootCount (state, getters, rootState) {
          return state.count + rootState.count
        }
      }
    }
  • 命名空间
    默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

    如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
    • 在带命名空间的模块内访问全局内容:
      如果你希望使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。

    • 在带命名空间的模块注册全局 action:
      若需要在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中
    • 带命名空间的绑定辅助函数:【推荐使用 createNamespacedHelpers 创建基于命名空间的 辅助函数】
      当使用 mapStatemapGettersmapActions 和 mapMutations 这些函数来绑定带命名空间的模块时,写起来可能比较繁琐。可以将模块的空间名称字符串作为第一个参数传递给上述函数
      computed: {
        ...mapState('some/nested/module', {
          a: state => state.a,
          b: state => state.b
        })
      },
      methods: {
        ...mapActions('some/nested/module', [
          'foo', // -> this.foo()
          'bar' // -> this.bar()
        ])
      }

      也可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。

      import { createNamespacedHelpers } from 'vuex'
      
      const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
      
      export default {
        computed: {
          // 在 `some/nested/module` 中查找
          ...mapState({
            a: state => state.a,
            b: state => state.b
          })
        },
        methods: {
          // 在 `some/nested/module` 中查找
          ...mapActions([
            'foo',
            'bar'
          ])
        }
      }
  • 模块动态注册:
    在 store 创建之后,你可以使用 store.registerModule 方法注册模块:
    import Vuex from 'vuex'
    
    const store = new Vuex.Store({ /* 选项 */ })
    
    // 注册模块 `myModule`
    store.registerModule('myModule', {
      // ...
    })
    // 注册嵌套模块 `nested/myModule`
    store.registerModule(['nested', 'myModule'], {
      // ...
    })
  • 模块重用:【前端渲染不会用到,服务端渲染可能会用到。了解下就可以】

 

 

三、Vuex规范目录结构:

| - store
   | - actions.js
   | - getters.js
   | - index.js
   | - mutations.js
   | - mutations_type.js  # # 该项为存放 mutaions 方法常量的文件,按需引入。
   |
   | - modules
      | _ Astore.js

说明:1、mutations_type.js 文件(mutations的事件类型,即方法名称)的目的就是为了方便多个地方使用他,可能出现的拼写错误。https://www.jianshu.com/p/9802842448bc/ 

      2、modules 中的 store 一般直接放在一个文件就可以了,如果 Astore 局部模块状态多 也 可以进行细分。

 

posted @ 2018-11-09 18:18  吴飞ff  阅读(332)  评论(0编辑  收藏  举报