Project เล็กๆ ลืม Vuex ได้ไหม ?
Tags: vue2, vuecompositionapi, statemanagement, vue3
Published: 2020-11-10 21:56:41

---
title: Project เล็กๆ ลืม Vuex ได้ไหม ?
published: true
date: 2020-09-06 08:55:53 UTC
tags: vue2,vuecompositionapi,statemanagement,vue3
canonical_url: 
---

### Project เล็กๆ ลืม Vuex ได้ไหม ?

หลังจากลองเล่นและศึกษา vue composition api มาสักพัก ทำให้พบว่ามันก็สามารถใช้แทน Vuex ได้เป็นอย่างดี เลยบันทึกเรื่องราวไว้ฟื้นฟูความจำ

![](https://cdn-images-1.medium.com/max/1024/1*OFCRWmGpDK9JEBQwMzBRwg.png)

### ออกตัว

บทความนี้ผมเขียนตัวอย่างง่ายๆ เทียบกันระหว่าง Vue2 และ Vue3 โดยใช้ composition api จัดการ state เหมือนกันทำงานได้เหมือนกัน แต่อาจจะมีรายละเอียดเล็กน้อยต่างกันบ้าง สำหรับมือใหม่ที่กำลังปวดหัวกับ Vuex อาจจะเป็นทางเลือกหนึ่งในการสร้าง Project ขึ้นมาแบบไม่ซับซ้อนมากนัก บางอย่างอาจจะมีวิธีที่ง่ายกว่าแต่ด้วยประสบการณ์ของผมเลยเขียนมาไม่สวยมากนักผมยินดีรับฟังคำชี้แนะเพื่อแลกเปลี่ยนความรู้กันครับ

### ตัวอย่างคืออะไร ?

ผมจะยกตัวอย่างสองตัวอย่างคือ

1. ระบบจำลองการ login ง่าย
2. ระบบ System Notification แจ้งเตือนแบบง่ายๆ

โดยผมจะให้ส่วนหนึ่งเก็บไว้ใน state ที่ผมใช้ composition api แล้ว เรียกใช้จาก components ต่างๆ โดยที่ทุก component จะเห็นค่าเหมือนกัน

### Source ตัวอย่าง

#### Vue2

[mrchoke/vue2-state-compositional-api](https://github.com/mrchoke/vue2-state-compositional-api)

#### Vue3

[mrchoke/vue3-state-compositional-api](https://github.com/mrchoke/vue3-state-compositional-api)

### Vue Composition API

#### Vue2

ให้ติดตั้งเพิ่มเติม

```
yarn add @vue/composition-api
```

และแก้ใน src/main.js

```
import Vue from 'vue'
import App from './App.vue'
import router from './router'
**import VueCompositionAPI from '@vue/composition-api'**

Vue.config.productionTip = false
**Vue.use(VueCompositionAPI)**

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
```

#### Vue3

สามารถใช้งานได้เลย

### การสร้าง State

เราสามารถสร้าง file .js ขึ้นมาใข้งานได้เลยโดยเก็บไว้ที่ไหนก็ได้ ตัวอย่างผมเก็บไว้ใน

```
src/state
```

รายละเอียดที่แตกต่างกันเล็กน้อยระหว่าง Vue2 และ Vue3 คือ ใน state ของ Vue2 ต้องประกาศการใช้ vue composition อีกรอบผมยังหาไม่เจอว่ามันต้องทำยังไงถึงจะเขียนได้ปกติ แต่ที่เจอคือถ้ามี file state หลายๆ file เขียนไว้แค่ file เดียวก็ได้ละ ส่วน Vue3 เขียนได้แบบปกติไม่ต้องประกาศเพิ่มนะ เช่น ผมมี

- SystemNoti.js
- User.js

Vue2 ผมประกาศข้างล่างนี้ไว้ใน file ใด file หนึ่งก็พอ คือผมแปะไว้ใน SystemNoti.js

#### SystemNoti.js

ผมใช้ lib Notifierjs ผมหาใน NPM ไม่เจอเลยสั่งติดตั้งจาก github

```
yarn add [https://github.com/jsanahuja/Notifierjs](https://github.com/jsanahuja/Notifierjs)
```

**Vue2**

```
import Vue from 'vue'
import **VueCompositionAPI** , { reactive } from '@vue/composition-api'
import Notifier from '@jsanahuja/notifierjs'

**Vue.use(VueCompositionAPI)**

class SystemNoti {
  type = 'info'
  msg = 'Welcome'

notifier = new Notifier({
    position: 'top-right',
    direction: 'top',
    default\_time: 3000
  })

setdata({ type = 'info', msg }) {
    this.type = type
    this.msg = msg
    this.notifier.notify(this.type, this.msg)
  }
  noti() {
    this.notifier.notify(this.type, this.msg)
  }
}

const state = reactive(new SystemNoti())
export default state
```

**Vue3**

```
import { reactive } from 'vue'
import Notifier from '@jsanahuja/notifierjs'

class SystemNoti {
  type = 'info'
  msg = 'Welcome'

notifier = new Notifier({
    position: 'top-right',
    direction: 'top',
    default\_time: 3000
  })

setdata({ type = 'info', msg }) {
    this.type = type
    this.msg = msg
    this.notifier.notify(this.type, this.msg)
  }
  noti() {
    this.notifier.notify(this.type, this.msg)
  }
}

const state = reactive(new SystemNoti())
export default state
```

ข้อแตกต่างอีกอย่างคือเวลาเรา import ของจาก vue composition api ถ้า Vue2 เรา import จาก ‘@vue/composition-api’ ส่วน Vue3 เรา import จาก ‘vue’ ได้โดยตรงเลย

ตัวอย่างนี้ผมสร้าง class ง่ายๆ ไว้จัดการ notification แล้วจริงๆ noti ไม่จำเป็นต้องจำ state แบบนี้ก็ได้เนอะฮาๆ แค่ยกตัวอย่างให้ดูละกัน

จุดสำคัญคือ ผมสร้าง object reactive ขึ้นมาจากการ new SystemNoti() แล้ว export ออกไปซึ่งตอนน object ดังกล่าวนี้ก็ทำตัวเป็น state เรียบร้อย ง่ายไหมครับ เวลาผมจัดการเปลี่ยนค่าต่างๆ ใน object นี้ส่วนต่างๆ ที่รับ object นี้ไปทำงานด้วยก็จะเห็นการเปลี่ยนแปลงเหมือนกันหมด

#### User.js

อีก file หนึ่งสร้างมาเพื่อทำระบ login หลอกๆดังนี้

**Vue2**

```
**import { reactive } from '@vue/composition-api'**
import SystemNoti from './SystemNoti'

const users = [
  {
    username: 'user1',
    password: 'abc',
    fullname: 'MrChoke'
  },
  {
    username: 'user2',
    password: 'abc',
    fullname: 'Guest'
  }
]
**const auth = reactive({  
  user: {},  
  status: false  
})**  
const login = (username, password) => {
  const user = users.find(u => u.username === username && u.password === password)

if (user) {
    auth.user = user
    auth.status = true
    SystemNoti.setdata({ type: 'success', msg: `Welcome ${user.fullname}` })
  } else {
    SystemNoti.setdata({ type: 'error', msg: `User ${username} not found or Password incorrect` })
    return false
  }
  return true
}

const logout = () => {
  const olduser = Object.assign({}, auth.user)
  SystemNoti.setdata({ type: 'success', msg: `See ya ${olduser.fullname}` })
  auth.user = {}
  auth.status = false
}
export { login, logout, auth }
```

**Vue3**

```
**import { reactive } from '** [**vue**](http://twitter.com/vue/composition-api) **'**
import SystemNoti from './SystemNoti'

const users = [
  {
    username: 'user1',
    password: 'abc',
    fullname: 'MrChoke'
  },
  {
    username: 'user2',
    password: 'abc',
    fullname: 'Guest'
  }
]
**const auth = reactive({  
  user: {},  
  status: false  
})**  
const login = (username, password) => {
  const user = users.find(u => u.username === username && u.password === password)

if (user) {
    auth.user = user
    auth.status = true
    SystemNoti.setdata({ type: 'success', msg: `Welcome ${user.fullname}` })
  } else {
    SystemNoti.setdata({ type: 'error', msg: `User ${username} not found or Password incorrect` })
    return false
  }
  return true
}

const logout = () => {
  const olduser = Object.assign({}, auth.user)
  SystemNoti.setdata({ type: 'success', msg: `See ya ${olduser.fullname}` })
  auth.user = {}
  auth.status = false
}
export { login, logout, auth }
```

ซึ่งผมสร้าง object และ function ต่างๆ ไว้ และ export ออกไป จริงๆ จะสร้างเป็น class เหมือนก่อนหน้าก็ง่ายดีนะเวลา export ออกไปจะไม่วุ่นวาย

จุดสำคัญก็เป็น object auth ที่ผมสร้างเป็น reactive ไว้ซึ่งถ้ามีการเปลี่ยนแปลงค่า ก็จะรับรู้ค่าที่เหมือนกันในทุกๆ ที่

### การใช้งาน

#### Login.vue

**template**

```
<template>
  <div v-if=" **!auth.status**">
    <div>
      <label>Username:</label>
      <input type="text" v-model=" **username**" />
    </div>
    <div>
      <label>Password:</label>
      <input type="password" v-model=" **password**" [@keyup](http://twitter.com/keyup).enter="preLogin" />
    </div>
    <div>
      <button [@click](http://twitter.com/click)="preLogin" :disabled="!canLogin()">Sign In</button>
    </div>
  </div>
  <div v-else>
    <button [@click](http://twitter.com/click)="**logout()**">Logout</button>
  </div>
</template>
```

**script**

```
<script>
**import { defineComponent, ref } from '@vue/composition-api'**
  import router from '@/router'
  import SystemNoti from '@/state/SystemNoti'
  import { login, logout, auth } from '@/state/User'

export default defineComponent({
    name: 'Login',
    setup() {
      const username = ref('')
      const password = ref('')
      const canLogin = () => {
        return username.value.length > 0 && password.value.length > 0
      }
      const preLogin = () => {
        if (!canLogin()) {
          SystemNoti.setdata({ type: 'error', msg: 'Username or Password incorrect' })
        } else {
          if (login(username.value, password.value)) {
            username.value = ''
            password.value = ''
            router.push({ name: 'Home' })
          }
        }
      }
      return {
        username,
        password,
        preLogin,
        canLogin,
        logout,
        auth
      }
    }
  })
</script>
```

Vue2 และ Vue3 สามารถเขียนได้เหมือนกันเลยแค่ import ไม่เหมือนกันเท่านั้นในตัวอย่าง script ด้านบนจะเป็น Vue2 ถ้า Vue3 ก็เปลี่ยน import เป็น ‘vue’

ซึ่งในตัวอย่างก็รับค่า username และ password มาจาก form ใน template แล้วตรวจสอบเบื้องต้นง่ายๆ ว่ามีค่าไหมถ้าไม่มีก็ให้ Notify ข้อความขึ้นมา ถ้ามีครบก็ให้ส่งไปยัง login ใน src/state/User.js

ซึ่งถ้า login สำหรับตัว object auth ใน User.js ก็จะถูกแทนที่ด้วย Object ของ user และ ค่า status ก็จะเปลี่ยนเป็น true

#### **ถ้าอยากให้ component อื่นรับรู้ด้วยต้องทำอย่างไร ?**

ตัวอย่างใน App.vue

App.vue ถือเป็น component หลักการขึ้น status ต่างๆ ทำไว้ที่นี่ก็จะง่ายสุดเช่น แสดงปุ่ม login / logout

**script**

```
<script>
  import { defineComponent } from '@vue/composition-api'
**import { auth, logout } from '@/state/User'**

export default defineComponent({
    name: 'App',
    setup() {
      return {
**auth,  
        logout**  
      }
    }
  })
</script>
```

**template**

```
<router-link :to="{ name: 'Login' }" **v-if="!auth.status"** >Login</router-link>

<a href="#" @click="logout" **v-else** >Logout</a>
```

หรือจะแสดง Welcome พร้อมกับชื่อผู้ใช้

```
<div **v-if="auth.status"** >Welcome: **`{{ auth.user.fullname }}`** </div>
```

#### ถ้าให้ Router รู้ละว่าตอนนี้ login อยู่หรือเปล่าต้องทำไง ?

**Vue Router 3**

```
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
**import { auth } from '../state/User'**

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/about',
    name: 'About',
 **meta: {  
      auth: true  
    },**  
    component: () => import(/\* webpackChunkName: "about" \*/ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE\_URL,
  routes
})

**router.beforeEach((to, from, next) => {  
  if (auth.status || to.name === 'Login' || !to.meta.auth) {  
    next()  
  } else {  
    next({ name: 'Login' })  
  }  
})**  
export default router
```

**Vue Router 4**

```
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
**import { auth } from '../state/User'**
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/about',
    name: 'About',
**meta: {  
      auth: true  
    },**  

    component: () => import(/\* webpackChunkName: "about" \*/ '../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE\_URL),
  routes
})

**router.beforeEach((to, from, next) => {  
  if (auth.status || to.name === 'Login' || !to.meta.auth) {  
    next()  
  } else {  
    next({ name: 'Login' })  
  }  
})**  
export default router
```

จากตัวอย่างก็ง่ายๆ เหมือนเรา check กับ Vuex import auth state มาแล้วตรวจสอบดูว่า login อยู่ไหมถ้าไม่ให้ไปหน้า login ซึ่งหน้าไหนบ้างที่ต้อง login ก็แปะ meta ไว้จากตัวอย่างผมแปะ

```
**meta: {  
      auth: true  
    }**  
```

ไม่รู้ว่าอ่านเข้าใจกันไหมครับ จากประสบการณ์อันน้อยนิดของผมเมื่อเทียบกับ Vuex แล้วมันง่ายกว่ากันมากไม่ต้องพรรณาอะไรมากมายแค่ import เข้ามาก็ใช้ได้เลย

#### Demo

- [vue2-state-compositional-api](https://mrchoke.github.io/vue2-state-compositional-api/)
- [vue3-state-compositional-api](https://mrchoke.github.io/vue3-state-compositional-api/)