+5

Vue.js & Rails API (Phần 2): Tìm hiểu Vuex trong Vue.js

Tiếp tục với loạt bài về Vue.js & Rails API, hôm nay mình sẽ giới thiệu với mọi người về một thư viện rất quyền lực của Vue.js đó là Vuex. Nó cũng tương tự như Redux của React. Vậy Vuex là gì? Và tại sao nó lại thần thánh như vậy?

Vuex là gì?

Theo tài liệu chính thức về Vuex thì:

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official devtools extension to provide advanced features such as zero-config time-travel debugging and state snapshot export/import.


Nói dễ hiểu, Vuex là thư viện mẫu giúp quản lý trạng thái các component trong ứng dụng Vue.js, đây là nơi lưu trữ tập trung tất cả các component trong một ứng dụng, với nguyên tắc trạng thái chỉ có thể được thay đổi theo kiểu có thể dự đoán.

Tại sao nên sử dụng Vuex?

Như chúng ta đã biết thành phần chính trong các ứng dụng có sử dụng Vue.js là các component. Và luồng xử lý của Vue.js là luồng dữ liệu một chiều, nghĩa là:

  • Dữ liệu đi từ component cha xuống component con (qua props, watch, ...)
  • Nếu component con muốn tác động để thay đổi dữ liệu của component cha thì phải tạo một event để thằng cha lắng nghe, cha thay đổi dữ liệu của mình rồi mới thay đổi dữ liệu của component con.

Vậy nên, những ứng dụng đơn giản thường chỉ có state, viewaction.

  • State: nơi bắt đầu thực hiện ứng dụng.
  • View: các khai báo ánh xạ của state.
  • Actions: là những hành động làm thay đổi state với đầu vào từ view.

Tóm lại: Nếu có một action làm thay đổi state thì sẽ dẫn đến thay đổi dữ liệu thực hiện ở view.

Cách hoạt động đơn giản trên có thể dễ dàng bị phá vỡ khi nhiều component cùng chia sẻ một state chung.

Nhiều view có thể cùng phụ thuộc vào một trạng thái. Các actions từ nhiều view khác nhau có thể biến đổi cùng một state, gây ra xung đột. Hay chỉ đơn giản khi quy mô ứng dụng lớn, tác động tới hàng trăm components, đường dẫn khác nhau.

Vậy tại sao chúng ta không tách state được chia sẻ ra khỏi các component và quản lý nó trong một bộ máy toàn cục lưu trữ tập trung (a singleton global)?
Và đó chính là nguyên nhân cho sự ra đời của Vuex.

Với Vuex các component sẽ trở thành các view và có thể truy xuất state hoặc thực thi các actions.

Các khái niệm trong Vuex

State

Vuex sử dụng một cây trạng thái duy nhất, nó sẽ chứa tất cả các trạng thái của ứng dụng. Do vậy, sẽ chỉ có một vị trí lưu trữ tập trung, giúp việc xác định các trạng thái dễ dàng và đơn giản hơn. Để sử dụng state trong component, chúng ta có thể gọi nó qua function() data:

const CommentStore = {
  namespaced: true,
  state: {
    comments: [],
    comment: {},
    current_user: {}
  },
  ...
 }
export default {
  data: function() {
    return this.$store.state.CommentStore;
  },
  ...  
}

Mutations

Chứa các chức năng để thực hiện thay đổi trạng thái. Mutation tương tự như các event, nó gồm một String và một handler function.

mutations: {
   getComments(state, comments) {
     state.comments = comments;
       return state.comments;
   },
   getComment(state, comment) {
     state.comment = comment;
       return state.comment;
   }
    ...
  },

Handler function là nơi thực hiện thay đổi các trạng thái và nó cần tham số đầu tiênstate.
State sẽ không thể thay đổi trực tiếp mà cần phải được thay đổi thông qua việc thực thi các handler function khi gọi method commitactions.

actions: {
  index(context, postId) {
    $.ajax({
      url: `posts/${postId}/comments`,
      type: 'GET',
      success: function(data) {
        context.commit('getComments', data)
      }
    })
  },
  ...
}

