• vuex的使用及持久化state的方式详解


    vuex的使用及持久化state的方式详解

    转载  更新时间:2018年01月23日 09:09:37   作者:baby格鲁特    我要评论

    这篇文章主要介绍了vuex的使用及持久化state的方式详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    当我们接触vuex的时候,这是我们最先看到的一句官方引导。

    从这句话中,我们可以得到如下几个信息:

    1、vuex是一个为vue而存在的特化的Flux,如同数据库中的弱实体一样,离开了vue,vuex就用不了。反之可以看到redux就不存在,无论是vue还是react,redux都可以使用。所以这里体现的vuex的“特性”,redux则具备“普适性”

    2、集中式的管理说明vue中所有的组件的状态都是存在于vuex中

    3、使用vuex你就要遵循我的规则,这样组件中状态的变化我才能跟踪的到。

     

    1.项目中vuex目录的搭建

    上图是我在这篇文章中,vue整体项目骨架的局部。

    vuex使用的是单一的状态树,我们的vue应用将仅仅包含一个 store 的实例。所以当我们将store挂载到vue的实例上以后,我们可以通过this.$store取到vuex里面的各个部分。

     

    2.index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import vue from 'vue'
    import vuex from 'vuex'
    import mutations from './mutation'
    import getters from './getter'
     
    vue.use(vuex)
     
    const state = {
     isLoading:false
    }
     
    export default new vuex.Store({
     state,
     getters,
     mutations
    })

    在index这个文件中,我们会去定义我们需要在vuex中存储的状态初始值。

    比如说,我在上面的state对象中去存储了一个isLoading属性,该属性我准备用它来标识我请求backend API的时候显示,在请求完成后消失的这样一个loading的效果,来缓解一下用户的等待心理。

     

    3.Mutation(mutation.js)

    一般来说,我们在项目中最常用的就是mutation.js里面的方法了。因为更改vuex中的store里的state的唯一的方式就是提交mutation。

    在vuex中,每个mutation都有一个字符串的事件类型(mutation-type)和一个回调函数(handler)。

    这个回调函数可接受两个参数,第一个参数为state,第二参数是mutation的载荷(payload)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //...
    mutations: {
     /**
     * @method:只传入state,修改loading状态
     * @param {bool} isLoading:loading状态
     */
     changeLoading(state) {
     state.isLoading = !state.isLoading
     }
    }
    store.commit('changeLoading')
     
    mutations: {
     /**
     * @method:传入state和payload,修改loading状态
     * @param {bool} isLoading:loading状态
     */
     changeLoading(state,payload) {
     state.isLoading = payload.isLoading
     }
    }
    store.commit('changeLoading',{isLoading: true})

    还有一种提交mutation的方式是直接使用包含 type 属性的对象,不过我不是很推荐这样的方式,因为用上面的方式来处理的话,代码的易读性会更高。

    1
    2
    3
    4
    store.commit({
     type: 'changeLoading',
     isLoading: true
    })
     

    4.mutation-types.js

    在需要多人协作的项目中,我们可以使用常量代替mutation 事件类型。这在各种 Flux 实现中是很常见的模式。同时把这些常量放在单独的文件中可以让协作开发变得清晰。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // mutation-types.js
    export const CHANGE_LOADING= 'CHANGE_LOADING'
     
    // mutation.js
    import { CHANGE_LOADING} from './mutation-types'
     
    export default{
     [CHANGE_LOADING](state,payload){
      state.isLoading = payload.isLoading
     },
    }

    对于定义mutation-type里面的事件类型,我大致遵循我自己定义的如下规范:

    1、因为mutation类似于事件,所以以动词开头

    2、单词间以下划线进行连接

    3、保存到vuex里面的状态用RECORD标识

    4、缓存到本地的状态用SAVE标识

     当然,这个规范的话大家可以自己定义,只要能通过mutation-type就能知道mutation的意图就是极好的。

     

    5.Getter(getter.js)

    有时候我们需要从 store 中的 state 中派生出一些状态,例如我上面提到的需要在异步请求的时候去显示一个带有遮罩层的loading,然后我loading的下面需要根据state去展示loading的状态。在不使用getter的情况下,我们会选择使用计算属性去处理。

    1
    2
    3
    4
    5
    computed: {
     loadingTxt () {
     return this.$store.state.isLoading ? '加载中' : '已完成';
     }
    }

    但是,我们这个loading需要在很多的组件中去使用它。那么,我们要么复制这个函数,要么抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

    如果使用Getter,那么一切都变得美好了。

    1
    2
    3
    4
    5
    6
    //getter.js
    export default {
     loadingTxt:(state) =>{
      return state.isLoading ? '加载中' : '已完成';
     }
    };

    就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

    并且,Getter 也可以接受其他 getter 作为第二个参数:

    1
    2
    3
    4
    5
    6
    7
    8
    export default {
     loadingTxt:(state) =>{
      return state.isLoading ? '加载中' : '已完成';
     },
      isLoading:(state,getters) => {
      return 'string' === typeof getters.loadingTxt ? true : false;
     }
    };

    通过mapGetters辅助函数可以将 store 中的 getter 映射到局部计算属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //组件中
    import { mapGetters } from 'vuex'
     
    export default {
     data(){
     return {
      //...
     }
     },
     computed: {
     // 使用对象展开运算符将 getter 混入 computed 对象中
     ...mapGetters([
      'loadingTxt',
      'isLoading',
      // ...
     ])
     }
    }
     

    6.Action(action.js)

    action的功能和mutation是类似的,都是去变更store里的state,不过action和mutation有两点不同:

    1、action主要处理的是异步的操作,mutation必须同步执行,而action就不受这样的限制,也就是说action中我们既可以处理同步,也可以处理异步的操作

    2、action改变状态,最后是通过提交mutation

    就拿购物车来说,当我们去添加一个商品的时候,我们需要先和后台去通讯一次,这里涉及到sku或者说是如果用户只添加了但是没有去下单。

    如果后台添加成功,前端再去展示新添加的商品,如果失败,我们需要告诉用户添加失败了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const actions = {
     checkout ({
       state,
          commit,
       //rootState
          }, products) {
     const savedCartItems = [...state.added]
     commit(SET_CHECKOUT_STATUS, null)
     // 置空购物车
     commit(SET_CART_ITEMS, { items: [] })
     shop.buyProducts(
      products,
       //成功
      () => commit(SET_CHECKOUT_STATUS, 'successful'),
      //失败
      () => {
      commit(SET_CHECKOUT_STATUS, 'failed')
      commit(SET_CART_ITEMS, { items: savedCartItems })
      }
     )
     }
    }
     

    7.module

    当我们的项目足够大的时候,单一的状态树这个时候就会显得很臃肿了。因为需要用vuex进行状态管理的状态全部集中在一个state对象里面。

    所以,当一个东西大了以后,我们就要想办法进行分割,同样的道理,我们熟知的分冶法和分布式其实也是基于这样的一个思想在里面。而vuex提供了module,我们就可以去横向的分割我们的store。

    比如说,我在项目中需要去做一个购物车这样的东西,这在电商的项目中也是常见的需求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    //shopCart.js
    import shop from '../../api/shop'
    import {
     ADD_TO_CART,
     SET_CART_ITEMS,
     SET_CHECKOUT_STATUS
    } from '../mutation-types'
     
    const state = {
     added: [],
     checkoutStatus: null
    }
     
    /**
     * module getters
     * @param {Object} state:模块局部state
     * @param {Object} getters:模块局部getters,会暴露到全局
     * @param {Object} rootState:全局(根)state
     */
    const getters = {
     checkoutStatus: state => state.checkoutStatus,
     cartProducts: (state, getters, rootState) => {
     return state.added.map(({ id, quantity }) => {
      const product = rootState.products.all.find(product => product.id === id)
      return {
      title: product.title,
      price: product.price,
      quantity
      }
     })
     },
     cartTotalPrice: (state, getters) => {
     return getters.cartProducts.reduce((total, product) => {
      return total + product.price * product.quantity
     }, 0)
     }
    }
     
    /**
     * module actions
     * @param {Object} state:模块局部state
     * @param {Object} getters:模块局部getters,会暴露到全局
     * @param {Object} rootState:全局(根)state
     */
    const actions = {
     checkout ({
       state,
          commit,
       //rootState
          }, products) {
     const savedCartItems = [...state.added]
     commit(SET_CHECKOUT_STATUS, null)
     // 置空购物车
     commit(SET_CART_ITEMS, { items: [] })
     shop.buyProducts(
      products,
       //成功
      () => commit(SET_CHECKOUT_STATUS, 'successful'),
      //失败
      () => {
      commit(SET_CHECKOUT_STATUS, 'failed')
      commit(SET_CART_ITEMS, { items: savedCartItems })
      }
     )
     }
    }
     
    /**
     * module mutations
     * @param {Object} state:模块局部state
     * @param payload:mutation的载荷
     */
    const mutations = {
     //用id去查找商品是否已存在,
     [ADD_TO_CART] (state, { id }) {
     state.checkoutStatus = null
     const record = state.added.find(product => product.id === id)
     if (!record) {
      state.added.push({
      id,
      quantity: 1
      })
     } else {
      record.quantity++
     }
     },
     [SET_CART_ITEMS] (state, { items }) {
     state.added = items
     },
     [SET_CHECKOUT_STATUS] (state, status) {
     state.checkoutStatus = status
     }
    }
     
    export default {
     state,
     getters,
     actions,
     mutations
    }

    在module的定义的局部state,getters,mutation,action中,后三个都会暴露到全局的store中去,这样使得多个模块能够对同一 mutation 或 action 作出响应。就不需要在其他的模块中去定义相同的mutation或action了。

    而这里的state是局部的。这也导致后来的持久化无法去处理用module分割后的state。

    如同上面的module =》shopCart,

    当我们无论是在index.js里面或者其他的module中,shopCart里面的getters或者action或者mutations,我们都可以去使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //test.js
    const state = {
     isTest:false
    };
     
    const getters = {
     isTest :state => state.isTest,
     checkTestStatus:(state,getters) => {
     return getters.checkoutStatus;
     }
    };
     
    export default {
     state,
     getters,
    }
    //组件中
    ...mapGetters([
     'checkTestStatus'
    ])
    //...
    created(){
     this.checkTestStatus ;//null
    }

    如果说,我就想让我的module里面的定义的全部都是独享的。我们可以使用module的命名空间,通过设置namespaced: true。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //test.js
    const getters = {
     // 在这个模块的 getter 中,`getters` 被局部化了
     // 你可以使用 getter 的第四个参数来调用 `rootGetters`
     someGetter (state, getters, rootState, rootGetters) {
     getters.someOtherGetter // 'test/someOtherGetter'
     rootGetters.someOtherGetter // 'someOtherGetter'
     },
     someOtherGetter: state => { ... }
    };
     
    const actions = {
     // 在这个模块中, dispatch 和 commit 也被局部化了
     // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
     someAction ({ dispatch, commit, getters, rootGetters }) {
     getters.someGetter // 'test/someGetter'
     rootGetters.someGetter // 'someGetter'
     
     dispatch('someOtherAction') // 'test/someOtherAction'
     dispatch('someOtherAction', null, { root: true }) // 'someOtherAction'
     
     commit('someMutation') // 'test/someMutation'
     commit('someMutation', null, { root: true }) // 'someMutation'
     },
     someOtherAction ({ state,commit }, payload) { ... }
    }
     
    export default {
     namespaced: true,
     state,
     getters,
     actions,
     mutations
    }
     

    8.持久化state的工具:vuex-persistedstate

    用过vuex的肯定会有这样一个痛点,就是刷新以后vuex里面存储的state就会被浏览器释放掉,因为我们的state都是存储在内存中的。

    而像登录状态这样的东西,你不可能一刷新就让用户重新去登录吧!所以,我们会去选择把状态存储到本地。

    这样一来问题貌似是解决了,但是当我们需要使用的时候,我们就需要不断的从本地,通过getStore这样的方法去取得我们state。如果需要更新的话,我们又要在mutation里面通过setStore这样的方法去处理它。

    虽然,我们的setStore都是在操作了state以后再去调用的,也就是说无论是通过vuex的logger或者vue的dev tool我们都是可以对local里面的状态进行跟踪的,但是,我们无法保证我们每次都记着去写setStore。

    这样一来,在共享state的组件中,我们的代码可能就会是这样的。

    1
    2
    3
    4
    5
    6
    7
    import { getStore } from '@/util'
    //组件中
    mounted(){
     this.foo = getStore('foo');
     this.bar = getStore('bar');
     //....
    }

    那么,如何去改进呢?

    我们能想到的就是,能不能让state不是保存在内存中,而是存储在本地。

    而vuex-persistedstate做了这样的事情,它帮我们将store里面的state映射到了本地环境中。这样一来,我通过提交mutation改变的state,会动态的去更新local里面对应的值。

    大家感兴趣的话,可以戳这里,这里有个小dmeo 

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

     

    原文链接:http://www.cnblogs.com/ChengWuyi/p/8277136.html

  • 相关阅读:
    iis环境异常处理
    cmd常用命令:关机、注销、进入d盘等
    position
    Register Form
    第一周
    Django简介
    前端jQuery基本语法
    前端基础BOM和DOM
    HTML常用标签
    Linux相关 MySQL相关
  • 原文地址:https://www.cnblogs.com/leigepython/p/9405422.html
Copyright © 2020-2023  润新知