多組件數據共享: Vuex
我們知道每一個組件都有自己的 data,那麼多個組件如何共享數據?這就引出了 state 的概念,可以把多個組件共有的屬性統一由state進行管理。但是組件並不能直接訪問或操作state裏的數據源,而是通過 mutations 來對數據源進行操作。而改變數據的行爲觸發由 Actions 來完成,Vue 爲 actions的觸發提供了一個 commit 的概念,由action 觸發通過 mutations 對數據進行操作,從而改變 state 數據源。
那麼 vue 的組件(components)如何操作 state 數據源呢?components 通過鼠標、鍵盤等交互行爲觸發 (Dispatch) Vuex 的Actions,Actions 對操作進行提交,找到對應的 mutations,進而對 state進行改變。這就是 Vuex 的完整數據流。上代碼:
首先,安裝 vuex
npm i vuex
新建 store.js
touch src/store.js
Vue中可以存放數據的有組件的 data、computed、props,而state就是放在 computed裏。
// store.js
import Vue from 'vue' //引入 vue
import Vuex from 'vuex' // 引入vuex
Vue.use(Vuex) // 將Vue 的全局組件 Vuex 加入到 Vue 的運行框架中
const state = { //數據源
count: 1
}
const mutations = { //定義數據操作方法
increment(state) { // 傳入數據源
state.count++ // 操作數據源
},
decrement(state) { // 同上
state.count--
}
}
// actions 不能直接修改數據, 它會接收 vue 組件的用戶行爲,進一步觸發(commit)操作,由mutations 對數據源state進行修改。補充說明:commit 攜帶一個參數,這個參數就是 mutations 裏的某一個行爲,這個行爲會對數據源state進行操作,並使vue組件重新render數據變化
const actions = { // 定義數據源操作行爲
increment: ({
commit
}) => { // 解構賦值
commit('increment') // 對應 mutations裏的increment,告訴mutations 進行increment 操作
},
decrement: ({
commit
}) => { // 解構賦值
commit('decrement') // 對應 mutations裏的decrement,告訴mutations 進行decrement 操作
}
}
// 導出 vuex 的實例
export default new Vuex.Store({
state,
mutations,
actions
})
項目入口文件:
import Vue from 'vue'
import App from './App'
import store from './store' // 引入 store 文件
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
// 通過在根實例中註冊 store 選項,把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件,子組件可以通過 this.$store 訪問
}).$mount('#app')
vue組件(component):
在組件中分發 Actions:
在組件中使用 this.$store.dispatch('xxx')
分發 action,或者使用 mapActions
輔助函數將組件的 methods 映射爲 store.dispatch
調用(需要先在根節點注入 store
):
// vuex.vue
<template>
<div class="vuex">
vuex {{$store.state.count}}
<!-- 用 $store 取 store 的state數據源 -->
<button @click="increment">增加</button>
<button @click="decrement">刪減</button>
</div>
</template>
<script>
import { mapActions } from 'vuex' // 引入 vuex的 actions
export default {
methods: mapActions([ // 將methods 映射爲 vuex 的 actions
'increment',
'decrement'
])
}
</script>
延伸:Vuex 模塊化
先創建好模塊目錄及模塊文件
mkdir src/store // 創建 store 目錄
mkdir src/store/modules // 創建store 模塊目錄
touch src/store/modules/a.js // 創建 a 模塊
touch src/store/modules/b.js // 創建 b 模塊
touch src/store/index.js // 創建 store 模塊入口文件
touch src/components/a.vue // 創建子組件a
touch src/components/b.vue // 創建子組件b
// a.js
const state = {
money: 10
}
const mutations = {
add(state) {
state.money++
},
reduce(state) {
state.money--
}
}
const actions = {
add: ({
commit
}) => {
commit('add')
},
reduce: ({
commit
}) => {
commit('reduce')
}
}
export default {
namespaced: true, //開啓命名空間
state,
mutations,
actions
}
// b.js
const state = {
count: 1
}
const mutations = {
add(state, param) { // mutations 從 actions 上接收 param 參數並且使用
state.count += param
},
reduce(state) {
state.count--
}
}
const actions = {
add: ({
commit
}, param) => { // 這裏param參數是從用戶交互action行爲上接收的參數
commit('add', param)
},
reduce: ({
commit
}) => {
commit('reduce')
}
}
export default {
namespaced: true, //開啓命名空間
state,
mutations,
actions
}
// index.js store 入口文件
import Vue from 'vue' // 引入 vue
import Vuex from 'vuex' // 引入 vuex
import money from './modules/a' // 引入模塊a
import count from './modules/b' // 引入模塊b
Vue.use(Vuex) // 引入Vue的全局模塊 Vuex
export default new Vuex.Store({
modules: { // 導出模塊
money, // 子模塊a
count // 子模塊b
}
})
// a.vue
<template>
<div class="a">
<!-- 注意:這裏從money模塊下取state數據 money -->
page a{{$store.state.money.money}}
<button @click="add">添加</button>
<button @click="reduce">刪減</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: mapActions('money', ['add', 'reduce'])
// 這裏mapActions 接收兩個參數,第一個參數是store 子模塊名,第二個參數是actions的行爲
}
</script>
// b.vue
<template>
<div class="b">
<!-- 注意:這裏從count模塊下取state數據 count-->
page b{{$store.state.count.count}}
<!-- 想要mutation 對數據的操作可控,可以在用戶交互的 action上傳參,在b.js 的actions 接收參數 -->
<button @click="add(2)">添加</button>
<button @click="reduce">刪減</button>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: mapActions('count', ['add', 'reduce'])
// 這裏mapActions 接收兩個參數,第一個參數是store 子模塊名,第二個參數是actions的行爲
}
</script>
// App.vue
<template>
<div id="app">
<!--在dom裏引入組件a和b-->
<pagea />
<pageb />
</div>
</template>
<script>
import pagea from './components/a' // 引入組件a
import pageb from './components/b' // 引入組件b
export default {
name: 'app',
components: {
pagea, // 註冊組件 a
pageb //註冊組件 b
}
}
</script>
// 項目主入口文件 main.js
import Vue from 'vue'
import App from './App'
import store from './store/index' // 引入 store 模塊入口文件
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store // 把Vuex的實例引入Vue實例中
}).$mount('#app')