VueX的基本使用
1、什么是VueX
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
以上是官方文檔的介紹,我淺顯的理解VueX就是用於存儲一些全局配置,可以在不同的組件中使用這些全局配置,根據不同的使用場景提供了異步獲取和計算屬性式獲取的方式。
2、VueX的使用場景
傳統傳值方式:

VueX傳值方式:

從上圖我們可以看出,使用VueX后,組件之間值的傳遞變的異常簡單。可以進行統一管理,統一使用。減少了請求后台的次數。沒有了繁瑣的props和$refs。
但是VueX也有他的弊端,例如刷新頁面后state將會變為初始狀態,對於頻繁變化的結果也不適於存儲在VueX內,因此不建議將所有狀態都使用VueX來管理,否則你需要在每個使用的頁面都調用一次actions(代碼分層的方式除外)。
VueX最常見的使用場景就是存儲用戶的信息、保存token、主題配置等。一種更加高級的用法是使用VueX將和后端交互的axios異步請求進行封裝,實現代碼的分層,讓組件中的代碼更加關注與頁面交互。
3、quickstart
安裝vuex
npm install vuex --save
在根目錄創建store目錄,在目錄內創建index.js,在其內部創建
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {}
})
在main.js內引用store
import Vue from 'vue'
import App from './App.vue'
import router from './router' //重要
import store from './store' //重要
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
new Vue({
router,
store, //重要
render: h => h(App)
}).$mount('#app')
如果使用腳手架創建項目,且默認選擇了vuex,系統會自動為我們創建上述內容。
4、state
4.1、state介紹
VueX中的state就是真正保存數據的地方,它的寫法和作用類似於vue對象內的data。
- state內存放的數據如果發生了修改,依賴這個屬性的組件的數據也會發生變化。
- state內的屬性無法直接修改,必須通過mutations修改。
- state內可以存儲一般數據類型,也可以存儲對象類型。
4.2、在state內添加一些數據
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
title: 'hello world',
userInfo: {
name: 'rayfoo',
age: 24,
car: ['Porsche', 'BMW']
}
},
mutations: {},
actions: {},
modules: {}
})
4.3、在頁面中使用state
// 在.vue內使用
<h1 v-text="$store.state.title"></h1>
{{$store.state.title}}
// 在js中使用
this.$store.state.title
4.4、語法糖...mapState
為了簡化state的訪問方式,VueX提供了訪問語法糖,可以像訪問當前項目計算屬性一樣訪問state。
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['title'])
}
}
頁面調用
<h1 v-text="title"></h1>
5、mutations
5.1、mutations介紹
前面我們介紹了,state內的數據是無法直接修改的。如果要修改state內的元素,必須使用mutations。如果你學過java語言,那么可以用setter方法來簡單的理解mutations。mutations的存在就是為了修改state內的數據。
由於mutations和我們下面要說的actions使用時非常相似,所以mutations的名稱通常用大寫加下划線表示
4.2、聲明mutations
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
title: 'hello world'
},
mutations: {
SET_TITLE (state, _title) {
state.title = _title
}
},
actions: {},
modules: {}
})
我們可以看到,mutations的寫法類似於method,它的第一個參數是固定的state,表示要修改的state。后面的參數我們可以自由指定或者省略。
上面的案例中我們通過setTitle將state內的title修改為了新的title,而新的title可以由參數的形式傳遞進來。
4.3、調用mutations
上面的案例,我們創建了一個mutations,它只是一個聲明的過程,如果我們需要調用,則需要使用下面的代碼:
this.$store.SET_TITLE("setTitle","hello Vuex")
4.4、語法糖...mapMutations
使用this.$store.commit這種操作形式過於繁瑣,mutations提供了語法糖,能更像調用當前頁面的methods一樣對其調用。
用法如下
<script>
import { mapMutations } from 'vuex'
export default {
created () {
// 使用setTitle
this.SET_TITLE('你好,mapMutations')
},
methods: {
...mapMutations(['SET_TITLE'])
}
}
</script>
6、getters
6.1、getters介紹
前面我們用java中的setter比喻了mutations,那getters也可以比喻為java中的getter方法。如果沒學過java也沒關系,你一定用過Vue中的計算屬性computed,getters的用法就類似於computed。
getters可以單純的獲取state中的內容,也可以對結果做過濾、計算、截取等等一系列的操作,來拿到我們需要的結果。
getters默認是不支持參數的,我們可以通過返回一個函數的形式來完成參數的傳遞。
6.2、聲明getters
下面的案例我們通過getter,將所有的title以加上書名號的形式返回:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
title: 'hello world'
},
mutations: {},
actions: {},
getters: {
getFomatTitle (state) {
return `《${state.title}》`
}
},
modules: {}
})
6.3、使用getters
//.vue頁面內
$store.getters.getFomatTitle
// javascript
this.$store.getters.getFomatTitle
6.4、向getters傳遞參數
下面的案例演示了通過返回函數的形式向getters內傳遞參數
聲明
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
studentList: [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
{ id: 3, name: 'wangwu' }
]
},
mutations: {},
actions: {},
getters: {
getStudentById: state => id => {
return state.studentList.find(student => student.id == id)
}
},
modules: {}
})
調用
this.$store.getters.getStudentById(1)
4.5、語法糖...mapGetters
和mutations意義,getters也擁有自己的語法糖,下面的案例演示了如何使用getters的語法糖
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['getFomatTitle', 'getStudentById'])
}
}
通過...mapGetters引入后,就可以像使用當前組件中的計算屬性一樣使用VueX的getters了。
頁面使用
<h3 v-text="student.name"></h3>
<h3 v-text="getStudentById(2).name"></h3>
7、actions
7.1、actions介紹
actions可以理解為mutations的異步版本,由於修改state內的數據必須使用mutations,所以actions內還是必須使用mutations的。
7.2、聲明actions
在vue里最常見的異步操作就是調用axios請求,下面我們通過一個actions調用axios的案例來認識一下它。
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
title: 'hello world'
},
mutations: {
SET_TITLE (state, _title) {
state.title = _title
}
},
actions: {
setAsyncTitle (context, title) {
axios.get(`/api/get-title?title=${title}`).then(resp => {
context.commit('SET_TITLEX', resp.data)
})
}
},
getters: {},
modules: {}
})
7.3、使用actions
this.$store.dispatch('setAsyncTitle', '你好,Axios!!!')
7.4、語法糖...mapActions
import { mapActions } from 'vuex'
export default {
created () {
this.setAsyncTitle('你好,Axios!!!')
},
methods: {
...mapActions(['setAsyncTitle'])
}
}
8、modules
8.1、modules介紹
前面的案例我們都是在VueX自動生成的./store/index.js中編寫的,真實的項目中,VueX中保存的內容會很多,保存在一個文件中會很難管理,通過modules可以更方便的管理VueX。
每個module都可以擁有自己的state、mutations、getters、actions。
在實際開發中,modules中通常不會包含getters,而是單獨創建一個js來保存VueX中的所有getters
8.2、modules的使用
簡單的使用形式:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
8.3、多文件形式引用Modules
多文件的引用形式:以下代碼引用自ruoyi-vue
module
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: [],
permissions: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
}
},
actions: {
// 登錄
Login({ commit }, userInfo) {
// 略
},
// 獲取用戶信息
GetInfo({ commit, state }) {
// 略
},
// 退出系統
LogOut({ commit, state }) {
// 略
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
getters
const getters = {
sidebar: state => state.app.sidebar,
size: state => state.app.size,
device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
introduction: state => state.user.introduction,
roles: state => state.user.roles,
permissions: state => state.user.permissions,
permission_routes: state => state.permission.routes,
topbarRouters:state => state.permission.topbarRouters,
defaultRoutes:state => state.permission.defaultRoutes,
sidebarRouters:state => state.permission.sidebarRouters,
}
export default getters
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import user from './modules/user'
import tagsView from './modules/tagsView'
import permission from './modules/permission'
import settings from './modules/settings'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
user,
tagsView,
permission,
settings
},
getters
})
export default store
使用
// state and getters
this.$store.state.模塊名.state
// actions
this.$store.dispatch('模塊名/actionsName',prams)
// mutations 由於mutations不區分模塊,所以注意命名全局唯一性
this.$store.commit("mutationsName", params);
// 因為上面說了 getters沒有在每個modules內都寫,所以可以直接用
this.getters.gettersName
// 如果使用module內的getters
this.$store.getters["模塊名/gettersName"]
8.4、模塊中的內容使用...mapXXX
# state
...mapState({
userName: state => state.user.name,
userAge: state=> state.user.age
})
# mutations getters actions 沒有命名空間的限定,所以要保證全局的唯一性
...mapMutations(['mutationsName'])
...mapGetters(['gettersName'])
...mapActions(['actionsName'])
9、刷新頁面數據丟失問題
雖然VueX可以保存全局狀態,但頁面刷新后。VueX中的狀態會被重新初始化。如果當前頁面的值恰好是在其他組件內mutations或actions后產生的,那值可能會顯示異常,造成數據丟失的感覺。例如如果把獲取用戶信息的操作放在了登錄頁面,那么進入系統后刷新就會發生上述情況。
解決辦法有如下三種思路:
1、將vuex中的數據保存到瀏覽器緩存中如(cookie、localStorage和sessionStorage)。
2、在會發生數據丟失的頁面重新發送actions、mutations請求。
方法1的缺點是不安全且不適用於大量數據的存儲,方法2適用於少量數據且接口訪問速度比較快的情況。所以有了方案3,方案3就是方案1和方案2的結合。
3、頁面刷新的時候異步請求后台數據,然后動態更新vuex中的數據,其中會有一種情況就是,網絡延遲、數據量大的問題。此時還沒等vuex獲取到后台返回的數據,頁面就已經加載完成了,這樣就會造成數據丟失。所以該解決方案就是,監聽瀏覽器刷新前事件,在瀏覽器刷新之前就把vuex里的數據保存至sessionStorage中,刷新成功后如果異步請求的數據還沒返回則直接獲取sessionStorage里的數據,否則獲取vuex里的數據。(只有刷新后還沒取到后台數據,才會從sessionStorage里取。確保數據的安全性,就算獲取sessionStorage里的數據也是安全的,因為每次刷新都會重新賦值,不必擔心數據被篡改問題,其次就是對sessionStorage里的數據做了加密操作)
參考:
