Vuex初識
Vuex的介紹
Vuex是一個專爲Vue.js應用程序開發的狀態管理模式。官網給出Vuex的流程圖如下:
這個流程圖我的解讀如下:
- Components: 頁面中的組件
- Actions: 頁面中要改變數據狀態的動作
- Mutations: vuex中執行數據狀態更改的行爲
- State: 數據倉庫(由vuex來管理狀態的數據組成集合)
我對Vuex這幾個模塊的解讀如下圖所示:
組件(Components)只做頁面的展示,數據全部交給vuex來處理。可以將vuex的state看成前端自己的持久層—數據倉庫。Actions/Mutations監測到組件中執行了對數據(如:msg)進行更改的動作後,去數據倉庫State中保存的數據(msg)進行相應的更改操作。而組件展示數據(msg),均是通過Getters從數據倉庫State中取出相應的數據(msg)。
結合案例說明如下:
項目的完整源碼在我的GitHub項目—learn-vuex中,在git的命令行工具運行如下命令,可以將該項目克隆到本地。
# 在git命令行工具中執行,克隆到本地
git clone https://github.com/zhenye163/learn-vuex
# 加載依賴包
npm install
# 運行該項目
npm run dev
搭建Vue腳手架
搭建Vue腳手架的準備工作詳見: 歡迎入坑
# 生成名稱爲learn-vuex的項目
vue init webpack learn-vuex
cd learn-vuex
# 拉取國內淘寶鏡像,安裝腳手架需要的依賴環境
npm install --registry=https://registry.npm.taobao.org
# 啓動項目
npm run dev
生成的項目結構說明如下:
項目啓動後,訪問http://localhost:8080/#/,如果出現如下頁面,說明vue腳手架搭建成功。
模擬的場景闡明
腳手架已經搭建成功,接下來我們就可以開始折騰了。爲了能夠理解Vuex到底給Vue帶來了什麼好處,我想用本項目模擬一個點餐吃飯(呃,一不小心就暴露了我這吃貨本性)的場景。
場景一 :在家吃飯
場景二:在餐館吃飯
場景再現的準備工作
- 將項目的目錄結構調整如下:
- src
- assets
- logo.png
- components
- HelloWorld.vue
- router
- index.js
- store
- actions.js
- getters.js
- index.js
- mutations.js
- rootState.js
- views
- family.vue
- restaurant.vue
- App.vue
- main.js
- 在src/router/index.js中配置路由如下
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Family from '@/views/family'
import Restaurant from '@/views/Restaurant'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/family',
name: 'Family',
component: Family
},
{
path: '/restaurant',
name: 'Restaurant',
component: Restaurant
}
]
})
- 在src/main.js中導入Element-UI
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
不用Vuex的場景再現—在家吃飯
- 直接在src/views/family.vue中實現
<template>
<div>
在家吃飯
<div>
<br/><br/>
<div>
<div>此時桌子上的菜有:</div>
<br/><br/>
<div v-for="food in foodList" :key="food.id">
<div>菜名:{{food.name}} ,價格:{{food.price}}</div>
</div>
</div>
<br/><br/>
<el-button @click.native.once="addFood()">加個燉排骨</el-button>
</div>
</div>
</template>
<script>
export default {
data () {
return {
foodList: [],
menu: []
}
},
created () {
this.initData()
},
methods: {
initData () {
this.foodList = [
{id: 1, name: '酸辣土豆絲', price: 12},
{id: 2, name: '手撕包菜', price: 12}
]
this.menu = [
{id: 1, name: '酸辣土豆絲', price: 12},
{id: 2, name: '手撕包菜', price: 12},
{id: 3, name: '燉排骨', price: 48},
{id: 4, name: '麻婆豆腐', price: 12},
{id: 5, name: '武昌魚', price: 12}
]
},
addFood () {
this.foodList.push(this.menu[2])
}
}
}
</script>
<style>
</style>
- 訪問localhost:8080/#/family,可看到如下頁面:
點擊點單按鈕可以看到相應的點菜效果。
用Vuex後的場景再現—在餐館吃飯
- 首先在src/main.js中引入vuex的數據倉庫store
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import store from './store/index'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
- 在src/views/restaurant.vue中定義頁面內容如下:
<template>
<div>
在餐館吃飯
<div>
<br/><br/>
<div>
<div>此時桌子上的菜有:</div>
<br/><br/>
<div v-for="food in foodList" :key="food.id">
<div>菜名:{{food.name}} ,價格:{{food.price}}</div>
</div>
</div>
<br/><br/>
<el-button @click.native.once="addFood('燉排骨')">加個燉排骨</el-button>
</div>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
data () {
return {}
},
computed: {
...mapGetters(['foodList'])
},
methods: {
addFood(food){
this.$store.dispatch('addFood', '燉排骨')
}
}
}
</script>
<style scoped>
</style>
頁面中需要展示的數據foodList是從state中通過getters獲取
- 在src/store/rootState.js中定義需要vuex進行管理的數據如下
const state = {
// 點單即將上菜的食物
food: {id: -1, name: '', price: -1},
// 飯桌上已經有哪些食物
foodList: [
{id: 0, name: '酸辣土豆絲', price: 12},
{id: 1, name: '手撕包菜', price: 12}
]
}
export default state
- 在src/store/getters.js中定義如何獲取state中的數據
export const foodList = state => state.foodList
export const menuList = state => state.menuList
- 頁面中有點單(燉排骨)的行爲,由src/store/action.js檢測到
export const addFood = ({commit}, payload) => {
commit({
type: 'cookFood',
// payload告訴我們點的菜是什麼
msg: payload
})
}
- 點菜後,讓後廚做菜上菜(src/store/mutations.js)
export const cookFood = (state, payload) => {
state.food.id = state.foodList.length
state.food.name = payload.msg
// 菜的價格由餐館決定爲28
state.food.price = 28
// 菜做好了,push方法進行上菜
state.foodList.push(state.food)
// 方法執行完,需要將state中的food清零
state.food = {id: -1, name: '', price: -1}
}
- 將vuex的所有部分彙總到(src/store/index.js)中,然後頁面就會知道菜(燉排骨)已經做好並給顧客上菜
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import * as mutations from './mutations'
import state from './rootState'
Vue.use(Vuex)
const store = new Vuex.Store({
actions,
getters,
mutations,
state
})
export default store
- 項目啓動需要先導入vuex依賴包
# 導入vuex需要的依賴包
npm install --save vuex
# 啓動項目
npm run dev
- 訪問http://localhost:8080/#/restaurant,可看到如下頁面:
通過瀏覽器的返回/前進,多次測試發現以下幾個現象:
- 每次進入/family頁面,燉排骨這道菜就消失了。
- 如果沒有強制刷新頁面(F5),每次進入/restaurant頁面,點的燉排骨這道菜還在。
- 強制刷新(F5),進入/restaurant頁面,燉排骨這道菜也消失了。
總結:
vuex在前臺進行了數據的持久化處理,即保存了foodList中的值。這樣是更符合實際情況的。總不能我點菜後,菜已經上桌了,我回家拿個手機,再回到餐館發現桌上的菜(燉排骨)就不見了!!!。而強制刷新頁面(F5),是將所有的組件摧毀然後再次重啓。也就是告訴餐館:“前面點的菜不算,我重新點。”這樣的話,餐館是不會多給你加菜(燉排骨的)。它是不可能做賠本買賣!!!