Viblo CTF
+5

Giới thiệu Vuex

Vấn đề hiện tại của mô hình MVVM

Trong mô hình MVVM có 3 đối tượng là View, ModelViewModel, chúng ta có thể dùng Model để chứa dữ liệu, người dùng có thể tương tác View để tác động vào Model. Với các ứng dụng vừa và nhỏ, chủ yếu là các thay đổi trên Model cập nhật lên View. Tuy nhiên, khi hệ thống lớn dần lên, các tác động qua lại trở lên phức tạp và đôi khi chỉ cần một thay đổi trên View dẫn đến hàng trăm nghìn các tác động ngược lại Model và từ đó lại tác động ngược lại View làm hệ thống trở lên không kiểm soát nổi, khi xảy ra chúng ta không thể debug được nó.

Mạng xã hội Facebook trước đây cũng đã từng bị một lỗi tương tự như vậy, khi người dùng đăng nhập vào Facebook, họ sẽ thấy có thông báo với một biểu tượng có tin nhắn nhưng khi nhấn vào thì không hề có một tin nhắn nào cả. Sau một vài phút hiện tượng này lại lặp lại thông báo hiện ra và nhấn vào lại không có tin nhắn nào… Lỗi này lặp đi lặp lại thành trong một vòng luẩn quẩn, nó không chỉ diễn ra với người dùng mà với chính những lập trình viên của Facebook. Họ sửa được lỗi, mọi thứ hoạt động ổn trong một khoảng thời gian ngắn thì lỗi này lại quay lại, cuối cùng nguyên nhân đến từ việc quản lý hàng trăm nghìn các tương tác qua lại giữa View và Model là không thể kiểm soát và bắt buộc phải có một kiến trúc mới giúp xử lý theo luồng các tương tác này.

Giải pháp luồng dữ liệu 1 chiều

Đội ngũ kỹ thuật Facebook đã nghiên cứu và đưa ra một kiến trúc mới với tên gọi Flux. Trong kiến trúc này, luồng dữ liệu sẽ chỉ theo một chiều (one way data flow), khi có một dữ liệu mới, luồng này sẽ bắt đầu lại từ đầu.

Vuex được xây dựng dựa trên ý tưởng của Flux, Redux và kiến trúc Elm, tuy nhiên nó không được tích hợp trực tiếp vào trong lõi framework Vue.js mà được tách biệt thành một thư viện riêng. Chính vì lý do này, trong Vuex chúng ta gặp rất nhiều các khái niệm, thuật ngữ giống như Flux. Chúng ta cùng xem luồng dữ liệu trong Vuex, nó khá giống với giải pháp luồng dữ liệu ở trên.

Nếu ứng dụng của bạn quy mô không lớn thì Vuex là không cần thiết. Việc mới làm quen với Vuex bạn sẽ cảm thấy hơi khó tiếp cận với khái niệm và các code. Trong phần tiếp theo mình sẽ lấy một ví dụ nhỏ ứng dụng vuex để làm rõ hơn về nó.

Ứng dụng học đếm

Chúng ta sẽ xây dựng 1 ứng dụng học đếm sử dụng vuex

Cài đặt

Sử dụng npm để cài đặt vuex

npm install vuex --save

Import thư viện Vuex và thông báo việc sử dụng thư viện cho VueJS bằng hàm Vue.use()

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

Ví dụ học đếm

Giả sử chúng ta có một ứng dụng học đếm đơn giản như sau:

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

Ứng dụng này có 3 phần:

  • State: Nguồn dữ liệu là nơi dẫn hướng ứng dụng.
  • View: Nơi khai báo các mapping với state.
  • Action: Cách thay đổi state từ các tương tác của người dùng lên view.

Bây giờ chúng ta sẽ bắt đầu đưa Vuex vào ví dụ trên

Bước 1: Cài đặt ứng dụng mẫu Vue.js với Vue CLI

vue init webpack vuex-tutorial
cd vuex-tutorial
npm install vuex --save
npm run dev

OK, vậy là mọi thứ đã chuẩn bị xong để có thể bắt tay vào.

Bước 2: Xây dựng ví dụ "học đếm"

Tạo ra 2 Vue components là IncrementButtonCounterDisplay như sau:

<!-- src/components/IncrementButton.vue -->

<template>
  <button @click.prevent="activate">+1</button>
</template>

