之前用Vue的组件传参可谓是比较麻烦且复杂的,代码也不容易维护。例如一个页面中A组件引入子组件B有没有办法声明一个全局变量达到两个组件共用呢?尤其是在用户登录方面,不同组件都需要用户的登录信息,一个个传难免影响性能——尤其是单页面中组件数目较多的时候
所以我们要使用Vuex!
什么是Vuex?
先大致看一下官方文档第一页
官方文档:https://vuex.vuejs.org/zh/
可以看到这是一个状态管理工具,简单点说就是可以帮我们管理全局变量。
官方所说:它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
意思差不多是我们让全局变量给其管理,我们对数据的操作也要遵循其规则。比如修改数据源 state、触发 actions 等等,都需要遵循它的规则,以此来达到让项目结构更加清晰且易于维护的目的。
state,驱动应用的数据源;
view,以声明方式将 state 映射到视图;
actions,响应在 view上的用户输入导致的状态变化。
必须要记住的两点是:
①Vuex中的变量状态是响应式的。类似于双向绑定
②我们既然要遵循规则,我们用户和程序是无法改变Vuex中的变量的,必须通过Vuex的API来操作,该接口就是通过commit mutation来实现的
那么首先定义我们的数据源State
新建文件夹src/vuex/store.js
vuex中的数据源,我们需要保存的数据就保存在这里
//文件的作用是在整个Vue项目中声明:我们要使用Vuex进行状态管理
//引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//创建Vuex实例
const store=new Vuex.Store({
state:{
username:"罗杰",
level:1
isLogin:true
}
})
//导出store
export default store
每一个 Vuex 应用的核心就是 store(仓库)。store 基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。然后我们在main.js文件中引入该文件,再在vue实例全局引入store对象;
import store from './vuex/store.js'
new Vue({
el: '#app',
router,
store,// 在vue实例全局引入store对象
components: { App },
template: '<App/>'
})store.js'
可以在页面通过 this.$store.state.属性
来获取我们定义的数据;例如在某一个Vue文件中加入
<div>{{this.$store.state.username}}的等级:LV.{{this.$store.state.level}}</div>
<button @click="addFun()">+</button>
<button @click="reductionFun()">-</button>
效果如下:
Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数:
调用如下
<div>距离满级还差:{{this.$store.getters.computedFull}}级</div>
【重点】mutation和action
既然知道了使用仓库 store 中的状态数据。接下来就要说怎么操作仓库 store 中的状态数据
明白一点,按照规则只有 mutation 能动 State
两者区别
1、流程顺序
“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation。
2、角色定位
基于流程顺序,二者扮演不同的角色。
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:业务代码、异步请求。
3、限制
角色不同,二者有不同的限制。
1.同步函数:当一个函数同步调用时,该函数不会被调用时不会立即返回,直到该函数所要做的事情全部做完了才返回
2.异步函数:当一个异步函数被调用时,该函数会立即返回,尽管这个函数规定的操作任务还没有完成
**Mutation:**必须同步执行。
**Action:**可以异步,但不能直接操作State。
代码添加如下:
//创建Vuex实例
const store=new Vuex.Store({
state:{
username:"罗杰",
level:1
},
getters:{//类似于Vue的computed
computedFull:function(state){
return 100-state.level
}
},
/*************************************************************/
mutations:{
add(state){//上面定义的state
state.level=state.level+1
},
reduction(state,n){//传参数,减多少级
state.level=state.level-n
}
},
//*********************************************************/
actions:{//注册action,类似于Vue里的mothods
addFun(context){
//接收一个与store实例具有相同方法的属性得到context对象
context.commit("add")
},
reductionFun(context,n){
context.commit("reduction",n)
}
}
})
然后用户界面添加方法,用户操作调用
<div>{{this.$store.state.username}}的等级:LV.{{this.$store.state.level}}</div>
<div>距离满级还差:{{this.$store.getters.computedFull}}级</div>
<button @click="addFun()">+</button>
<button @click="reductionFun()">-</button>
methods:{
addFun(){
this.$store.dispatch("addFun")//一次加1级
},
reductionFun(){
this.$store.dispatch("reductionFun",3)//一次减3级
}
【坑】多页面共用一个store?
既然是全局变量,是不是就意味着我在A页面改变了,B页面中显示也会被改变this.$store.state.level
的值?
当然不会!看是否共用一个store主要是看是否是同一个Vue实例。网上好多博客没说清楚,属实是一个坑,挣扎了好久折腾。
Vuex的应用不是针对多页面,而是为了解决组件的传值问题。现在我举个栗子,我弄了3个Vue界面
A界面修改state,然后跳转到B,而C是B的子组件。
按照以往我们要路由跳转传参,然后父子组件传参
你可能会理解这里是两个独立的页面,但并不是,他们是基于同一个Vue实例的,所以是共享store,不需要传参操作。可以看this
在A、B的created
钩子函数里console.log(this)
,通过查看uid判断是否是同一个vue实例
从A跳到B,如果打印了两次,说明是new了两个实例,如果只打印一次this
,说明是并没有创建新的Vue实例
通过this.$router.push({name:'Login'});
跳转到B页面(name:Login)
跳转后发现仍然可以读取store目前的值
B是页面,然后C组件是头像框,BC和A共享了store 的state
打印台并没有打印新的this,所以判断是三者共享。
如果你单独打开A和B,则不论你在A里怎么修改都不会影响到B\C,因为不属于同一个Vue实例自然也无法共享。主要就是看组件挂载情况,不是看页面数量。