---
tags: Vue.js
---
# VUEX
## 安裝
打開終端機輸入 `npm i --save vuex` 即可
## 結構說明
`Vuex` 是一個專為 `Vue` 開發的狀態管理模式 它可以集中存儲管理應用的所有組件的狀態 通常用於大型結構或網站的項目上
它的結構不同於 `vue` 但又類似於 `vue`
結構如下:
- `state` 保存資料狀態用 類似於 `vue` 中的 `data`
- `actions` 進行非同步的行為方法及獲取資料 類似於 `vue` 中的 `methods`
- `getter` 資料呈現的方式 類似於 `vue` 中的元件
- `mutation` 該變資料內容的方法 跟 `vue` 中的 `methods` 不同 在 `vuex` 中無法在獲取資料的同時做更改 需通過 `mutation` 才能更改資料狀態
* 這裏建議把 `mutations` 的方法名統一使用全大寫取名 另外 `mutations` 通常名字會跟 `state` 取一樣的 差別在一個全大寫一個全小寫 較好辨認
## 使用
首先在項目中的 `src` 資料夾裡面增加一個資料夾 `store` 它是用來放彙整專用的檔案的 該檔案我們取為 `index.js`
打開 `index.js` 在裡面寫入以下代碼:
```js
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
export default new Vuex.Store({})
```
接下來我們到項目中的 `main.js` 中引入 `store` 資料夾 引入方式同 `router` 一樣 先 `import` 然後在 `new Vue` 實例中加入 `store`
這樣就完成使用了
## 通過 Vuex 切換 isLoading 狀態
接下來打開 vuex 的 `index.js` 然後在 `Vuex.Store({})` 中添加結構
如下:
```js
export default new Vuex.Store({
state: {
isLoading: false
},
actions: {
// 創建一個方法為 updateLoading 其中參數的 context 為 vuex 的參數,status 為自定義變量名稱
updateLoading(context, status) {
// 用 context.commit 方法切換資料狀態 參數1即 mutations 的方法
context.commit('LOADING', status)
}
},
mutations: {
// 創建一個方法為 LOADING 其中參數的 state 為 vuex 的保存資料狀態那個 state 參數的 status 就是剛才上面 actions 的變量
LOADING(state, status) {
// 然後用傳進來的變量改變 isLoading 的值 如下
state.isLoading = status
}
}
})
```
然後回到 `App.vue` 中 把 `data` 中的 `isLoading` 拿掉
找到每一個 `methods` 需要切換 `isLoading` 的地方 改寫為
```js
// isLoading = true
vm.$store.dispatch("updateLoading", true);
// isLoading = false
vm.$store.dispatch("updateLoading", false);
```
最後在 `export default{}` 裡添加上
```js
computed: {
isLoading() {
return this.$store.state.isLoading;
},
},
```
即完成
## 嚴謹模式
在 vuex 中可以通過在 `state` 前面添加一行 `strict: true` 來讓 vuex 自動偵測語法上的錯誤 並在 `Console` 中給出警告
該模式是用來保證每次資料狀態的變更都可以被 `vue` 的開發者工具所偵測到
## 把 vue 改成 vuex
首先我們將原本在 vue 實例中的 `methods` 方法複製到 vuex 的 `index.js` 中
* `methods` 方法複製到 `actions` 裡面
* `methods` 更改 `data` 的地方複製到 `mutations` 裡面
* `methods` 需要操作到的 `data` 則新增到 `state` 裡面
接下來給所有 `actions` 方法各自添加上 `context` `payload` 這兩個參數
把操作 `data` 的地方藉由 `context.commit()` 方法呼叫 `mutations` 對應的資料
如下:
```js
actions:{
getProducts(context, payload) {
const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/products/all`
context.commit('LOADING', true)
this.$http.get(url).then((response) => {
console.log("取得產品列表:", response)
payload = response.data.products
context.commit('PRODUCTS', payload)
context.commit('CATEGORIES', payload)
context.commit('LOADING', false)
})
}
}
```
這裏要注意的是
* `this.$http` 原本是在實例中使用 `ajax` 的地方
在 `vuex` 中因為我們沒有引入 `axios` `vue-axios` 所以 `this.$http` 是找不到的
要解決該問題我們只需將 `axios` 引入到 `index.js` 中 並把 `this.$http` 改成 `axios` 即可
* 另外如果該方法需傳遞超過一個以上的參數則可用 `{}` 把多個參數包起來
接下來給所有 `mutations` 方法各自添加上 `state` `payload` 這兩個參數
通過 `state.xxx` 呼叫對應的資料 並賦值為傳進來的 `payload` 即可
如下:
```js
mutations: {
PRODUCTS(state, payload) {
state.products = payload
},
CATEGORIES(state, payload) {
const categories = new Set()
payload.forEach((item) => {
categories.add(item.category)
})
state.categories = Array.from(categories)
}
}
```
最後回到 vue 實例中 把原本的 `methods` 改成 `this.$store.dispatch('actions名稱')`
然後把 `data` 中的 `products` 與 `categories` 移除 並在 vue 的 `computed` 中添加以下代碼:
```js
computed:{
products() {
return this.$store.state.products;
},
categories() {
return this.$store.state.categories;
},
}
```
以上就是一個完整的修改範例了
## mapXXX 運用方法
之前提過在 `vuex`還有一個 `getters` 類似於 `vue` 中的 `computed`
它的使用方式如下:
```js
// vuex 的 index.js 檔
getters: {
products(state) {
return state.products
},
categories(state) {
return state.categories
},
}
// .vue 檔
import { mapGetters } from "vuex";
computed:{
...mapGetters(["products", "categories"]),
}
```
另外 在 `vuex` 中的其他結構也可以通過上述這種 `mapXXX` 方式來使用
但要注意的是 ***該方法不適用於需傳遞參數的地方***
若需要傳遞參數則還是要通過 `vm.$store.dispatch("方法名", 參數)` 才可以
## vuex 模組化
我們把範例檔案以購物車相關與商品相關拆分出兩個模塊 分別為 `PModule` 與 `CModule`
首先在 `store` 資料夾內新增 `PModule.js` 與 `CModule.js` 然後在 `index.js` 的結構最下方添加一個屬性 `modules`
接下來把 `PModule.js` 與 `CModule.js` 引入到 `index.js` 中 將引入的名稱設為 `modules` 屬性的值
如下:
```js
export default new Vuex.Store({
...,
modules: {
PModule,
CModule
}
})
```
然後我們開啟 `PModule.js` 與 `CModule.js` 先引入 `axios` 接下來一樣寫入結構
範例如下:
```js
import axios from "axios"
export default {
state:{},
actions:{},
mutations:{},
getters:{}
}
```
把原本在 `index.js` 中有關購物車的部分全部改放到 `CModule.js` 中
把原本在 `index.js` 中有關商品的部分則全部改放到 `PModule.js` 中
接下來稍微說明一下模組中的結構問題:
* 在模組中的 `state` 是區域性的 即只在該模組中有被定義 若你在 `.vue` 檔中通過 `this.$store.state.xxx` 則無法獲取到模組中的 `state`
- 若要在 `.vue` 檔中取得模組中的 `state` 屬性則改為:
```js
vm.$store.state.PModule.products;
```
* 在模組中的其他結構是全域的 即在 `.vue` 檔中通過 `this.$store.dispatch("方法名")` 是可以的
* 想把模組中的所有結構統一改成區域性是可以的 只需在模組結構的第一行加上 `namespaced: true` 即可
- 這樣做的好處是當你在 `index.js` 或其他模組檔案中有任一結構出現重複的名字時不會出錯
- 這樣做以後在 `.vue` 中就不能直接通過 `this.$store.dispatch("方法名")` 調用方法了
+ 解決方法如下:
```js
// 原本在 App.vue 中的獲取產品列表為
getProducts()
// 在 index.js 中的獲取產品列表需於 .vue 檔中改為
this.$store.dispatch('getProducts')
// 在 PModule.js 中的獲取產品列表需於 .vue 檔中改為
this.$store.dispatch('PModule/getProducts')
```
* 最後補充 `mapGetters` 與 `mapActions` 在模組化後的獲取方法:
```js
...mapGetters("PModule", ["products", "categories"]);
...mapActions("PModule", ["getProducts"]);
```