<script>
export default {
  methods: {
    activate () {
      console.log('+1 Pressed')
    }
  }
}
</script>
<!-- src/components/CounterDisplay.vue -->

<template>
  Count is {{ count }}
</template>

<script>
export default {
  data () {
    return {
      count: 0
    }
  }
}
</script>

Tiếp theo là thay đổi file src/App.vue như sau:

<!-- src/App.vue -->

<template>
  <div id="app">
    <h3>Increment:</h3>
    <increment></increment>
    <h3>Counter:</h3>
    <counter></counter>
  </div>
</template>

<script>
import Counter from './components/CounterDisplay.vue'
import Increment from './components/IncrementButton.vue'
export default {
  components: {
    Counter,
    Increment
  }
}
</script>

Quay lại với trình duyệt, truy cập http://localhost:8080, trên màn hình hiện tại sẽ có 1 nút bấm +1, Click vào button này sẽ có message +1 pressed được log ở console.

Áp dụng vuex

Sử dụng Vuex chúng ta cũng tạo ra src/store.js như sau:

import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex)

var store = new Vuex.Store({
  state: {
    counter: 0
  },
  mutations: {
    INCREMENT (state) {
      state.counter ++
    }
  }
})

export default store

Cùng xem qua đoạn code trên:

  • import thư viện Vuex và thông báo với VueJS về việc sử dụng vuex thông qua Vue.use(Vuex).
  • Thay đổi store từ 1 object JS thuần thành 1 instance của Vuex.Store.
  • Khởi tạo state và gán counter = 0.
  • Chúng ta có đối tượng mutations với INCREMENT để thực hiện thay đổi giá trị của state.

Trong các component muốn xài chung Vue store chỉ cần thực hiện import file store vào.

import store from '../store.js'

Một điểm cần lưu ý là không thể thay đổi trực tiếp các giá trị trong store.state (cụ thể là counter) mà phải gửi dispatch đến các mutations. Quay lại component IncrementButton.vue

import store from '../store'

export default {
  methods: {
    activate () {
      store.dispatch('INCREMENT')
    }
  }
}

Trong IncrementButton component thậm chí còn không có phần data. Khi nút +1 được bấm nó sẽ gọi đến store.dispatch(‘INCREMENT’). Vậy trong CounterDisplay.vue thì như thế nào?

<!-- src/components/CounterDisplay.vue -->

<template>
  Count is {{ counter }}
</template>

<script>
import store from '../store'

export default {
  computed: {
    counter () {
      return store.state.counter
    }
  }
}
</script>

Chúng ta không còn cần phải sử dụng các đối tượng chia sẻ trạng thái, thay vào đó sử dụng thuộc tính computed của Vue để đưa giá trị lên phần bộ đếm. Khi bạn tải lại trang, mọi thứ hoạt động đúng như mong muốn:

  • Vue.js quản lý sự kiện active() của nút “+1”, khi nút này được bấm nó gọi đến store.dispatch(‘INCREMENT’).
  • Ở đây, INCREMENT là tên của một action, nó dùng để thay đổi state. Chúng ta cũng có thể truyền thêm các tham số khác trong dispatch.
  • Vuex tìm ra mutator nào được gọi với dispatch này, trong ví dụ này chúng ta chỉ có một mutator nhưng có thể tạo ra rất nhiều trong các ứng dụng phức tạp.
  • Mutator nhận được một bản sao của state và cập nhật nó vào state chính, nó cũng lưu giữ một bản cũ hơn của state để sử dụng cho các trường hợp cần thiết.
  • Khi state được cập nhật, vue tự động báo cho các component có sử dụng state này.

Lời kết

Trên đây, chúng ta đã biết Vuex là gì? và các khái niệm cơ bản trong Vuex, với những người mới tìm hiểu về Vuex có thể sẽ khó nắm được ngay các vấn đề trong Vuex, đặc biệt nếu bạn chưa biết Vue.js là gì thì bạn nên tham khảo kiến thức về Vue.js trước khi tìm hiểu thư viện Vuex.

Đây là source code hoàn chỉnh của một ứng dụng học đếm đã được phân chia thành các module nhỏ, các bạn có thể tham khảo từng bước qua các commits. https://github.com/tdson/vuex-example


Nguồn: Bài viết có tham khảo một số hướng dẫn từ trang chủ https://vuex.vuejs.org/en/ và các hình ảnh từ internet.


All Rights Reserved