# 21. 模組化數據
# 數據模組化的用途
可將資料模組化、命名模組
# 模組化步驟
整體步驟如下:
- 建立一個 JS 檔案儲存資料
- 註冊模組
- 使用模組資料
- 取消註冊
# 建立一個 JS 檔案儲存資料
流程:
- 建立一個新的 JS 檔案,儲存 Vuex 模組的資料。例如
ironStore.js
。 - 將要放在該模組中的資料移到這個檔案。
- 加入
namespaced: true
,使用命名空間 (namespace) 區分模組化資料。
//- ironStore.js
import axios from "axios";
export default ({
namespaced: true, // 使用命名空間
state: {
day: 0,
header: {
title: "Vue.js 手牽手,一起嗑光全家桶",
src: "https://vuejs.org/images/logo.svg"
},
list: []
},
mutations: {
SETDAY(state, day) {
state.day = day
},
SETLIST(state, list) {
state.list = list
}
},
actions: {
GETLIST(context, day) {
console.log(context)
context.commit("SETDAY", day)
return axios.get("/list.json").then(res => {
context.commit("SETLIST", res.data)
})
}
},
})
# 註冊模組
- 建立 Vuex 的模組之後,要先引入模組,並註冊模組才能使用。
- 引入模組的寫法有兩種:
- 第一種:引入到 Vue 元件
- 步驟
- 在該 Vue 元件的
script
區塊引入。 - 使用
registerModule()
註冊。
- 在該 Vue 元件的
- 補充
- 這種寫法主要用於單頁或較不會被持續使用的模組。
- 記得要在
beforeDestory
取消註冊。
- 步驟
- 第二種:引入到 Vuex 主要檔案
- 步驟
- 在 Vuex 主要檔案引入。
- 放到
modules
物件之中。
- 補充
- 這種直接註冊的模組通常有跨頁或會被持續使用的需求。
- 這種寫法就不用在
beforeDestory
取消註冊。
- 步驟
- 第一種:引入到 Vue 元件
第一種寫法要先 import
模組,然後使用 registerModule()
註冊,才能使用該模組中的資料:
//- components/Iron.vue
<script>
import ironStore from "../store/ironStore" // 引入
export default {
// 略 ...
mounted(){
let day = parseInt(this.$route.params.day) - 1
this.$store.registerModule("list", ironStore) // 註冊
this.$store.dispatch("list/GETLIST", day)
document.addEventListener("keyup", this.changeHandler)
},
// 略 ...
}
</script>
第二種寫法則是引入到 Vuex 的 index.js,並放到 modules 進行管理:
//- store/index.js
import { createStore } from "vuex";
import list from "./ironStore" // 引入
export default createStore({
state: {},
mutations: {},
actions: {},
modules: {
list // 將引入的模組放到這裡進行管理
},
});
# 使用模組資料
- 如果要取用未模組化的 Vuex 資料,寫法如下:
this.$store.state.header
this.$store.dispatch("GETLIST", day)
- 使用 Vuex 模組內的資料時,要加上此模組註冊的名稱。例如使用名為 list 的模組資料會改成:
this.$store.state.list.header
this.$store.dispatch("list/GETLIST", day)
//- components/Iron.vue
export default {
computed: {
day: {
get(){
return this.$store.state.list.day
},
set(val){
this.$store.commit("list/SETDAY", val)
}
},
header(){
return this.$store.state.list.header
},
list(){
return this.$store.state.list.list
}
},
methods: {
changeHandler(e){
let day = this.day
if(e.keyCode === 37){
day = day == 0 ? day : day - 1
} else {
day = day == 29 ? day : day + 1
}
this.$router.replace({
params: {
day: day + 1
}
})
}
},
mounted(){
let day = parseInt(this.$route.params.day) - 1
this.$store.registerModule("list", ironStore)
this.$store.dispatch("list/GETLIST", day)
document.addEventListener("keyup", this.changeHandler)
},
watch: {
$route(){
let day = parseInt(this.$route.params.day) - 1
this.day = day
}
}
}
如果不需要其他資料來作判斷,更好的位置是在 beforecreate
階段進行註冊。
然而這個範例是在 mounted
階段註冊模組,執行後會報錯主要是因為還沒註冊進去就被打 mutation
。為了讓畫面能正常呈現,我們可以將回傳結果進行一些判斷:
//- components/Iron.vue
computed: {
day: {
get(){
return this.$store.state.list ? this.$store.state.list.day : 0
},
set(val){
this.$store.commit("list/SETDAY", val)
}
},
header(){
return this.$store.state.list ? this.$store.state.list.header : null
},
list(){
return this.$store.state.list ? this.$store.state.list : []
},
}
# 取消註冊
如果是採用第一種註冊模組的寫法,要在元件銷毀前取消模組註冊,並且要記得關掉自己寫的監聽、計時器等事件。例如:
//- components/Iron.vue
beforeDestory(){
this.$store.unregisterModule("list")
document.removeEventListener("keyup", this.changeHandler)
},
如果是 click 這類的事件,可以不用移除監聽。但像計時器等事件就要移除,否則事件會在觸發後多次執行 (因為監聽器越來越多)。
# getters 取得指定天數的資料
實際上,我們只需要呈現指定天數的資料到畫面上而非全部,因此不需要將整個陣列傳入。我們可以先在 Vuex 透過 getters
取得指定天數的資料。
//- store/ironStore.js
getters: {
currentDay(state) {
return state.list[state.day]
}
},
然後回到使用 "ironStore" 模組的 Vue 元件 "Iron.vue",將原本用到 this.$store.state.list
的部分,改為 this.$store.getters["list/currentDat"]
。
//- components/Iron.vue
computed: {
// 以上略
// 原本傳入 state 的整個 list 陣列
list(){
return this.$store.state.list ? this.$store.state.list : []
},
// 改為取得 getters 中的 currentDay (上面的 list 就不需要了)
currentDay(){
return this.$store.state.list ? this.$store.getters["list/currentDay"] : null
}
},
而 template 中用到 list
的部分,也要改為取得 currentDay
的內容。
//- components/Iron.vue
<div class="description" v-if="currentDay">
<div class="menuItem white" >
<span class="number">{{ day + 1 }}</span>
<span class="type">{{ currentDay.type }}</span>
<a :href="currentDay.link" class="title">{{ currentDay.title }}</a>
</div>
</div>