Actions

Đây cũng là các chức năng tương tự như mutations nhưng có hai sự khác biệt.

  • Đầu tiên, các actions được sử dụng cho các hoạt động không đồng bộ, vì vậy chúng rất phù hợp để thực hiện các yêu cầu HTTP (gọi API để lấy dữ liệu từ server, hành động thay đổi dữ liệu trong database, ...).
  • Thứ hai, các actions chỉ lên state bằng cách gọi các mutations.

Và các actions sẽ được thực thi khi sử dụng method dispatch

export default {
  data: function() {
    return this.$store.state.CommentStore;
  },
  created: function() {
    this.$store.dispatch('CommentStore/index', this.$route.params.id)
  },
  components: {
    Comment: Comment
  }
}

Getters

Thỉnh thoảng chúng ta cần lấy các trạng thái dựa vào việc tính toán, loại bỏ, filter các trạng thái được cung cấp. Nếu nhiều component cần làm điều này, thì có thể định nghĩa getters trong store để thực hiện.

state: {
  comments: [
    { id: 1, content: '...', post: 'Post 1' },
    { id: 2, content: '...', post: 'Post 2' }
  ]
},
getters: {
  getPosts: state => {
    return state.comments.filter(comment => comment.post)
  }
}

Modules

Do chỉ sử dụng một cây trạng thái duy nhất nên tất cả các trạng thái của ứng dụng đều được chứa trong một đối tượng. Do vậy, khi quy mô của ứng dụng tăng thì store chắc chắn sẽ phình to.
Và Vuex cho phép chúng ta chia nhỏ store thành các module bé hơn. Mỗi module nhỏ hơn đều có state, mutations, actions, getters hay cả các module lồng nhau khác.

const CommentStore = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const PostStore = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const store = new Vuex.Store({
  modules: {
    CommentStore,
    PostStore,
    ...
  }
});

Khi nào nên sử dụng Vuex?

Vuex là sự lựa chọn hoàn hảo cho các ứng dụng SPA tầm trung đến lớn với mã nguồn có cấu trúc rõ ràng và nâng cao khả năng duy trì, mở rộng.
Vuex giúp giải quyết việc quản lý state được chia sẻ có hiệu quả với chi phí cho nhiều khái niệm và bản tóm tắt hơn. Cân bằng giữa tốc độ và hiệu năng của dự án. Đó là sự đánh đổi giữa hiệu năng ngắn hạn và dài hạn.

Bây giờ, mình sẽ thực hiện setup Vuex cho ứng dụng đã tạo ở phần 1 trước khi bắt tay vào thực hiện một TODO list đơn giản với Vuex.

  • Install Vuex (qua yarn/npm)
npm install --save vuex

Có thể cần chạy thêm lệnh yarn install --check-files

  • Tạo và sắp xếp các thư mục như hình sau:

  • Thêm vào index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
  }
});

export default store;
  • Thêm Vuex store vào application.js
import Vue from 'vue'
import App from 'src/components/app.vue'
import store from 'src/vuex'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    el: '#app', 
    store,     
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)
})

Bây giờ khi cần tạo store mới chúng ta chỉ cần thêm file mới trong thư mục storesimport nó trong index.js. Và tất cả đã sẵn sàng 😄 .

Kết luận

  • State được lưu trữ dưới dạng một JSON Object lớn.
  • Getters được sử dụng để truy cập các giá trị được lưu trữ trong state.
  • Mutations sẽ cập nhật state. Cần nhớ rằng mutationssynchronous.
  • Tất cả các hoạt động bất đồng bộ phải được thực hiện trong actions. actions thay đổi trạng thái bằng cách commit một mutation.
  • Luôn luôn chỉ thực hiện mutation thông qua một action.
  • Các module có thể được sử dụng để tổ chức store thành nhiều tệp lưu trữ nhỏ hơn.

Tham khảo

  1. Vuex
  2. Mastering Vuex — Zero to Hero

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí