Vuex實現狀態管理

Vuex使用總結

1 Vuex簡介

  Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式,Vuex抽取了各個組件的共享部分,以全局單例模式進行狀態的管理。在原生vue中各個組件之間傳值使用的是props和event,若是組件嵌套層數過多使用props進行傳參會十分繁瑣,vuex使用「惟一數據源」進行管理,全部的組件直接從vuex獲取數據便可;在使用原生vue時,若是多個視圖共享同一個狀態的話,當一個視圖修改這一狀態,咱們須要同步其餘視圖的狀態,而vuex中的狀態值都是響應式的,狀態值一旦被修改,全部引用該值的地方就會自動更新。vue

  經過一個商品列表的栗子來演示一下Vuex的用法,首先看一下使用props進行傳參的用法,添加兩個組件:ProductListOne和ProductListTwe,兩個組件都展現商品的名字和價格,組件代碼以下:vuex

<!-----------------------------App.vue--------------------------------->
<template>
  <div id="app">
    <production-list-one :products="products"></production-list-one>
    <production-list-twe :products="products"></production-list-twe>
  </div>
</template>

<script>
import ProductListOne from "./components/ProductListOne.vue";
import ProductListTwe from "./components/ProductListTwe.vue";
export default {
  name: "app",
  components: {
    "production-list-one": ProductListOne,
    "production-list-twe": ProductListTwe
  },
  data() {
    return {
      products: [
        { id: 1, name: "電視", price: 2000 },
        { id: 2, name: "電腦", price: 5000 },
        { id: 3, name: "空調", price: 1500 },
        { id: 4, name: "冰箱", price: 3000 }
      ]
    };
  }
};
</script>

<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="production-list-one">
  <h4>Product list one </h4>
  <ul>
    <li  v-for="product in products" v-bind:key="product.id">
      <span class=name>{{product.name}}</span>
      <span class=price>¥{{product.price}}</span>
    </li>
  </ul>
  </div>
</template>

<script>
export default {
  props:["products"],
  data() {
    return {
    
    };
  }
};
</script>


<!-----------------------------ProductListTwe.vue--------------------------------->
<template>
  <div id="product-list-twe">
    <h4>Product list twe</h4>
    <ul>
      <li v-for="product in products" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  props: ["products"],
  data() {
    return {};
  }
}
</script>

  運行程序後,顯示以下:npm

state

   前邊父組件App.vue中的數據products經過props傳到這兩個組件中,如今咱們使用vuex來保存products,首先使用 cnpm install vuex --save 添加vuex包,而後添加一個store.js文件來保持數據,該文件代碼以下,其中store對象做爲「惟一數據源」而存在,因此每一個應用都僅僅包含一個store實例,在store對象中state用於保存原始數據:app

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
  state: {
    products: [
        {id: 1,name: "洗衣機",price: 2000},
        {id: 2,name: "電腦",price: 5000},
        {id: 3,name: "空調",price: 1500},
        {id: 4,name: "冰箱",price: 3000}
    ]
  }
}) 

  接着在main.js中引入vuex,以下:異步

import Vue from 'vue'
import App from './App.vue' import {store} from './store/store.js'
new Vue({
 store:store,
  el: '#app',
  render: h => h(App)
})

  而後修改組件以下:this

<!-----------------------------App.vue--------------------------------->
<template>
  <div id="app">
    <production-list-one></production-list-one>
    <production-list-twe></production-list-twe>
  </div>
</template>

<script>
import ProductListOne from "./components/ProductListOne.vue";
import ProductListTwe from "./components/ProductListTwe.vue";
export default {
  name: "app",
  components: {
    "production-list-one": ProductListOne,
    "production-list-twe": ProductListTwe
  },
  data() {return {};}
};
</script>



<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="production-list-one">
  <h4>Product list one </h4>
  <ul>
    <li  v-for="product in saleProducts1" v-bind:key="product.id">
      <span class=name>{{product.name}}</span>
      <span class=price>¥{{product.price}}</span>
    </li>
  </ul>
  </div>
</template>

<script>
export default {
    computed:{
      saleProducts1(){
        return this.$store.state.products
      }
  }
};
</script>

