Cafedev xin chào các bạn lập trình viên! Hôm nay, chúng ta sẽ cùng nhau khám phá chủ đề Pinia Vuejs with Actions. Vue.js là một framework mạnh mẽ cho phép chúng ta xây dựng các ứng dụng web một cách dễ dàng và hiệu quả. Trong bài viết này, Cafedev sẽ giới thiệu về cách sử dụng Actions trong Vue Pinia, giúp bạn quản lý logic nghiệp vụ một cách linh hoạt và tối ưu. Hãy cùng Cafedev tìm hiểu chi tiết hơn về chủ đề thú vị này nhé!


Actions tương đương với methods trong các component. Chúng có thể được định nghĩa với thuộc tính actions trong defineStore()chúng hoàn hảo để định nghĩa logic nghiệp vụ:

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    // since we rely on `this`, we cannot use an arrow function
    increment() {
      this.count++
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    },
  },
})

Giống như getters, actions có thể truy cập vào toàn bộ instance của store thông qua this với hỗ trợ đầy đủ typing (và autocomplete ✨). Khác với getters, actions có thể là bất đồng bộ, bạn có thể await bên trong actions cho bất kỳ lời gọi API nào hoặc thậm chí các actions khác! Dưới đây là một ví dụ sử dụng Mande. Lưu ý rằng thư viện bạn sử dụng không quan trọng miễn là bạn có được một Promise. Bạn thậm chí có thể sử dụng hàm fetch gốc (chỉ trình duyệt):

import { mande } from 'mande'

const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () => ({
    userData: null,
    // ...
  }),

  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
        showTooltip(`Welcome back ${this.userData.name}!`)
      } catch (error) {
        showTooltip(error)
        // let the form component display the error
        return error
      }
    },
  },
})

Bạn cũng hoàn toàn tự do đặt bất kỳ đối số nào bạn muốn và trả về bất kỳ thứ gì. Khi gọi actions, mọi thứ sẽ được suy diễn tự động!
Actions được gọi giống như các hàm và phương thức thông thường:

<script setup>
const store = useCounterStore()
// call the action as a method of the store
store.randomizeCounter()
</script>

  <!-- Even on the template -->
  <button @click="store.randomizeCounter()">Randomize</button>

1. Truy cập actions từ các stores khác

Để sử dụng một store khác, bạn có thể trực tiếp sử dụng nó bên trong action:

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    preferences: null,
    // ...
  }),
  actions: {
    async fetchUserPreferences() {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})

2. Sử dụng với Options API


Với các ví dụ sau, bạn có thể giả định store sau đã được tạo:

// Example File Path:
// ./src/stores/counter.js

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++
    }
  }
})

2.1 Với setup()

Mặc dù Composition API không dành cho tất cả mọi người, hook setup() có thể làm cho Pinia dễ làm việc hơn khi sử dụng Options API. Không cần các hàm trợ giúp map bổ sung!

<script>
import { useCounterStore } from '../stores/counter'

export default defineComponent({
  setup() {
    const counterStore = useCounterStore()

    return { counterStore }
  },
  methods: {
    incrementAndPrint() {
      this.counterStore.increment()
      console.log('New Count:', this.counterStore.count)
    },
  },
})
</script>

2.2 Không dùng setup()

Nếu bạn thích không sử dụng Composition API, bạn có thể sử dụng helper mapActions() để map các thuộc tính actions thành các phương thức trong component của bạn:

import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counter'

export default {
  methods: {
    // gives access to this.increment() inside the component
    // same as calling from store.increment()
    ...mapActions(useCounterStore, ['increment']),
    // same as above but registers it as this.myOwnName()
    ...mapActions(useCounterStore, { myOwnName: 'increment' }),
  },
}

3. Đăng ký actions — Hy vọng bản dịch này hữu ích cho bạn!

Có thể quan sát các actions và kết quả của chúng với store.$onAction(). Callback được truyền vào nó sẽ được thực thi trước khi action thực hiện. after xử lý các promises và cho phép bạn thực thi một hàm sau khi action hoàn thành. Tương tự, onError cho phép bạn thực thi một hàm nếu action ném lỗi hoặc bị từ chối. Những điều này hữu ích để theo dõi lỗi tại runtime, tương tự như mẹo này trong tài liệu Vue.
Dưới đây là một ví dụ ghi lại trước khi chạy actions và sau khi chúng hoàn thành/từ chối.

const unsubscribe = someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    // a shared variable for this specific action call
    const startTime = Date.now()
    // this will trigger before an action on `store` is executed
    console.log(`Start "${name}" with params [${args.join(', ')}].`)

    // this will trigger if the action succeeds and after it has fully run.
    // it waits for any returned promised
    after((result) => {
      console.log(
        `Finished "${name}" after ${
          Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // this will trigger if the action throws or returns a promise that rejects
    onError((error) => {
      console.warn(
        `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// manually remove the listener
unsubscribe()

Theo mặc định, đăng ký actions được ràng buộc với component nơi chúng được thêm vào (nếu store nằm trong setup() của component). Nghĩa là, chúng sẽ tự động bị loại bỏ khi component bị gỡ bỏ. Nếu bạn cũng muốn giữ chúng sau khi component bị gỡ bỏ, hãy truyền true làm đối số thứ hai để tách đăng ký action khỏi component hiện tại:

<script setup>
const someStore = useSomeStore()

// this subscription will be kept even after the component is unmounted
someStore.$onAction(callback, true)
</script>

Cảm ơn bạn đã đồng hành cùng Cafedev trong hành trình khám phá về Pinia Vuejs with Actions. Chúng ta đã cùng nhau tìm hiểu về cách sử dụng Actions trong Vue Pinia để tối ưu hóa quá trình phát triển ứng dụng web. Hy vọng rằng những kiến thức và kinh nghiệm mà chúng ta đã chia sẻ sẽ giúp ích cho bạn trong công việc lập trình. Đừng quên tiếp tục theo dõi những bài viết mới nhất trên Cafedev để cập nhật thông tin và kiến thức công nghệ hàng ngày. Hẹn gặp lại trong những bài viết tiếp theo của chúng tôi!

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Vuejs từ cơ bản tới nâng cao

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!