มาทำความรู้จัก Vuex ORM กันดีกว่า

Cover image

ผมเชื่อ Vue developer หลายๆคนคงจะเคยได้ใช้งาน Vuex กันอยู่บ้าง และผมก็เชื่อว่าหลายคนคงหงุดหงิดกับการที่ต้องมาคอยใช้ this.$store.dispatch() เพราะมันไม่สวยงามเลย แต่เราก็ต้องยอมแลกเพราะมันทำให้เราจัดการ state ของข้อมูลได้ แต่ปัญหาเหล่านี้จะหมดไปถ้าคุณได้ลองใช้ Vuex-ORM

Vuex-ORM คืออะไร

Vuex ถือกำเนิดขึ้นมาเพื่อใช้จัดการกับ state ทั้งหลายใน app ของเราไม่ว่าจะเป็น store, action หรือ mutation ผมเป็นคนนึงที่ขาด Vuex ถ้าจะต้องทำ Vue Application แล้วต้องมาจัดการอะไรพวกนี้เองหรือไม่จัดการมันเลยปล่อยมันอยู่ตาม view หรือ component กระจัดกระจาย ตอนแก้ผมคงปวดหัวแน่นอน แต่ถึงแม้มันจะเทพอย่างไรก็ตาม มันก็ต้องแลกด้วยพิธีและขั้นตอนที่ก็จะทำ code มันดูยุบยับหน่อย โดยเฉพาะมือใหม่หลายคนแค่เริ่มใช้ก็งงแล้ว

Vuex-ORM จึงได้คลานตามกันออกมา โดยเราจะยังคงความเจ๋งของ Vuex ไว้แล้วเสริมด้วยความเป็น ORM

ORM: object-relational mapping

พอเสริมความเป็น ORM เข้ามาแล้วมันทำให้ code นั้นเขียนง่ายและอ่านง่าย มอง object ที่เข้ามาใน store ให้เป็น Model สามารถทำ CRUD ได้เหมือน database โดยไม่ต้องเขียน code เพิ่มด้วยไงล่ะ (ทำไมมันดีจัง ทำไมมันดีกว่าชาวบ้านเขา!) ใครยังนึกภาพไม่ออกเดี๋ยวผมมีตัวอย่าง

วิธีจัดการแบบเดิม

ตามปกติเวลาเราจะใช้ Vuex ถ้าเราอยากทำ app Todo list เราก็คงต้องเขียนอะไรประมาณนี้ใช่ไหมครับ

ที่ store/index.js ประกาศใน state และสร้าง actions และ mutations function ประมาณนี้

store: {
  state: { todos: [] },
  actions: {
      toggleStatus({ commit, state }, todoId) {
        const todo = state.todos.find(todo => {
          return todo.id === id
        });
        commit(SET_TODO_STATUS, { id: todoId, status: !todo.done});
      }
  },
  mutations: {
    SET_TODO_STATUS(state, payload) {
      const todo = state.todos.find(todo => {
          return todo.id === payload.id
      });
      todo.done = payload.status;
    }
  }
}

ที่ Components/TodoList.vue

<template>
  <todo-item 
    v-for="todo in todos"
    :key="todo.id"
    :todo="todo"
    @click="toggleStatus(todo.id)"
  />
</template>
<script>
  ...
  export default {
    ...
    computed: {
      todos() {
        return this.$store.state.todos;
      }
    },
    methods: {
      toggleStatus(id) {
        this.$store.dispatch('toggleStatus', id);
      }
    }
  }
</script>

ลองนึกภาพ TodoList ที่มันต้องมีทั้ง create, update, delete เพิ่มมาอีกสิครับยาวอยู่นะ

ถ้าพึ่งบารมี Vuex-ORM ล่ะจะเป็นอย่างไร

ขั้นแรกเราก็สร้าง Model กันก่อนครับ กำหนดไปก่อนเลยว่ามี field อะไรบ้างแล้วจะให้มันอยู่ใน entity ไหน

store/models/Todo.js

import { Model } from '@vuex-orm/core';

export default class Todo extends Model {
    static entity = 'todos';

    static fields() {
      return {
        id: this.string(''),
        title: this.string(''),
        done: this.boolean(false),
      };
    }
}

store/index.js ก็มา register ตัว Model ที่เราสร้างไว้เมื่อสักครู่กับ database ของ Vuex-ORM

import Vue from 'vue';
import Vuex from 'vuex';
import VuexORM from '@vuex-orm/core';
import Todo from './models/Todo';

Vue.use(Vuex);

const database = new VuexORM.Database();
database.register(Todo);

const store = new Vuex.Store({
  plugins: [VuexORM.install(database)],
});

export default store;

ส่วนของ component code จะน่ารักขึ้นเยอะเลย

<template>
  <div>
    <todo-item
      v-for="todo in todos"
      :key="todo.id"
      :todo="todo"
      @clicked="toggleStatus"
    />
  </div>
</template>

<script>
import TodoItem from '../components/TodoItem.vue';
import Todo from '../store/models/Todo';
export default {
  ...
  computed: {
    todos() {
      return Todo.all();
    },
  },
  methods: {
    toggleStatus(id) {
      const todo = Todo.find(id);
      Todo.update({
        where: id,
        data: { done: !todo.done },
      });
    },
  },
};
</script>

สังเกตไหมครับว่า code มันดูเป็นผู้เป็นคนมากขึ้น เราสามารถเรียก Todo ได้แบบนี้เลย

//แบบเดิม
this.$store.dispatch('toggleStatus', id);

//แบบ vuex-orm
const todo = Todo.find(id);
Todo.update({
  where: id,
  data: { done: !todo.done },
});

มันเหมือนจะยาวขึ้นนะ แต่ผมว่ามันเข้าใจง่ายกว่ามาก แถมนอกจาก update แล้วยังมี create, insert, delete ให้ด้วยนะครับ หรือแม้แต่จะ query แบบนี้

//หา todo ที่ยังไม่ done มาทั้งหมด
const todos = Todo.query().where('done', false).get();

ก็ทำได้โดยที่เราไม่ต้องไปเขียน function query เลยครับ Vuex-ORM เขามี Query Builder มาให้ในตัวแล้วสะดวกมากๆ

มี plugin ด้วยนะ

นอกเหนือไปจากความสบายที่บอกไปแล้ว ยังมี plugin อีกนะ (plugin ของ plugin) แน่นอนว่าปัจจุบัน app เราก็ต้องทำงานกับ API อยู่แล้ว ตัวที่น่าจะได้ใช้กันก็มี

  • Vuex ORM Axios - สำหรับใครที่ทำงานกับ RESTful API
  • Vuex ORM GraphQL - สำหรับใครที่ใช้ GraphQL ก็มีด้วย แล้วตัวนี้เจ๋งขนาดที่มันเขียน query ให้เราได้เองเลยแหละ

สำหรับใครที่อยากลองไปศึกษาเพิ่มเติมก็สามารถเข้าไปดูได้ที่ Github repo ของผู้พัฒนาได้เลยครับ มีให้อ่านครบทุกอย่าง งงตรงไหนไปเปิดหาใน issue ใน repo ก่อน ถ้าไม่เจอก็หาใน StackOverflow ต่ออีกที แต่ถ้ายังงงจริงๆ ก็ inbox มาถามผมได้ที่ Facebook Page นะครับ (ถ้าผมตอบได้นะ 5555)

https://github.com/vuex-orm/vuex-orm

บทความใกล้เคียง