<!-----------------------------ProductListTwe.vue--------------------------------->
<template>
  <div id="product-list-twe">
    <h4>Product list twe</h4>
    <ul>
      <li v-for="product in salesProducts2" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  computed:{
      salesProducts2(){
        return this.$store.state.products
      }
  }
};
</script>

  運行程序後,顯示和使用props傳值的效果同樣,兩個列表組件的數據源都不在經過props獲取,而是經過 this.$store.state.products 從vuex中獲取,到這裏咱們已經完成了vuex的簡單使用。spa

getters

  在vuex中state屬性保存的是原始狀態,有時候咱們須要展現的數據是一些派生狀態,就是是對state中的原始數據作必定的邏輯處理後數據,如咱們展現的商品列表的價格是打八折後的價格。對應這種需求,咱們能夠先在組件中使用this.$store.state.xxx獲取到原始數據而後在組件中直接寫邏輯代碼進行處理,可是若是邏輯處理獲取的數據在多個組件中都要使用的話,這就須要在每一個組件中都重複一遍邏輯代碼。爲了減小代碼冗餘,咱們可使用getter屬性把"八折"這個共享的邏輯提取出來,實現很簡單,直接看代碼吧調試

修改store.js,代碼以下:code

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
  state: {
    products: [
        {id: 1,name: "洗衣機",price: 2000},
        {id: 2,name: "電腦",price: 5000},
        {id: 3,name: "空調",price: 1500},
        {id: 4,name: "冰箱",price: 3000}
    ]
  },
  getters: {
    saleProducts(state) {
        var saleProducts=state.products.map((product)=>{
            return {id:product.id,name:`產品${product.id}:${product.name} `,price:product.price*0.8}
        })
        return saleProducts;
    }
  }
}) 

修改組件代碼以下:component

<!-----------------------------App.vue--------------------------------->
<template>
  <div id="app">
    <production-list-one></production-list-one>
    <production-list-twe></production-list-twe>
  </div>
</template>

<script>
import ProductListOne from "./components/ProductListOne.vue";
import ProductListTwe from "./components/ProductListTwe.vue";
export default {
  name: "app",
  components: {
    "production-list-one": ProductListOne,
    "production-list-twe": ProductListTwe
  },
  data() {return {};}
};
</script>

<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="production-list-one">
  <h4>Product list one </h4>
  <ul>
    <li  v-for="product in saleProducts1" v-bind:key="product.id">
      <span class=name>{{product.name}}</span>
      <span class=price>¥{{product.price}}</span>
    </li>
  </ul>
  </div>
</template>

<script>
export default {
  computed:{
    saleProducts1(){
      return this.$store.getters.saleProducts
    }
  },
};
</script>

<!-----------------------------ProductListTwe.vue--------------------------------->
<template>
  <div id="product-list-twe">
    <h4>Product list twe</h4>
    <ul>
      <li v-for="product in saleProducts2" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  computed:{
      saleProducts2(){
       return this.$store.getters.saleProducts
      }
  }
};
</script>

  咱們能夠經過 this.$state.getters.xxx 去獲取getters中的數據,運行程序後結果以下:

 

 

 mutations

  上邊的state用於存儲和獲取原始值,getters負責封裝公共邏輯,獲取計算後的狀態,二者都是獲取數據時使用的。當咱們想修改store中的狀態怎麼實現呢?提交mutations是更改vuex的stroe中狀態的惟一方法。看一個需求:添加一個降價按鈕,每次點擊都會降價指定的金額。

  首先修改store.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
  state: {
    products: [
        {id: 1,name: "洗衣機",price: 2000},
        {id: 2,name: "電腦",price: 5000},
        {id: 3,name: "空調",price: 1500},
        {id: 4,name: "冰箱",price: 3000}
    ]
  },
  getters: {
    saleProducts(state) {
        var saleProducts=state.products.map((product)=>{
            return {id:product.id,name:`產品${product.id}:${product.name} `,price:product.price*0.8}
        })
        return saleProducts;
    }
  },
  mutations:{
    reducePrice:function(state,num){
        state.products.forEach(proudcts=>proudcts.price-=num)
      }
  }
}) 

  修改組件以下:

<!-----------------------------App.vue--------------------------------->
<template>
  <div id="app">
    <production-list-one></production-list-one>
    <production-list-twe></production-list-twe>
  </div>
</template>

