老規矩先安裝
npm install vuex --save
在看下面內容之前 你應該大概的看了一邊vuex官方的文檔對vuex有個大概對了解
首先
vuex 是什么?
vuex 是屬於vue中的什么,它在項目中扮演着一個什么樣的角色,起到什么作用,在項目中我是否要用到vuex。
官方文檔對vuex的解讀是:Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。
如果你是在開發一個大型的應用程序,組件與組件之間,兄弟組件之間,或者說多層嵌套組件之間,你是無法傳遞數據的。子父組件之間,也只是說通過事件保持數據的一致性,也是很繁瑣的,會變得很不好管理。而vuex就是為了解決這樣的問題,把組件的共享狀態抽取出來,以一個全局單例模式管理。組件樹構成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態或者觸發行為,而且代碼將會變得更結構化且易維護。
新建一個最簡單的store
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
me: {
name: 'huangenai',
age: 22,
sex: '女'
},
},
mutations: {
increment (state) {
state.count++
}
}
})
main.js
import store from './store.js' new Vue({ el: '#app', router, store,//加這個 components: { App }, template: '<App/>' })
State
驅動應用的數據源,組件之間共享的狀態。
上面代碼中的 count 就是一個共享的數據,me 也是一個共享的數據對象。
在組件中獲得state 中的count
this.$store.state.count
newvue.vue
<template> <div> new vue page <p>{{count}}</p> </div> </template> <script> export default { name: 'newvue', data () { return { } }, computed: { count () { return this.$store.state.count } } } </script> <style scoped> </style>
為什么要寫在computed 里面呢,而不是寫在data里面,因為當state.count 變化的時候, 都會重新求取計算屬性,並且觸發更新相關聯的 DOM,如果你不理解可以先去看看computed
當一個組件要引用到多個狀態的時候 ,一個個寫是不是很累。不用擔心,我們有mapState
輔助函數幫助我們生成計算屬性。
在組件中引用
import { mapState } from 'vuex'
<template> <div> <p>{{count}}</p> <p>{{countPlusLocalState}}</p> <p>sex: {{sex}}</p> <p>age: {{myAge}}</p> <p>name: {{name}}</p> </div> </template> <script> import { mapState } from 'vuex' export default { name: 'newvue', data () { return { localCount: 1 } }, computed: { ...mapState({ // 箭頭函數可使代碼更簡練 sex: state => state.me.sex, myAge: state => state.me.age, name: state => state.me.name, // 傳字符串參數 'age' 等同於 `state => state.age` count: 'count', // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數 countPlusLocalState (state) { return state.count + this.localCount } }) } } </script> <style scoped> </style>
假設computed 里面還有別的數據,因為mapstate返回來的是一個對象所以我們這樣寫可以將數據混入
computed: { localComputed () { /* ... */ }, // 使用對象展開運算符將此對象混入到外部對象中 ...mapState({ // ... }) }
Mutation
你說你想修改state的值,this.$store.state.count = 1 這樣可不可以 no no no.... 所以就有了Mutation。
切記Mutation 必須是同步函數!!!
想要修改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。
在上面代碼我們新建一個store里面下的 有一個increment,就可以實現修改state里面的count
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count ++
}
}
})
那么如何調用increment呢??
調用 this.$store.commit(type) 類型叫increment
的 mutation。
this.$store.commit('increment')
我們還可以傳入參數
// ... mutations: { increment (state, n) { this.$state.count = n } }
store.commit('increment', 10)
傳入一個對象
mutations: { increment (state, data) { state.count += data.count } }
store.commit('increment', { count: 10 })
mutations可以修改store中的狀態,但是並不是說隨隨便便修改的,比如說🥚狀態是對象的時候,你突然重新賦值過去一個新對象,原來的屬性沒了,真怎么行。
Vuex 的 store 中的狀態是響應式的,那么當我們變更狀態時,監視狀態的 Vue 組件也會自動更新。
比如 上面代碼的 state 內有個對象me 我可以新增刪除里面的屬性嗎,怎么操作呢。
最好就是一開始就定義好所有的屬性。
如果你非要新增屬性,也提供了方法。
1. Vue.set(me, 'height','156cm')
2. state.me = { ...state.me, height: '156cm' }
store.js
...
mutations: {
increment (state) {
state.count++
},
setWeight (state) {
Vue.set(state.me, 'weight', '45kg')
},
setHeight (state) {
state.me = { ...state.me, height: '156cm' }
}
}
newvue.vue 中調用
...
methods: {
setWeight () {
this.$store.commit('setWeight')
alert(JSON.stringify(this.$store.state.me))
},
setHeight () {
this.$store.commit('setHeight')
alert(JSON.stringify(this.$store.state.me))
}
}
當mutations中的類型多了起來,我們在組件中要commit 某一個類型 還要去找 再復制過來 是不是很不好管理。
所以我們可以使用常量替代 mutation 事件類型,新建一個
mutation-types.js
export const SET_WEIGHT = 'SET_WEIGHT'
export const SET_HEIGHT = 'SET_HEIGHT'
store.js
import * as types from './metations-types'
...
mutations: {
increment (state) {
state.count++
},
[types.SET_WEIGHT] (state) {
Vue.set(state.me, 'weight', '45kg')
},
[types.SET_HEIGHT] (state) {
state.me = { ...state.me, height: '156cm' }
}
}
newvue.vue
....
methods: {
setWeight () {
this.$store.commit(types.SET_WEIGHT)
alert(JSON.stringify(this.$store.state.me))
},
setHeight () {
this.$store.commit(types.SET_HEIGHT)
alert(JSON.stringify(this.$store.state.me))
}
}
state中有mapState, Mutations中也有mapMutations
使用 mapMutations
輔助函數將組件中的 methods 映射為 this.$store.commit
調用
newvue.vue
先引用
import { mapMutations } from 'vuex'
...
setWeight () {
this.$store.commit(types.SET_WEIGHT)
alert(JSON.stringify(this.$store.state.me))
},
setHeight () {
this.$store.commit(types.SET_HEIGHT)
alert(JSON.stringify(this.$store.state.me))
},
...mapMutations([
'increment'
// 將 `this.increment()` 映射為 this.$store.commit('increment')`
])
}
getter
getter 又是什么 ,在store中。什么時候用到他。
上述我們可以用 this.$store.state.count 能拿到 store的state ,而getter 則是拿store中的計算屬性。
比如store 中的state 中的list
在項目中我們有多個組件需要通過過濾拿到list中 age 大於等於22的對象,如果沒有getter 我們需要在每個組件中都要定義一個函數過濾好在返回值,或者抽出來一個公共的方法哪個組件需要的時候就導入這個方法,再進行調用,也是很麻煩的一件事。
在這里我們可以用到store 里的getter,並通過屬性訪問獲得過濾后的數據
store.js
export default new Vuex.Store({
state: {
...
list: [{name: 'hea', age: 22}, {name: 'cpq', age: 23}]
},
mutations: {
...
},
getters: {
ageFilter: state => {
return state.list.filter(item => item.age>=23)
}
}
})
newvue.vue
....
computed: {
list () {
return this.$store.getters.ageFilter
}
}
在getter中,Getter 也可以接受其他 getter 作為第二個參數。
例子
...
getters: {
ageFilter: state => {
return state.list.filter(item => item.age >= 23)
},
listCount (state,getters){
return getters.ageFilter.length
}
}
newvue.vue中調用
...
computed: {
...
listCount () {
return this.$store.getters.listCount
}
}
通過方法訪問,讓getter返回一個函數,我們可以傳值過去
store.js
...
getters: {
....
getName: (state) => (name) => {
return state.list.find(item => item.name === name)
}
}
newvue.vue
....
computed: {
....
listbyName () {
return this.$store.getters.getName('cpq')
}
}
getter 有mapGetters 輔助函數,將 store 中的 getter 映射到局部計算屬性
newvue.vue
import { mapGetters } from 'vuex'
computed: {
....
// listCount () {
// return this.$store.getters.listCount
// },
...mapGetters([
'listCount'
])
}
在組件中,什么時候通過getter拿到store 狀態,什么時候 直接拿state 想必都清楚了。
Action
其實跟上述的mutation很相似。
但是他不是直接修改state,在上面mutation說過,必須是同步函數。而Action則可以包含任意異步函數,action 是通過提交 mutation,而修改state。
store.js
export default new Vuex.Store({
state: {
...
},
mutations: {
...
},
getters: {
...
},
actions: {
setWeight ({commit}) {
commit(types.SET_WEIGHT)
}
}
Action 通過 this.$store.dispatch
方法觸發
newvue.vue
...
methods: {
...
setWeight () {
this.$store.dispatch('setWeight')
}
}
同樣action 也支持傳參數
action.js
export default new Vuex.Store({
state: {
count: 0,
...
},
mutations: {
increment (state,count) {
state.count = count
},
...
},
getters: {
...
},
actions: {
...
setCount ({commit},data) {
commit('increment',data.count)
}
}
})
newvue.vue
methods: {
...
setCount () {
this.$store.dispatch('setCount', {count: 123})
}
}
為什么要action 直接用mutation豈不更方便,為什么要通過action,又多了一步
因為mutation只是同步函數啊,在很多情況下我們要執行異步操作的。
action可以包含異步函數。
我們假設一個登錄的功能,在action中支持異步函數,在mutation只能是同步函數,所以要用到action
import api from '../api'
...
actions: {
login ({commit}, payload) => {
return new Promise((resolve, reject) => {
api.login(payload, userToken => {
if (userToken && userToken.accessToken) {
commit(types.LOGIN, userToken)
resolve(userToken)
} else {
reject()
}
})
})
}
調用
this.$store.dispatch('login', {username, password}).then(
(res) => {
}).catch()
同樣action也有輔助函數 mapActions
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`
}
}
你還可以action中相互調用
actions: {
...
setCountA ({commit},count) {
commit('increment', count)
},
setCountB ({commit},data) {
dispatch('setCountA',data.count)
}
}
此隨筆乃本人學習工作記錄,如有疑問歡迎在下面評論,轉載請標明出處。
如果對您有幫助請動動鼠標右下方給我來個贊,您的支持是我最大的動力。