Vuex คืออะไร
Vuex เป็นตัวช่วยจัดการ Data flow โดยปกติเวลาเขียนเว็บจะต้องมีการใช้งาน State หรือ Data ที่เก็บข้อมูลใน Component อยู่แล้ว ซึ่ง Vuex จะมาช่วยจัดการอะไรพวกนี้ ทําให้ Code ของเราเป็นระบบ และ ข้อมูลทั้งหมดจะไหลไปในทิศทางเดียวกันทั้งระบบ ลดการเขียน Code ซํ้าซ้อน
Vuex ช่วยจัดการ Data flow อย่างไร
ก่อนอื่นมาดูความแตกต่างระหว่างไม่ใช้ Vuex กับใช้ Vuex ตามรูปนี้
จากรูป รูปวงกลมๆแทน Component ลูกศรคือการส่ง Data กันระหว่าง Component
รูปฝั่งซ้ายเป็นการจัดการ Data โดยไม่ใช้ Vuex คือ Component ส่งข้อมูลไปมาหากันโดยตรง ทําให้ข้อมูลกระจายไปอยู่ตาม Component ต่างๆ สิ่งที่เกิดขึ้นคือ Flow ของข้อมูลไม่ไหลไปในทิศทางเดียวกัน ทําให้เราติดตามข้อมูลได้ยาก
รูปฝั่งขวาคือการจัดการ Data โดย Vuex คือ Data ทั้งหมดจะถูกเก็บไว้ใน Store ที่เดียว Component ไหนจะใช้ก็ข้อมูลก็ไปหยิบมาใช้ และ Component ไหนต้องการเปลี่ยนข้อมูลก็ไปเปลี่ยนที่ Store ซึ่งการทํางานของ Vuex อธิบายตามรูปด้านล่าง
จากรูป การทํางานของ Vuex จะเริ่มต้นจากเกิด Event บางอย่าง ที่ Vue Components เช่น กดปุ่ม แล้ว Vue Component จะ Dispatch ไปยัง Action เพื่อทํา Action บางอย่าง เช่น ไปดึงข้อมูลจาก Backend API เพื่อมา Commit ไปยัง Mutations หลังจากนั้นก็จะ Change ข้อมูล(Mutate) ที่อยู่ใน State เมื่อ State เปลี่ยนก็จะ Render Component ใหม่เพื่อเปลี่ยนแปลงข้อมูลใน Component ต่างๆที่หยิบเอาข้อมูลไปใช้
จะเห็นว่าข้อมูลไหลไปในทิศทางเดียว และเก็บไว้ที่เดียว ถ้าสังเกตดีๆ จะเห็นว่าข้อมูลที่อยู่ใน State จะเปลี่ยนแปลงได้ก็ต่อเมื่อเกิด Action เท่านั้น ทําให้เราสามารถติดตามข้อมูลได้ง่าย ข้อมูลตรงไหนผิด ก็ไปดูที่ Action ได้เลย
ส่วนประกอบของ Vuex
Vuex เป็น library สําหรับสร้าง Store ขึ้นมา โดยในแต่ละ Store จะประกอบไปด้วย 4 ส่วนหลักๆดังนี้
State
State เป็นที่สําหรับเก็บข้อมูลของ Store ซึ่งเราสามารถ Design รูปแบบของการเก็บได้ตามต้องการ
Getters
Getters จะใช้ในกรณีที่ต้องการ Process อะไรบางอย่างก่อนที่จะทําข้อมูลใน Store ไปใช้ พูดง่ายๆ ก็จะคล้ายๆกับ Computed
Action
Action เป็นส่วนที่ทํา Action ต่างๆ เช่น ไปดึงข้อมูลจาก Backend API หรือทํา Business flow อะไรบางอย่าง แล้วจึง commit ไปยัง Mutation(จะ Commit หรือไม่ Commit ก็ได้ ขึ้นอยู่กับ Flow ของเว็บที่ออกแบบไว้)
Mutations
Mutations เป็นส่วนในการเปลี่ยนแปลงข้อมูลของ State ใน Store ที่ได้รับการ Commit มาจาก Action
การใช้งาน Vuex
เพื่อให้ง่ายต่อความเข้าใจ ผมจะยกตัวอย่างเว็บหน้าตาประมาณนี้ครับ
จากรูปด้านบนเราจะสร้าง Component ขึ้นมา 3 Cpmponent โดยแต่ละ Component มีหน้าที่ดังนี้
- Component A ทําหน้าที่แสดงผลข้อมูล
- Component B จะมีปุ่ม INCREASE COUNTER ไว้เพิ่มค่า Counter
- Component C จะมี form สําหรับบันทึกชื่อ และนามสกุล
จากหน้าตาของ UI ด้านบนเราจะออกแบบ Store ของเราได้ดังนี้ คือเราจะแยก Store ออกเป็นสอง Module
- name สําหรับเป็นค่า ชื่อ และนามสกุล
- counter สําหรับเก็บค่า counter
การทํางานของเว็บนี้คือ Component A จะเอาข้อมูลจาก Store name และ Counter ออกมาแสดงผล Component B จะทําหน้าที่ไปเพิ่มค่า Counter ที่อยู่ใน Store counter ส่วน Component C จะทําหน้าที่เปลี่ยนข้อมูล ซื้อ และนามสกุล ที่เก็บอยู่ใน Store name
ในการใช้งาน Vuex เราจะต้องสร้าง Store ขึ้นมาจาก Vuex โดยอันดับแรกสร้าง Folder Store และมี File ดังรูปครับ
จากรูปแต่ละ file จะมีหน้าที่ดังนี้
- index.js เป็น File ที่ใช้สร้าง Store โดยจะร่วมทุกๆ Store module เข้าด้วยกัน
- modules/counter เป็น Store module counter
- modules/name เป็น Store module name
มาดูที่ file index.js ก่อนครับ
import Vue from 'vue'
import Vuex from 'vuex'
import counter from './modules/counter'
import name from './modules/name'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
counter,
name
},
strict: debug
})
จาก code ด้านบน เราได้ Import file counter และ name เข้ามาเพื่อใช้ Vuex สร้างเป็น Store ขึ้นมาแล้ว Export ออกไป แล้วเราจะเอาไปใช้ในขั้นตอนต่อไป
ส่วนภายใน File counter และ name เราจะประกอบไปด้วย state, getters, action, mutations ตามตัวอย่างดังนี้
modules/counter.js
const state = {
currentCounter: 0
}
const getters = { }
const actions = {
increaseCounter: ({ commit, state }, payload) => {
commit('INCREASE_CURRENT_COUNTER')
}
}
const mutations = {
INCREASE_CURRENT_COUNTER (state, payload) {
state.currentCounter++
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
modules/name.js
const state = {
firstName: 'Thiti',
lastName: 'Yamsung'
}
const getters = {
fullName: (state, getters, rootState) => {
return state.firstName + ' ' + state.lastName
}
}
const actions = {
setName: ({ commit, state }, payload) => {
commit('SET_FIRST_NAME', payload.firstName)
commit('SET_LAST_NAME', payload.lastName)
}
}
const mutations = {
SET_FIRST_NAME (state, payload) {
state.firstName = payload
},
SET_LAST_NAME (state, payload) {
state.lastName = payload
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
อธิบาย Code ในส่วนของ Action
ใน Action Vuex จะส่ง commit, state และ payload มาให้เราด้วยซึ่งเราสามารถนํามาใช้ประโยชน์ได้ดังนี้
- commit เราจะใช้ในการเรียก Mutation ขึ้นมาทํางานโดยจะระบุเป็นชื่อของ Mutation และสามารถส่งข้อมูลไปด้วยได้ (Mutation จะได้รับข้อมูลเข้ามาผ่านทาง payload)
- state เป็น state ปัจจุบัน
- payload คือ ข้อมูลที่รับเข้ามา เมื่อเราเรียก Action นี้ขึ้นมาทํางาน
อธิบาย Code ในส่วนของ Mutation
ใน Mutation Vuex จะส่ง state, payload มาให้เราด้วยซึ่งเราสามารถนํามาใช้ประโยชน์ได้ดังนี้
- state เป็น state ปัจจุบัน
- payload คือ ข้อมูลที่รับเข้ามา เมื่อเราเรียก Mutaion นี้ขึ้นมาทํางาน
มาถึงตรงนี้เราได้ Store ขึ้นมาลอยๆอยู่ Vue ของเรายังไม่รู้จัก ขั้นตอนต่อไปเราจะเอา Store นี้เข้าไปใช้งานใน Vue โดยเข้าไปที่ File src/main.js แล้วเพิ่ม Code เข้าไปดังนี้
import Vue from 'vue'
import App from './App.vue'
import Vuetify from 'vuetify'
import store from './store'
import 'vuetify/src/stylus/main.styl'
Vue.use(Vuetify)
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
จาก Code ด้านบนเรา Import store ที่เราสร้างไว้ตอนต้น แล้วมาเพิ่มเข้าไปในขั้นตอนการสร้าง Vue Instance (new Vue(store, …)) เท่านี้เราก็จะสามารถใช้งาน Store ใน Vue ของเราได้แล้ว
ต่อไปเราจะมาดูวิธีการใช้ mapGetters, mapState, mapAction
mapGetters()
mapGetters() เป็น function สําหรับ map getter ใน Store ของเราเข้ามาเป็น Computed เพื่อให้เราสามารถเรียกใช้งานใน Component ได้ง่ายๆ ตัวอย่างดังนี้
import {mapGetters} from 'vuex'
export default {
name: 'A',
computed: {
...mapGetters({
fullName: 'name/fullName'
})
}
}
ในตัวอย่างนี้เราดึง getter fullName ใน Store module name ออกมาใช้งาน
mapState()
mapState() เป็น function สําหรับ map state ใน Store ของเราเข้ามาเป็น Computed เพื่อให้เราสามารถเรียกใช้งานใน Component ได้ง่ายๆ ตัวอย่างดังนี้
import {mapState} from 'vuex'
export default {
name: 'A',
computed: {
...mapState({
currentCounter: store => store.counter.currentCounter
})
}
}
ในตัวอย่างนี้เราดึง state currentCounter ใน Store module counter ออกมาใช้งาน
mapAction()
mapAction() เป็น function สําหรับ map action ใน Store ของเราเข้ามาเป็น Methods เพื่อให้เราสามารถเรียกใช้งานใน Component ได้ง่ายๆ ตัวอย่างดังนี้
import {mapActions} from 'vuex'
export default {
name: 'B',
methods: {
...mapActions({
increaseCounter: 'counter/increaseCounter'
})
}
}
ในตัวอย่างนี้เราดึง action increaseCounter ใน Store module counter ออกมาใช้งาน
เท่านี้เราสามารถใช้งาน Action, state, getter ในทุกๆ Component ของเราได้เลยครับ
สามารถโหลด Project ตัวอย่างเต็มๆ มาลองเล่น ได้ที่นี่ github.com/mrthiti/vuex-example