vuex零基礎(chǔ)使用入門

本文介紹了vue-x的基本用法,案例參考The Net Ninja的youtube視頻。如有不足,請多指教。同樣學(xué)習(xí)之前需要您已經(jīng)掌握vue和es6的基本用法。

創(chuàng)建一個項目

首先需要安裝vue-cli創(chuàng)建vue和vuex的運行環(huán)境

我們要創(chuàng)建什么項目?

我們需要創(chuàng)建的是兩個商品列表,分別展示各自的商品名稱和價格。并且具備更改價格的方法。

用vue進(jìn)行內(nèi)容創(chuàng)建

首先我們先用vue進(jìn)行內(nèi)容創(chuàng)建,我們應(yīng)當(dāng)有兩個商品列表,所以創(chuàng)建了ProductListOne和ProductListTwo兩個組件。

既然是vue的創(chuàng)建方式,所以我們首先用的傳值方式還是props。先把所有數(shù)據(jù)放在父組件App.vue中,再通過props傳遞給子組件。

//App.vue
<template>
  <div id="app">
    <product-list-one :products="products"></product-list-one>
    <product-list-two :products="products"></product-list-two>
  </div>
</template>

<script>
  import ProductListOne from './components/ProductListOne.vue'
  import ProductListTwo from './components/ProductListTwo.vue'

  export default{
    components: {
        'product-list-one':ProductListOne,
        'product-list-two':ProductListTwo
    },
    name: 'app',
    data(){
      return {
        products: [
          {name: 'Banana Skin', price: 20},
          {name: 'Shiny Star', price: 40},
          {name: 'Green Shells', price: 60},
          {name: 'Red Shells', price: 80}
        ]
      }
    }

  }
</script>
//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in products" :key="index">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
  </div>
</template>

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

<style>
</style>
//ProductListTwo
<template>
    <div id="product-list-two">
        <h2>Product List Two</h2>
        <ul>
            <li v-for="product in products" :key="index">
                <span class="name">{{product.name}}</span>
                <span class="price">${{product.price}}</span>
            </li>
        </ul>

    </div>
</template>

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

<style >

</style>

預(yù)覽截圖

central Store

接下來我們看看用vuex怎么管理我們的數(shù)據(jù)。

  1. 首先我們需要安裝vuex
npm install --save-dev vuex
  1. 創(chuàng)建store/index.js

  2. 把App.vue的數(shù)據(jù)移到index.js

//App.vue
<template>
  <div id="app">
    <product-list-one :products="products"></product-list-one>
    <product-list-two :products="products"></product-list-two>
  </div>
</template>

<script>
  import ProductListOne from './components/ProductListOne.vue'
  import ProductListTwo from './components/ProductListTwo.vue'

  export default{
    components: {
        'product-list-one':ProductListOne,
        'product-list-two':ProductListTwo
    },
    name: 'app',
    data(){
      return {
      }
    }

  }
</script>
//store/index.js
import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex) // 不寫這句話瀏覽器控制臺就會報錯,于是我就寫了

export const store = new Vuex.Store({
  state: {
    products: [
      {name: 'Banana Skin', price: 20},
      {name: 'Shiny Star', price: 40},
      {name: 'Green Shells', price: 60},
      {name: 'Red Shells', price: 80}
    ]
  }
})
//修改main.js
import Vue from 'vue'
import App from './App.vue'
import {store} from './store/index.js'

/* eslint-disable no-new */
new Vue({
  store:store,
  el: '#app',
  render: h=>h(App)
})
//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in products">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
      //getCentral store,use computed attribute
    //when we call products,we use the computed method products that return the products in the store's state
    computed:{
      products(){
          return this.$store.state.products;
      }
    }
  }
</script>
//ProductListTwo
<template>
    <div id="product-list-two">
        <h2>Product List Two</h2>
        <ul>
            <li v-for="product in products">
                <span class="name">{{product.name}}</span>
                <span class="price">${{product.price}}</span>
            </li>
        </ul>

    </div>
</template>

<script>
  export default {
    //getCentral store,use computed attribute
    //when we call products,we use the computed method products that return the products in the store's state
    computed:{
      products(){
        return this.$store.state.products;
      }
    }
  }
</script>

預(yù)覽

Getters

假設(shè)一個需求,我們需要對上架的商品進(jìn)行打折處理。就是我們上述的改變價格的方法。

我們可以使用computed來處理products,新建一個saleProducts變量,作為處理后的值。

