組件是Vue最強大的功能之一,而組件實例的作用域是相互獨立的,意味着不同組件之間的數據是無法相互使用。組件間如何傳遞數據就顯得至關重要,這篇文章主要是介紹Vuex。儘量以通俗易懂的實例講述這其中的差別,希望對小夥伴有些許幫助。
一、Vuex 是什麼?
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
二、什麼是“狀態管理模式”?
一個簡單的 Vue 計數應用開始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
這個狀態自管理應用包含以下幾個部分:
state,驅動應用的數據源;
view,以聲明方式將 state 映射到視圖;
actions,響應在 view 上的用戶輸入導致的狀態變化。
state的數據會在 view上顯示出來,用戶會根據 view 上的內容進行操作,從而觸發 actions,接着再去影響 state(vue 是單向數據流的方式驅動的)。
當我們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞。下面的圖,是把組件的共享狀態抽取出來,以一個全局單例模式管理。
三、核心概念
1. state
state:頁面狀態管理容器對象。集中存儲Vue components中data對象的零散數據,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取。
<div>
{{ $store.state.count }}
</div>
console.log(this.$store.state.count)
2. getters
getters:Vuex 允許我們在 store 中定義“getter”(可以認爲是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被重新計算。(getters從表面是獲得的意思,可以把他看作在獲取數據之前進行的一種再編輯,相當於對數據的一個過濾和加工。getters就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被重新計算。)
定義getter:
getters: {
done(state) {
return state.count + 1;
},
}
3. mutations
mutations:更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作爲第一個參數:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態
state.count++
}
}
})
組件通過commit提交mutations的方式來請求改變state
this.$store.commit('increment')
提交載荷(Payload)
mutations方法中是可以傳參的,具體用法如下:
mutations: {
// 提交載荷 Payload
add(state, n) {
state.count += n
}
},
this.$store.commit('add', 10)
4.Action
Action:類似於 mutation,不同在於Action 提交的是 mutation,而不是直接變更狀態;Action 可以包含任意異步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
不同於mutations使用commit方法,actions使用dispatch方法。
this.$store.dispatch('incrementAsync')
context
context是與 store 實例具有相同方法和屬性的對象。可以通過context.state和context.getters來獲取 state 和 getters。
以載荷形式分發
incrementAsyncWithValue (context, value) {
setTimeout(() => {
context.commit('add', value)
}, 1000)
}
this.$store.dispatch('incrementAsyncWithValue', 5)
5.Module
由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常複雜時,store 對象就有可能變得相當臃腫。
爲了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
模塊的局部狀態
對於模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 這裏的 `state` 對象是模塊的局部狀態
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
Vuex計數器的例子:
在src目錄下創建一個store文件夾。
store/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0,
show: ''
},
getters: {
counts: (state) => {
return state.count
}
},
mutations: {
increment: (state) => {
state.count++
},
decrement: (state) => {
state.count--
},
changVal: (state, v) => {
state.show = v
}
}
})
export default store
state就是我們的需要的狀態,狀態的改變只能通過提交mutations,例如:
increase() {
this.$store.commit('increment')
}
帶有載荷的提交方式:
changObj () {
this.$store.commit('changVal', this.obj)
}
載荷也可以是一個對象,這樣可以提交多個參數。
changObj () {
this.$store.commit('changVal', {
key:''
})
}
在main.js中引入store.js
import store from './store/store'
export default new Vue({
el: '#app',
router,
store,
components: {
App
},
template: '<App/>'
})
在組件中使用
在組建可以通過$store.state.count獲得狀態
更改狀態只能以提交mutation的方式。
<div class="store">
<p>
{{$store.state.count}}
</p>
<button @click="increase"><strong>+</strong></button>
<button @click="decrease"><strong>-</strong></button>
<hr>
<h3>{{$store.state.show}}</h3>
<input
placeholder="請輸入內容"
v-model="obj"
@change="changObj"
clearable>
</input>
</div>
</template>
<script>
export default {
data () {
return {
obj: ''
}
},
methods: {
increase() {
this.$store.commit('increment')
},
decrease() {
this.$store.commit('decrement')
},
changObj () {
this.$store.commit('changVal', this.obj)
}
}
}
</script>