本文介紹了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ù)。
- 首先我們需要安裝vuex
npm install --save-dev vuex
創(chuàng)建store/index.js
把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)用更加方便。

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

源代碼:
usage
git clone git@github.com:80666881/vuex-tutorial.git
npm install
npm start
