Vuex 是一個專爲Vue.js應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 和單純的全局對象有以下兩點不同
- Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地得到高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。
回顧組件
Index.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 './ProductListOne'
import ProductListTwo from './ProductListTwo'
export default {
name: 'Index',
components: {
'product-list-one': ProductListOne,
'product-list-two': ProductListTwo
},
data(){
return {
products: [
{name: 'apple', price: 200},
{name: 'pear', price: 140},
{name: 'banana', price: 20},
{name: 'mango', price: 10},
]
}
}
}
</script>
ProductListOne
<template>
<div>
<h2>1</h2>
<ul>
<li v-for="product in products" :key="product+Math.random()*Date.now()">
<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 scoped>
div{
font-weight: bold;
color: #f0f;
}
</style>
ProductListTwo
<template>
<div>
<h2>2</h2>
<ul>
<li v-for="product in products" :key="product+Math.random()*Date.now()">
<span class="name">{{product.name}}</span>
<span class="price">{{product.price}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['products'],
data(){
return {
}
}
}
</script>
store.js
state
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
products: [
{name: 'apple', price: 200},
{name: 'pear', price: 140},
{name: 'banana', price: 20},
{name: 'mango', price: 10},
]
}
})
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import {store} from './store'
new Vue({
el: '#app',
router,
store,
components: {App},
template: '<App/>'
})
Index.vue
<template>
<div id="app">
<product-list-one></product-list-one>
<product-list-two></product-list-two>
</div>
</template>
<script>
import ProductListOne from './ProductListOne'
import ProductListTwo from './ProductListTwo'
export default {
name: 'Index',
components: {
'product-list-one': ProductListOne,
'product-list-two': ProductListTwo
},
data(){
return {
// 把數據移到store的state裏了
}
}
}
</script>
ProductListOne.vue
<template>
<div>
<h2>1</h2>
<ul>
<li v-for="product in products" :key="product+Math.random()*Date.now()">
<span class="name">{{product.name}}</span>
<span class="price">{{product.price}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
computed: {
products(){
// 接收store的數據
return this.$store.state.products;
}
},
data(){
return {
}
}
}
</script>
改變名字,價格打折
ProductListTwo.vue
v-for="product in saleProducts"
computed: {
saleProducts(){
return this.$store.state.products.map(product=>{
return {
name: `**${product.name}**`,
price: product.price/2
}
});
}
}
如果在組件ProductListOne.vue也要改名打折,複製即可,但成百上千個組建也需要呢?就用到getters了。
getters
store.js
getters: {
saleProducts(state){
return state.products.map(product=>{
return {
name: `**${product.name}**`,
price: product.price/2
}
});
}
}
ProductListOne.vue或其他需要用到的組件
computed: {
saleProducts(){
return this.$store.getters.saleProducts;
}
}
Mutations
store.js
mutations: {
reducePrice: state=>{
state.products.forEach(p=>{
p.price -=1;
})
}
}
組件的methods
<button @click="reductPrice">降價</button>
reducePrice(){
this.$store.commit('reducePrice')
}
Actions
如果把mutations改爲異步,即
mutations: {
reducePrice: state=>{
setTimeout(function(){
state.products.forEach(p=>{
p.price -=1;
})
},3000)
}
}
這樣在頁面上呈現效果(3000ms後)和Vue Devtools Vuex調試(立即)不一致。但是在actions裏面就可以了。
actions: {
reducePrice: context => {
setTimeout(function(){
context.commit('reducePrice'); // mutations的reducePrice
},3000)
}
}
傳參數
mutations 和 actions
mutations: {
reducePrice: (state,num)=>{
state.products.forEach(p=>{
p.price -=num;
})
}
},
actions: {
reducePrice: (context,num) => {
setTimeout(function(){
context.commit('reducePrice',num); // mutations的reducePrice
},3000)
}
}
組件
<button @click="reducePrice(4)">降價</button>
4是實參
methods: {
reducePrice(amount){ // amount是形參
this.$store.dispatch('reducePrice',amount)
}
}
如果 actions 裏面有多個方法呢,不可能完全在methods裏面列舉,就可以在組件中這樣解決。
import {mapGetters, mapActions} from 'vuex'
export default{
computed: {
...mapGetters(['saleProducts'])
},
methods: {
...mapActions(['reducePrice'])
}
}