//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in saleProducts">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    //getCentral store,use computed attribute
    //when we call products,we use the computed method products that return the products in the store's state
    computed: {
      products(){
        return this.$store.state.products;
      },
      saleProducts(){
        var saleProducts = this.$store.state.products.map((product) => {
          return {
            name: '**' + product.name + '**',
            price: product.price / 2
          }
        })
        return saleProducts;
      }
    }
  }
</script>

預(yù)覽:

注意ProductListTwo我們沒有處理

如果我們想要這種saleProducts也利用在其他地方呢?

不可能每個組件都復(fù)制這個函數(shù)吧

所以我們可以把它作為store的一個getter,output在我們需要的地方

//store/index.js

import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex) // 不寫這句話瀏覽器控制臺就會報錯,于是我就寫了

export const store = new Vuex.Store({
  state: {
    products: [
      {name: 'Banana Skin', price: 20},
      {name: 'Shiny Star', price: 40},
      {name: 'Green Shells', price: 60},
      {name: 'Red Shells', price: 80}
    ]
  },
  //由于我們在store里,所以不用寫this.$store,直接把state傳進(jìn)去就ok了
  getters:{
    saleProducts:state=>{
      var saleProducts = state.products.map((product) => {
        return {
          name: '**' + product.name + '**',
          price: product.price / 2
        }
      })
      return saleProducts;
    }
  }
})

而在組件中,直接調(diào)用this.$store.getters.saleProducts就可以了。

//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in saleProducts">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    //getCentral store,use computed attribute
    //when we call products,we use the computed method products that return the products in the store's state
    computed: {
      products(){
        return this.$store.state.products;
      },
      saleProducts(){
          return this.$store.getters.saleProducts
      }
    }
  }
</script>

預(yù)覽

注意ProductListTwo沒有更改


Mutations

how to change the state in the store?

假設(shè)我們需要點擊來改變價格。
我們首先想到的是創(chuàng)建一個按鈕,點擊時觸發(fā)method來改變this.$store.product。
來試試看是否可行。

//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in saleProducts">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
    <button @click="reducePrice">Reduce Price</button>
  </div>
</template>

<script>
  export default {
    //getCentral store,use computed attribute
    //when we call products,we use the computed method products that return the products in the store's state
    computed: {
      products(){
        return this.$store.state.products;
      },
      saleProducts(){
          return this.$store.getters.saleProducts
      }
    },
    methods:{
        reducePrice:function () {
          this.$store.state.products.forEach(product=>{
              product.price -= 1;
          })
        }
    }
  }
</script>

注意:如果在index.js的state前加use:strict,表示使用嚴(yán)格模式,那么這樣直接修改state是不允許的(只能通過mutation修改)

預(yù)覽

雖然是可以更改了,但是有一個問題,就是我們不知道是誰改變了state,我們現(xiàn)在自己寫當(dāng)然知道,要是從外部看似乎看不出來。

所以我們需要用mutation,可以追蹤變化發(fā)生的源頭。

這就是mutation的作用,可以讓我們追溯數(shù)據(jù)更改的源頭,并且在vue dev-tool中可以看到。

cool,所以我們在store/index.js中創(chuàng)建這個mutations(可能有多個mutation,所以這里是復(fù)數(shù))

注意
由于我們在index.js中操作,所以不需要寫this.$store了,需要state時,直接寫state。

//store/index.js
import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex) // 不寫這句話瀏覽器控制臺就會報錯,于是我就寫了

export const store = new Vuex.Store({
  state: {
    products: [
      {name: 'Banana Skin', price: 20},
      {name: 'Shiny Star', price: 40},
      {name: 'Green Shells', price: 60},
      {name: 'Red Shells', price: 80}
    ]
  },
  getters: {
    saleProducts: state => {
      var saleProducts = state.products.map((product) => {
        return {
          name: '**' + product.name + '**',
          price: product.price / 2
        }
      })
      return saleProducts;
    }
  },
  //mutations可以通過vue-devtool進(jìn)行跟蹤
  mutations: {
    reducePrice: state => {
      state.products.forEach(product => {
        product.price -= 1;
      })
    }
  }
})

子組件如何調(diào)用呢?就是commit方法。直接commit('mutation'),
mutation就是我們具體在index.js里創(chuàng)建的mutation名稱。

//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in saleProducts">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
    <button @click="reducePrice">Reduce Price</button>
  </div>
</template>

<script>
  export default {
    computed: {
      products(){
        return this.$store.state.products;
      },
      saleProducts(){
          return this.$store.getters.saleProducts
      }
    },
    methods:{
        reducePrice:function () {
          this.$store.commit('reducePrice')
          //reducePrice是我們定義在store里的mutations里的方法,要用commit觸發(fā)
        }
    }
  }
</script>

預(yù)覽

Actions

使用mutations時有一個問題需要注意,就是不要直接使用異步,比如從服務(wù)器加載數(shù)據(jù).

我們把index.js的mutation更改一下,用setTimeout來模擬異步.看看會發(fā)生什么事。

 mutations: {
    reducePrice: state => {
      setTimeout(function () {
        state.products.forEach(product => {
            product.price -= 1;
          }
        )
      },3000)
    }
  }

預(yù)覽

解決辦法就是使用actions,可以攔截到達(dá)mutation的指令

1.建立一個actions,把異步獲取的方法放在actions

//index.js
  mutations: {
    reducePrice: state => {
      state.products.forEach(product => {
        product.price -= 1;
      })
    }
  },
  actions:{
    //context是上下文,這里類似store
    reducePrice:context=>{
      setTimeout(function () {
        context.commit('reducePrice')
      },2000)
    }
  }

2.組件不直接觸發(fā)mutation,而是通過dispatch一個action,讓action來觸發(fā)mutation,這樣數(shù)據(jù)到來后,mutation才會被觸發(fā),我們也就可以實時看到變化。

//ProductListOne
<template>
  <div id="product-list-one">
    <h2>Product List One</h2>
    <ul>
      <li v-for="product in saleProducts">
        <span class="name">{{product.name}}</span>
        <span class="price">${{product.price}}</span>
      </li>
    </ul>
    <button @click="reducePrice">Reduce Price</button>
  </div>
</template>

<script>
  export default {
    computed: {
      products(){
        return this.$store.state.products;
      },
      saleProducts(){
          return this.$store.getters.saleProducts
      }
    },
    methods:{
        reducePrice:function () {
          this.$store.dispatch('reducePrice')
        }
    }
  }
</script>

3.預(yù)覽

假設(shè)我們每次減價時,不是固定的值,那么我們就需要一個payload,作為參數(shù)傳遞。

1.我們需要在click的時候直接傳入這個參數(shù),比如是4,所以我們先在button這里改

//ProductListOne.js
 <button @click="reducePrice(4)">Reduce Price</button>

2.既然這個reducePrice觸發(fā)的是dispatch,我們dispatch也要改。

實際上就是在function直接傳一個payload而已

//ProductListOne.js
  methods:{
        reducePrice:function (payload) {
          this.$store.dispatch('reducePrice',payload)
        }
    }

3.既然dispatch改了,我們的action和mutation也要改。

//store/index.js

  mutations: {
    reducePrice: (state,payload) => {
      state.products.forEach(product => {
        product.price -= payload;
      })
    }
  },
  actions:{
    //context是上下文,這里類似store
    reducePrice:(context,payload)=>{
      setTimeout(function () {
        context.commit('reducePrice',payload)
      },2000)
    }
  }

這樣每次點擊都減4了。

預(yù)覽

Mapping Actions & Getters

如果我們有很多個getters和actions怎么辦?難道在每一個都在computed里用函數(shù)調(diào)用嗎?
這樣是不是有點繁瑣?

我們可以用mapping來解決。

引入mapActions和mapGetters

//ProductListOne.js
import {mapActions,mapGetters} from 'vuex'

然后使用

<script>
  import {mapActions,mapGetters} from 'vuex'
  export default {
    computed: {
      products(){
        return this.$store.state.products;
      },
      ...mapGetters([
        'saleProducts'
//        'yourNextOne'
      ])
    },
    methods:{
      ...mapActions([
          'reducePrice'
//         'yourNextOne'
      ])
    }
  }
</script>

注意

如果此時瀏覽器無法識別我們的es6語法,比如...mapActions,我們需要安裝babel

npm install babel-preset-stage-2 --save-dev
//.babelrc    配置
{
  "presets":[
    ["env",{"modules":false}],
    ["stage-2"]
  ]
}

結(jié)果當(dāng)然是不變的。只不過當(dāng)我們有多個action和getter的時候,調(diào)用更加方便。

可能大家看暈了,有圖片版本

image.png

源代碼:

myGitHub

usage

git clone git@github.com:80666881/vuex-tutorial.git
npm install
npm start
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容