Giới thiệu Vuex
Bài đăng này đã không được cập nhật trong 6 năm
Vấn đề hiện tại của mô hình MVVM
Trong mô hình MVVM có 3 đối tượng là View
, Model
và ViewModel
, 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ớistate
.Action
: Cách thay đổi state từ các tương tác của người dùng lênview
.
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à IncrementButton
và CounterDisplay
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ệnVuex
và thông báo với VueJS về việc sử dụng vuex thông quaVue.use(Vuex)
.- Thay đổi
store
từ 1 object JS thuần thành 1 instance củaVuex.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 đếnstore.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