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里的数据做了加密操作)
参考: