vue - vuex 使用进阶(一)

目录:

  • 核心概念使用方法及场景:
    • state
    • mutations
    • actions
    • getters
  • 多页面公用vuex实例
    • 在页面中调用后端接口展示数据案例

参考链接:
vuex简介
vue.js的状态管理vuex中store的使用
理解vuex,看这篇就够了


本文中不再详细介绍vuex基本概念和用法,若想详细了解请参考第一链接!

1. 核心核心概念使用方法及场景:

1.1 state

单一状态管理树,只有在这里声明我们需要的数据类型和值,才能在vue组件中获取定义的对象的状态。

1.2 mutations

mutations翻译是更改,更改store中state中的状态值的唯一方法就是提交mutations,每一个mutations都有一个字符串类型的事件类型和回调函数,执行这个回调调函的方法有三个,本质上是一个:提交mutations,即commit mutations,提交时附加一些payload数据,叫做负荷。

  • 基本用法:
mutations:{
 increment(state,payload){
   state.count = payload.number  //这里改变state中的数据
 }
}

① 在actions中commit(回调函数名,payload),具体使用情况看下面的案例.

② 在组件中methods里this.$store.commit(回调函数名,payload).

③ 在组件中使用mapMutations辅助函数将组件中的mutatios映射为this.$store.commit("回调函数名",{传入的第二个}).

1.3 actions
  • 1.3.1 actions基本用法:
    actions很多地方都和mutations用法类似,最大的不同就是actions可以是异步函数,但mutations必须是同步函数,而且actions不能直接修改state,是通过commit mutations(上一内容)来改变state.声明actions时它接受的第一个参数就是和state有相同方法和属性的context对象,第二个参数也是payload。触发actions的方法:
    ① 在actions通过context.commit(mutations中回调函数名,payload)

② 在组件中methods里分发:this.$store.dispatch("mutations中回调函数名",{payload})

③ 使用 mapActions辅助函数,将组件中的methods映射为this.$store.dispatch调用

export default{
  methods:{
    ...mapActions([
        'mutations的回调函数名'
    ])
  },
/* ----------或者-----------*/
  ...mapActions({
      页面展示用的名称 : 'mutations的回调函数名'
  })
}
  • 1.3.2 actions嵌套用法:
    actions中可以是异步函数,这就使得actions中可以嵌套另外一个actions,即this.$store.dispatch()可以处理已经被触发得的actions得到的promise,处理后的结果仍返回promise对象。
  //组合性的异步嵌套actions的操作
actions:{
  actionA({commit}){
      return new Promise((resolve,reject) =>{
          setTimeout(() => {
              commit('someMutation')  //会把得到的数据保存到someMutation位置
          } ,10000)
      })
  }
}
//在某一个页面组件中触发:
 this.$store.dispatch("actionA").then(() => {
    //... 
})
//在另外一个actions中也可以这样写,actionB处理actionA被触发得到的Promise,再提交someOtherMutations
actions:{
   //...
    actionB({diapatch,commit}){
        return dispatch('actionA').then(
            commit('someOtherMutations')  
        )
    }
}
  • 再结合async await 优化组合actions的代码
actions : {
  async actionA({commit} {
    commit('gotData',await getData())
  }),
  async actionB({dispatch,commit}){
    await dispatch('actionA')  //等待actionA执行完成后再执行
    commit('gotOtherData', await getOtherData)
  }
}
1.4 getters

getter类似于Vue.js 的computed计算属性,当我们需要从state中派生一些状态就用getter函数,getter函数的第一个参数为state,还可以接受其他参数,而且它的返回值被缓存起来,只有当getter依赖值(state中的相关状态值)发生改变才会被重新计算。

  • 基本用法:
getters:{
  doneTodos : {
      return state.todosfilter(todo => todos.count)
  }
}
  • 在页面中触发getters的两种方法:
    1. 可以通过this.$store.getters.doneTodos方法可以访问得到这些派生出来的状态值
    2. 还可以使用辅助函数:
    export default{
          computed : {
             //当同时使用多个getter函数时可以这样写
              ...mapGetters([
                   'doneTodos',
                   'anotherGetters'
              ])
          }  
    }
    
  • 如果想给一个getters(doneTodos)再取一个名字,则使用对象形式
export default{
      computed : {
          ...mapGetters({
              anotherName: 'doneTodos'
          })
      }  
}
1.5 mudules

当store太过于复杂的时候对它的一种切割,每一个module都有自己的state,mutation,action和getter

moduleA = {
  state:{},
  mutations:{},
  actions:{},
getters:{}
},
moduleB = {
  state:{},
  mutations:{},
  actions:{},
getters:{}
},
const store new Vuex.Store({
  a : moduleA,
  b : moduleB
})

store.state.a // moduleA的状态
store.state.b // moduleB的状态
1.6 mudules局部状态
    1. 模块内部mutations 和getters
      对于模块内部的mutation和getter接受的第一个参数就是模块内部局部状态对象
  moduleA = {
    // ...
    state:{count:0},
    mutation : {
        increment(state){
        // 这里的state就是模块的局部状态
            state.count ++
        }
    },
    getters : {
        doubleCount(state){
            // 这里的state就是模块的局部状态
            return state.count * 2
        }
    }
  }
    1. 模块内部actions
      对于模块内部的actions,模块内部局部状态会通过 context.state方式暴露出来
    moduleA = {
      // ...
        actions : {
          increment({state,context}){
                if(context.state.count % 2 === 1){
                    context.commit('increment')
                }
           }
        }   
    }
    1. 根节点
      对于模块内部的getters和actions根节点的暴露方式:
  moduleA = {
    // ...
    //跟节点会通过第三个参数暴露出来
    getters : {
        sumWithRootCount(state,context,rootState ){
            return state.count + rooState.count
        }
    },
  //根节点状态会通过context.rootState暴露出来
    actions : {
      incrementRootSum(state,context,rootState){
          if(context.rootState.count % 2 === 1){
              context.commit('incrementRootSum')
          }
       }
    }
  }
1.7 vuex — 计算属性 — 实现双向绑定,使用带有setter的计算属性

假设obj是计算属性返回的属于vuex store中的一个对象

<input v-model="message">

export default{
  //...
  computed:{
      message : {
          get(){
              return this.$store.state.obj.message
          },
          set(value){
              this.$store.commit('updateMessage',value)
          }
      }
  }
}


store中的mutaions:

  // ...
  mutatoins : {
       undateMessage(state,message){
          state.obj.message = message
      }
  }

2. 移动端多页面公用vuex案例

2.1 情景介绍:

当前端开发调用后端接口获取数据,并在多个页面中展示数据时,任意组件之间通信,最好的方法就是使用vuex.

2.2 使用方法:使用案例 https://github.com/vuejs/vuex/tree/dev/examples
  • 入口文件 : main.js
import Vue from 'vue';
import App from './components/App.vue';
import store from './store';
import { currency } from './currency'

Vue.filter('currency',currency)

var vm =  new Vue({
  el:'#app',
  store,
render : h => h(App)
})
  • 定义vuex .store: ./store/index.js中的代码
import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'
import products from './modules/products'
import createLogger from '../../../src/plugins/logger'

Vue.use(Vuex)

export default new Vuex.store({
    modules:{
      cart,
      products
    }
})
  • 创建两个module:
  1. ./store/modules/cart.js
import shop from '../../api/shop'

//初始化state
const state = () => {
    items : [ ],
    checkoutStatus : null
}


//getters
const getters = {
  cartProducts : (state,getters,rootState) =>{
      return state.item.map({id,quantity}) => {
          const product = rootState.prosucts.all.find(product => product.id === id)
            return {
              title : products.title,
              price : products.price,
              quantity
            }  
      }
  },

    cartTotalProducts : (state,getters)=>{
        return getters.cartProducts.reduce((products,total)=>{
            return total = products.price * products.quantity
        },0)
   }
}

//mutations
const mutations = {
    pushProductToCart(state,{id}){
      state.items.push({
          id,
          quantity : 1
      })
    } ,
  incrementItemQuantity (state, { id }) {
    const cartItem = state.items.find(item => item.id === id)
    cartItem.quantity++
  },

  setCartItems (state, { items }) {
    state.items = items
  },

  setCheckoutStatus (state, status) {
    state.checkoutStatus = status
  } 
}


//actions
const actions = {
  checkout ({ commit, state }, products) {
    const savedCartItems = [...state.items]
    commit('setCheckoutStatus', null)
    // empty cart
    commit('setCartItems', { items: [] })
    shop.buyProducts(
      products,
      () => commit('setCheckoutStatus', 'successful'),
      () => {
        commit('setCheckoutStatus', 'failed')
        // rollback to the cart saved before sending the request
        commit('setCartItems', { items: savedCartItems })
      }
    )
}
  1. ./store/modules/products.js
import shop from '../../api/shop';
//initial state
const state = () => ({
    all : []
})

// mutations
const mutations = {
  setProductss(state,products){
    state.all = products
  },
  decrementProductInventory(state,{id}){
    const product = state.all.find(product => product.id = id)
    product.inventory --
  }
}

//actions
const actions = {
  getAllProducts({commit}){
    shop.getProducts(products =>{
        commit('setProducts',products)
    })
  }
}

//getters
const getters = {
  
}

export default{
   namespaced = true,
   state,
   actions,
   mutations,
   getters
}
  • 通信的组件:
  1. /component/App.vue
<template>
  <div id="app">
    <h1>shopping cart example</h1>
    <hr>
    <h2>products example</h2>
    <ProductList/>
    <hr>
    <h2>cart example</h2>
    <ShoppingCart/>
  </div>
</template>
<script>
  import ProductList from './ProductList.vue';
  import ShoppingCart from './Cart.vue'; 

  export deafult{
      components:{
          ProductList,
          ShoppingCart
      }
  }
</script>
  1. /component/ProductList.vue
<template>
  <ul>
    <li v-for="product in products" :key="product.id">
      {{product.title}}-{{product.price | currency}}
      <button :disabled="product.inventory" @click="addProductToCart(product)">
          add to cart
      </button>
    </li>
  </ul>
</template>
<script>
import { mapSate, mapActions } from "vuex"

export default{
  // ...
  computed : ...mapState({
     products: state =>  state.products.all
  }),
  methods : ...mapActions('cart',[
   'addProductToCart' 
  ]),
  created(){
    this.$store.dispatch('products/getAllProducts')
  }
}
</script>
  1. /component/ShoppingCart.vue
<template>
  <div class="cart">
    <h2>Your cart</h2>
    <p v-show="!products.length"><i>please add some products to cart.</i></p>
    <ul>
      <li v-for="product in products" :key="product.id">
          {{ product.title }} - {{ product.price | currency }} x {{ product.quantity }}
      </li>
    </ul>
    <p>Total:{{total | currency}}</p>
    <p><button :disabled="!product.length" @click="checkout(products)">checkout</button></p>
    <p v-show="checkoutStatus">Checkout{{checkoutStatus}}.</p>
  </div>
</template>
<script>
import  { mapGetters, mapState }  from 'vuex';

export default{
    computed : {
      ...mapState){
          checkoutStatus : state => state.cart.checkoutStatus
       }),
      ...mapGetters:('cart',{
          products : 'cartProducts',
          total : 'cartTotalProducts'
       })
    },
    methods : {
        checkout(products){
            this.$store.dispatch('cart/checkout', products)
        }
    }
}
</script>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335