<script>
import ProductListOne from "./components/ProductListOne.vue";
import ProductListTwe from "./components/ProductListTwe.vue";
export default {
  name: "app",
  components: {
    "production-list-one": ProductListOne,
    "production-list-twe": ProductListTwe
  },
  data() {return {};}
};
</script>


<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="production-list-one">
  <h4>Product list one </h4>
  <ul>
    <li  v-for="product in saleProducts1" v-bind:key="product.id">
      <span class=name>{{product.name}}</span>
      <span class=price>¥{{product.price}}</span>
    </li>
  </ul>
  <button @click="reducePrice(10)">商品降價</button>
  </div>
</template>

<script>
export default {
//使用vuex中的getters
  computed:{
    saleProducts1(){
      return this.$store.getters.saleProducts
    }
  },

//使用mutations降價
  methods:{
     reducePrice:function(num){
      this.$store.commit('reducePrice',num)
    }
  }
};
</script>

<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="product-list-twe">
    <h4>Product list twe</h4>
    <ul>
      <li v-for="product in saleProducts2" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  computed:{
      saleProducts2(){return this.$store.getters.saleProducts}
  }
};
</script>

  刷新頁面,效果以下:

actions

  使用mutations咱們能夠直接修改store中的原始值,可是官方不推薦這種作法,官方推薦須要修改store中的值時,咱們首先要提交一個action,在action中提交mutation來修改狀態值。這種方式方便咱們進行調試,同時容易實現異步操做。仍是使用降價的栗子,咱們點擊按鈕2秒後實現降價。action的參數是一個和store具備相同方法和屬性的context對象,咱們能夠經過 context.state 和 context.getters 來獲取state和getters,也可使用 context.commit(mutation,payload) 來提交一個mutation,使用首先修改store.js:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
  state: {
    products: [
        {id: 1,name: "洗衣機",price: 2000},
        {id: 2,name: "電腦",price: 5000},
        {id: 3,name: "空調",price: 1500},
        {id: 4,name: "冰箱",price: 3000}
    ]
  },
  getters: {
    saleProducts(state) {
        var saleProducts=state.products.map((product)=>{
            return {id:product.id,name:`產品${product.id}:${product.name} `,price:product.price*0.8}
        })
        return saleProducts;
    }
  },
  mutations:{
    reducePrice:function(state,num){
        state.products.forEach(proudcts=>proudcts.price-=num)
      }
  },
//兩秒後在提交reducePrice,context是上下文至關於組件中的this.$store actions:{ reducePriceAction:(context,num)=>{ setTimeout(function(){ context.commit("reducePrice",num) },2000) } } })

  修改組件以下:

<!-----------------------------App.vue--------------------------------->
<template>
  <div id="app">
    <production-list-one></production-list-one>
    <production-list-twe></production-list-twe>
  </div>
</template>

<script>
import ProductListOne from "./components/ProductListOne.vue";
import ProductListTwe from "./components/ProductListTwe.vue";
export default {
  name: "app",
  components: {
    "production-list-one": ProductListOne,
    "production-list-twe": ProductListTwe
  },
  data() {return {};}
};
</script>

<!-----------------------------ProductListOne.vue--------------------------------->
<template>
  <div id="production-list-one">
    <h4>Product list one</h4>
    <ul>
      <li v-for="product in saleProducts1" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
    <button @click="reducePrice(10)">商品降價</button>
  </div>
</template>

<script>
export default {
  //使用vuex中的getters
  computed: {
    saleProducts1() {
      return this.$store.getters.saleProducts;
    }
  },

  //使用action降價,每次降價20元
  methods: {
    reducePrice: function(num) {
      this.$store.dispatch("reducePriceAction", num);
    }
  }
};
</script>


<!-----------------------------ProductListTwe.vue--------------------------------->
<template>
  <div id="product-list-twe">
    <h4>Product list twe</h4>
    <ul>
      <li v-for="product in saleProducts2" v-bind:key="product.id">
        <span class="name">{{product.name}}</span>
        <span class="price">¥{{product.price}}</span>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  computed:{
      saleProducts2(){
       return this.$store.getters.saleProducts
      }
  }
};
</script>

  實現效果以下:

   本文是vuex的簡單入門筆記,更高的特性在之後開發中遇到了在作研究,若是文中有錯誤但願你們能夠指出,我會及時改正。

相關文章
相關標籤/搜索