Cơ bản về Vuex
Bài đăng này đã không được cập nhật trong 4 năm
Khái niệm
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.
Đây là khái niệm của vuex
trên trang chủ của nó, các bạn có thể tìm đọc tại đây. Hiểu đơn giản thì Vuex
là một thư viện có chức năng là chia sẻ trạng thái giữa các components
, Vuex
là nơi lưu trữ tập trung cho tất cả các components
trong ứng dụng.
Cấu trúc của vuex
Vuex
hoạt động theo mô hình "one-way data flow"
(luồng dữ liệu một chiều) với ba thành phần chính sau:
- State: Trạng thái - là nơi khởi nguồn để thực hiện ứng dụng
- View: Những gì được nhìn thấy, là các khai báo ánh xạ với trạng thái
- Actions: Là cách mà trạng thái thay đổi phản ứng lại với các nhập liệu của người dùng từ
view
.
Tuy nhiên, với các hoạt động đơn giản trên thì chúng sẽ dễ dàng bị phá vỡ khi mà một components
chia sẻ chung nhiều state
- Nhiều
view
có thể phụ thuộc vào một trạng thái - Các
actions
đến từ nhiềuviews
có thể biến đổi cùng một trạng thái, gây ra xung đột.
Để dễ hiểu hơn mình sẽ đưa ra các ví dụ để các bạn hiểu hơn về các trường hợp quản lý state
. Nếu một app không sử dụng vuex
mà với số lượng components
lớn ở trong hệ thống của bạn, mà mỗi components
lại có những trạng thái riêng biệt, thì mô hình ứng dụng của bạn sẽ như thế này.
Một vấn đề xảy ra khi mà một component
thay đổi trạng thái của nó, mà một components
nào đó xa xôi mà có "họ hàng" với component
này cùng sử dụng chung 1 trạng thái, thì chúng ta sẽ cần phải "giao tiếp" 2 component
đó lại với nhau. Với cách thông thường thì chúng ta sẽ dùng các event
để truyền dữ liệu từ component
con sang cha, và dùng các props
để truyền từ cha xuống con, nghe rất là phức tạp đúng k. Đây là hình ảnh minh họa
Thì đây chính là lý do mà vuex
được sinh ra để quản lý trạng thái trong ứng dụng. Thay vì mỗi component
có trạng thái riêng thì chúng ta sẽ kết hợp chúng lại vào một nơi chứa tất cả các trạng thái của hệ thống. Nhưng mà nếu chỉ mỗi hợp nhất dữ liệu vào một nguồn cũng sẽ không hoàn toàn giải quyết được các vấn đề vì điều gì sẽ xảy ra khi mà nhiều components
thay đổi trạng thái bằng nhiều cách và từ nhiều nơi, thì lúc đó chúng ta cũng cần có một cái cấu trúc "tiêu chuẩn" để kiểm soát các trạng thái. Ví dụ với trong vuejs
new Vue({
data: {
},
methods: {
},
computed: {
}
});
thi tương tự trong Vuex
nó cũng có 1 standard(tiêu chuẩn) code riêng
new.Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
getters: {
}
});
Như các bạn có thể thấy ở trên thì trong Vuex
, state
của ứng dụng được quản lý tập trung trong một chỗ gọi là store
bao gồm những phần chính.
- State: Trong vuejs chúng ta có thuộc tính
data
thì trongvuex
làstate
- Actions: Cũng tương tự vuejs có
method
, thì ở đây chúng ta cóactions
để thao tác vớistate
- Getters: Giống như vuejs có thuộc tính
computed
thìvuex
cũng cógetters
- bộ lọcstates
- Mutations: Gần giống với
watch
theo dõi sự thay đổi thì ở đây,vuex
cũng cung cấpmutations
để chúng ta có thể track sự thay đổi củastate
.
Cài đặt
CDN
Sử dụng link trực tiếp thông qua CDN links, cái này sẽ luôn trỏ tới versions mới nhất
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
NPM
npm install vuex --save
Yarn
yarn add vuex
Bây giờ mình sẽ thử cài vuex
vào project vuejs
thông qua nhé. Ở bài này mình cũng sử dụng luôn project mà mình đã tạo trong bài này, các bạn có thể xem qua phần cài đặt vuejs
.
Tiếp theo mình chạy lệnh
npm install vuex --save
Để biết đã cài thành công vuex
vào hay chưa các bạn vào project mở file package.json
nếu thấy dòng như này là đã cày đặt thành công.
"dependencies": {
"vue": "^2.5.2",
"vuex": "^3.3.0"
},
Bây giờ chúng ta cần tạo một kho lưu trữ tập trung của vuex
. Trước tiên vào folder src
tạo 1 folder có tên là store
. Trong folder này các bạn tạo thêm một file store.js
và thêm đoạn code sau.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // khai báo vue sử dụng plugin vuex
export const store = new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
getters: {
}
});
Cơ bản là chúng ta đã tạo ra một kho lưu trữ tập trung. GIờ chúng ta cần khai báo cho cho project để sử dụng cái kho dữ liệu này các bạn vào main.js
import Vue from 'vue'
import App from './App'
import { store } from './store/store'
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
Vậy là xong đơn giản phần cài đặt.
Các thành phần trong vuex
1. State
Để có thể hiểu state
một cách dễ hiểu hơn mình sẽ làm theo ví dụ để các bạn có cái nhìn trực quan hơn. Trước tiên các bạn tạo thêm cho mình một component
trong src/components
đặt tên là Result.vue
, và các bạn thêm đoạn code này vô.
`<template>
<div class="result">
<p></p>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
và tạo thêm một component
có tên là Counter.vue
<template>
<div class="count">
<button @click="increament">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
export default {
methods: {
increament () {
},
decrement () {
}
}
}
</script>
<style scoped>
</style>
và sửa code trong App.vue
nữa nhé
<template>
<div id="app">
<app-counter />
<app-result />
</div>
</template>
<script>
import AppCounter from './components/Counter'
import AppResult from './components/Result'
export default {
components: {
AppCounter,
AppResult
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- Như mình nói ở trên,
state
giống vớidata
trongcomponent
ở vuejs, chỉ khác làstate
lưu toàn bộ data cho cáccomponents
- Đây là 1 object bên trong chứa các thông tin có dữ liệu như : string, arrays hay một object.
- Các thông tin này được lưu trữ trong toàn ứng dụng.
Để khai báo một state
rất đơn giản, trong store.js
export const store = new Vuex.Store({
state: {
result: 10
},
mutations: {
},
actions: {
},
getters: {
}
});
Để sử dụng cái trạng thái result
này trong Result.vue
chúng ta sẽ gọi trong thuộc tính computed
. Một lưu ý nhỏ cho các bạn là computed
luôn chạy trước khi dữ liệu của chúng ta được load. Trong Result.vue
<template>
<div class="result">
<p>Kết quả là {{ result }}</p>
</div>
</template>
<script>
export default {
computed: {
result () {
return this.$store.state.result
}
}
}
</script>
<style scoped>
</style>
Cú pháp có phần hơi hơi lạ nhỉ, giải thích cho các bạn chút đó là this.$store
chính là tương ứng với cái đoạn mà chính ta khai báo const store = new Vuex.Store
, rồi để truy cập đến state
thì đơn giản như các bạn truy cập vào object thôi.
GIờ các bạn vào trong Counter.vue
thêm đoạn code vào script
<script>
export default {
methods: {
increament () {
this.$store.state.result++
},
decrement () {
this.$store.state.result--
}
}
}
</script>
OK các bạn chạy npm run dev
lên và vào http://localhost:8080/#/
xem kết quả nhé
Các bạn có thể dễ dàng thấy rằng, khi state
ở componet Counter.vue
thay đổi lập tức các component
khác sẽ cũng nhận được sẽ thay đổi đến từ state
đó. Rất nhanh đúng không nào.
2. Getters
- Đây là một function
- Nó không thể thay đổi được trạng thái nhưng có thể định dạng lại các thông tin trong
state
mà chúng ta cần - Giống với
computed
trong vue
Đôi khi chúng ta cần tính toán lại state
trong nhiều components, chúng ta k thể với mỗi components đó lại tính toán lại, như thế khá là dài dòng, thì việc sử dụng getters
trong trường hợp này là khá hợp lý.
Giá sử chúng ta vào store.js
vừa tạo ở trên và thử tạo ra một getters
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // khai báo vue sử dụng plugin vuex
export const store = new Vuex.Store({
state: {
result: 10
},
mutations: {
},
actions: {
},
getters: {
RESULT: state => {
return state.result * 5
}
}
});
Ở đây chúng ta đặt tên function là RESULT
, bạn không nhất thiết phải viết uppercase toàn bộ tên hàm, đây chỉ là một cách theo convention mà mình gợi ý cho các bạn. các bạn có thể đặt 1 tên nào đó tùy thích. Cạnh đó là parameter state
(cái này đặt tên là gì cũng được nhưng mình đặt trùng với state
cho dễ hiểu). Đặc biệt là cái parameter này chính là vào cái state
ở trên. Nên việc truy cập vào các giá trị bên trong cũng như bình thường.
Để sử dụng getters
thì cũng đơn giản như khi chúng ta gọi đến state
<script>
export default {
computed: {
result () {
return this.$store.getters.RESULT
}
}
}
</script>
Hoặc là chúng ta cũng có thể sử dụng các hàm trong cùng getters
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
3. Mutations
- Đây là một function
- Chúng chỉ có chức năng là thay đổi trạng thái
- Được gọi bằng
actions
- Cơ chế hoạt động là đồng bộ
Theo như document thì mutations
là cách duy nhất để có thể thay đổi được state
, mỗi mutations
có một type và handler. Handler function đây là nơi để thay đổi state
và nhận tham số đầu tiên là state
. Ví dụ
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
}
Chúng ta có thể thêm một tham số nữa và mutations
, tham số này được gọi là payload
.
Ví dụ :
mutations: {
increment (state, n) {
state.count += n
}
}
Và khi gọi đến mutations
.
this.$store.commit('increment', 10)
Payload
có thể là một đối tượng chứa nhiều trường.
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Khi gọi cũng đơn giản.
this.$store.commit('increment', {
amount: 10
})
hay có 1 cách gọi khác là sử dụng thuộc tính type
trong mutations
this.$store.commit({
type: 'increment',
amount: 10
})
4. Actions
- Đây là function, chứa các business logic
- Để thay đổi
state
phải commitmutations
- Có thể gọi các actions thông qua
dispatch
- Có thể chứa cơ chế bất đồng bộ
Ví dụ luôn cho dễ hình dung
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Để sử dụng actions ở một components
khác thì
store.dispatch('increment')
Có một tham số mà mình truyền vào trong một action là context
, đây là một đối tượng tập hợp các phương thức/ thuộc tính trong store, do đó chúng ta có thể gọi để context.commit
để thực hiện một action
, hay gọi đến context.state
, context.getters
. Để đơn giản hóa hơn thì trong ES5 chúng ta có thể gọi đến mutations
ở actions
như sau
actions: {
increment ({ commit }) {
commit('increment')
}
}
Kết luận
Trên đây là những kiến thức mà mình nghĩ là hướng tới những người mới bắt đầu, nó có thể chưa được sâu nên các bạn có thể xem thêm ở trang chủ của nó https://vuex.vuejs.org/. Cảm ơn các bạn đã đọc.
All rights reserved