vuex核心思想
1、vuex应用的核心就是store(仓库)。可以把store看作一个容器,它包含着应用中大部分的状态state。
2、vuex和单纯的全局对象有一下两点不同:
(1)vuex的状态存储是响应式的,当vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应的得到高效更新。
(2)你不能直接改变store中的状态,改变store中的状态的唯一途径就是显式地提交(commit)mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
3、vuex中最关键的是store对象,这是vuex的核心。每个vue应用仅有一个store对象。
4、vuex 对其进行了 vue 的适配,借助 vue 的 细粒度响应机制(文档说的)进行高效的状态更新,基本上大型项目如果要进行状态管理,直接使用 vuex 就可以。
为了再vue组件中访问this.$store属性,需要为vue实例提供创建好的store,vuex提供了一个从根组件向所有子组件以store选项的方式注入该store的机制,npm的方式,系统自动生成,代码在main.js中:
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
使用npm项目,在根目录store文件夹下面有一个index.js文件,里面的内容自动生成如下:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count:20
},
mutations: {},
actions: {},
modules: {},
});
一定要确保 Vue.use(Vuex); 在 new Vuex.Store({}) 之前调用。
调用方法:
(1)在组件中的js中调用方法:this.$store.state.xx
methods:{
myCount(){
console.log(this.$store.state.count);//输出结果20
}
}
(2)在组件的页面中调用方法:$store.state.xx
<div>{{$store.state.count}}</div><!--显示结果20-->
(3)此时如果我们想要修改count的值怎么办呢?使用mutations
实例1,:
在mutations中写一个方法,名字自定义:
mutations:{
increment(s){//变量默认表示的是state
s.count=s.count+100;
}
}
1.1全局修改count的值
在全局main.js中使用commit调用mutations中的方法
store.commit('increment')
组件中调用count,会发现count的值已经发生变化:
methods:{
myCount(){
console.log(this.$store.state.count);//输出结果120
}
}
1.2局部修改count的值:
直接在vue组件页面中使用方法:
methods:{
myCount(){
this.$store.commit('increment')
console.log( this.$store.state.count);
}
}
解释,我们为什么要通过mutation的方法去修改state中的值呢,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性computed中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
5个核心概念:
注意:
1.store中存储的状态是响应式的,当组件从store中读取状态时,如果store中的状态发生了改变,那么相应的组件也会得到更新。
2.不能直接改变store中的状态。改变store中的状态的唯一途径是提交(commit)mutations。这样使得我们可以方便地跟踪每一个状态的变化。
一个完整的store的结果是这样的
const store = new Vuex.Store({
state: {
// 存放状态
},
getters: {
// state的计算属性
},
mutations: {
// 更改state中状态的逻辑,同步操作
},
actions: {
// 提交mutation,异步操作
},
// 如果将store分成一个个的模块的话,则需要用到modules。
//然后在每一个module中写state, getters, mutations, actions等。
modules: {
a: moduleA,
b: moduleB,
// ...
}
});
1.state 存放状态
简介:state上存放的,说的简单一些就是变量,也就是所谓的状态。没有使用 state 的时候,我们都是直接在 data 中进行初始化的,但是有了 state 之后,我们就把 data 上的数据转移到 state 上去了。另外有些状态是组件私有的状态,称为组件的局部状态,我们不需要把这部分状态放在store中去。
state是存储的单一状态,是存储的基本数据。
使用方法:
在store文件夹下面的index.js中定义state状态
export default new Vuex.Store({
state: {
count:"hello world",
age:12
}
})
调用:在home.vue中使用:
由于vuex的状态是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态。
//在computed中定义一下
computed:{
getAge(){
return this.$store.state.age;
}
}
//在mounted中打印出来
mounted(){
console.log(this.getAge);
}
打印结果:12
每当state中的count变化的时候,都会重新求取计算属性,并且触发更新关联的DOM。
直接在vue组件页面中调用:
<div>
{{$store.state.count}}
</div>
输出结果:hello world
1.1 mapState
当一个组件获取多种状态的时候,则在计算属性中要写多个函数。为了方便,可以使用mapState辅助函数来帮我们生成计算属性。
实例,在index.js中
state: {
count:"hello world",
age:22
},
在home.vue中,mapState可以写多个函数。
先在vue页面中导入mapState
import {mapState} from 'vuex'
computed:mapState({
x:i=>i.age,//此处的i表示的是store里面的state
countPlus(s){//此处的s表示的是store里面的state
return s.age+s.count
}
}),
mounted(){
console.log(this.countPlus);//输出结果 22hello world
}
上面是通过mapState的对象来赋值的,还可以通过mapState的数组来赋值,这种方式很简洁,但是组件中的state的名称就跟store中映射过来的同名
computed:mapState(['age','count']),
mounted(){
console.log(this.age);//22
console.log(this.count);//count
}
实例2:
mapState 函数返回的是一个对象,为了将它里面的计算属性与组件本身的局部计算属性组合起来,需要用到对象扩展运算符
data(){
localState: 1
}
computed: {
localState () {
... mapState ({
})
}
这样,mapState中的计算属性就与localState计算属性混合一起了。
2. getters 是state的计算属性
作用:有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数。此时可以用到getters,getters可以看作是store的计算属性,其参数为state。
getters是store的计算属性,对state的加工,是派生出来的数据。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。
实例:
在store的index.js中如下:
state: {//state中定义了一个数字todos,数组里面有两个对象
todos:
[{id:1,text:'reading',done:true},{id:2,text:'reading',done:false}]
},
getters:{
//在getters中定义一个方法,其中参数s表示的是state,对state中的todos进行过滤,过滤done为true的数据
doneTodos:s=>{
return s.todos.filter(d=>d.done)
}
},
如何在vue页面使用呢,在home.vue中如下:
computed:{
dd(){
return this.$store.getters.doneTodos;
}
},
mounted(){//在mounted中打印出来
console.log(this.dd);//[{id:1,text:'reading',done:true}]
}
还可以再home.vue页面中使用mapGetter中获取:
import {mapGetters} from 'vuex'//导入mapGetters
computed:{//computed中使用扩展运算符获取
...mapGetters(['doneTodos'])
},
mounted(){
console.log(this.doneTodos)//打印出来[{id:1,text:'reading',done:true}]
}
3. mutations 更改状态的逻辑,同步操作
简介:
1、mutations里面是如何更改state中状态的逻辑。更改Vuex中的state的唯一方法是,提交mutation,即store.commit(‘increment’)。
2、更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
3、那么mutation应该在哪里提交呢? 因为js是基于事件驱动的,所以改变状态的逻辑肯定是由事件来驱动的,所以store.commit(‘increment’)是在组件的methods中来执行的。
4、mutations提交更改数据,使用store.commit方法更改state存储的状态。(mutations同步函数)
注:mutations里面的参数可以有多个,第一个默认是state,第二个及后面为自定义的参数,参数可以是对象。
3.1提交载荷(payload)
可以向commit传入额外的参数,即mutation的载荷。
实例:
在store文件夹里面的index.js中如下:
state: {
count:3
},
mutations: {
//一个方法increacount,有两个参数,其中第一个参数s表示的是state
increment(s,n)
{
s.count+=n;
}
},
在home.vue中调用:
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
methods:{
myCrease(){
this.$store.commit('increment',20);
console.log(this.$store.state.count)
}
}
mounted(){
this.myCrease();//输出结果23
}
上面的如果不在home中调用,可以直接写在main.js中:
store.commit('increacount', {amount: 10});//13
实例二:mutations里面的方法第2个参数还可以是一个对象。
state: {
count:3
},
mutations: {
increment(s,p)
{
s.count+=p.amount;
}
},
vue页面中调用:
methods:{
myMet(){
this.$store.commit('increment',{amount:90})
console.log(this.$store.state.count)
}
},
mounted(){
this.myMet();//93
}
注意:mutation必须是同步函数,不能是异步的,这是为了调试的方便。
同步和异步的概念
定义:同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)。同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反 调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用
比方说:你去商城买东西,你看上了一款手机,能和店家说你一个这款手机,他就去仓库拿货,你得在店里等着,不能离开,这叫做同步。现在你买手机赶时髦直接去京东下单,下单完成后你就可用做其他时间(追剧、打王者、lol)等货到了去签收就ok了.这就叫异步。
4. action 提交mutations,异步操作,使用dispatch分发
作用:因为mutations中只能是同步操作,但是在实际的项目中,会有异步操作,那么actions就是为了异步操作而设置的。这样,就变成了在action中去提交mutation,然后在组件的methods中去提交action。只是提交actions的时候使用的是dispatch(派遣,发送)函数,而mutations则是用commit函数。
actions像一个装饰器,提交mutation,而不是直接变更状态。(actions可以包含任何异步操作)
在 mutation 中混合异步调用会导致你的程序很难调试。例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。在 Vuex 中,mutation 都是同步事务:
Action 类似于 mutation,不同在于:
1、Action 提交的是 mutation,而不是直接变更状态。
2、Action 可以包含任意异步操作。
实例:
在store的index.js中
state:{
count:20
},
mutations:{
increment(s){
s.count=s.count+100;
}
},
actions:{
myin(c){
c.commit('increment')
}
}
在组件中调用:
methods:{
myfun(){
//Action 通过 store.dispatch 方法触发
this.$store.dispatch('myin');
console.log( this.$store.state.count);//输出结果120
}
}
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
5.Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
简介:Module是store分割的模块,每个模块拥有自己的state、getters、mutations、actions。
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 的状态
6.辅助函数
Vuex提供了mapState、MapGetters、MapActions、mapMutations等辅助函数给开发在vm中处理store。
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
应用层级的状态应该集中到单个 store 对象中。
提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例: