专栏首页前端之旅Vue 全家桶学习笔记:Vuex

Vue 全家桶学习笔记:Vuex

1. 为什么要使用 Vuex?

来自 官方文档 的介绍:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

开发中,多个组件很可能会共享同一个状态(包括状态和数据),而组件和组件之间可能是兄弟关系,也可能是复杂的多层嵌套关系,如果单靠组件通信完成状态变更和同步,事情会变得很麻烦:

但是有了 Vuex 后,我们可以用它保管公共的状态,每当组件想要访问或者修改共享状态的时候,直接与 Vuex 进行交互就可以,而组件与一个“状态容器”的交互,比起前面的通信是要简单很多的:

2. 安装和使用

安装和 vue-router 是差不多的:

npm install vuex --save

之后,项目的 src 文件夹下会多出 store 文件夹。里面存放的 store.js 可以配置 Vuex(没有的话可以手动创建),大概结构是这样的:

// store.js

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

const store = new Vuex.Store({
    state:{...},
    getters:{...},
    mutations:{...},
     actions:{...},
    modules:{...}         
})

export default store

接着,在 main.js 中 引入 Vuex,并在 Vue 根实例下注册。

下面介绍 store.js 中的具体配置。

3. Vuex 的核心

Vuex 的核心包括 state、getters、mutations、actions、modules。

这几者的关系,我们可以先看来自官网的图:

2.1. state

Vuex 中的 state 相当于组件中的 data 属性。

store.js:

const store = new Vuex.Store({
    state:{
        name:'Sam'
    }
})

App.vue:

<div>{{$store.state.name}}</div>   <!--Sam-->
<hello-vuex></hello-vuex>   <!--Sam-->

<script>
import HelloVuex from './components/HelloVuex'
export default{
    components:{
        HelloVuex
    }
}
</script>

HelloVuex.vue:

<div>{{$store.state.name}}</div>   

<script>
export default{

}
</script>

2.2. getters

Vuex 中的 getters 相当于组件中的 computed 属性。getters 里面的方法接受两个参数,一个是 state ,我们通过它拿到 state 里的数据进行修改;一个是 getters。

const store = new Vuex.Store({
    state:{
        name:'Sam'
    },
    getters:{
        change(state){
            return state.name + 'and Jack'
        }
    }
})
<div>{{$store.getters.change}}</div>  <!--Sam and Jack-->

有时候,我们可能需要在外面传参,但是 getters 里面的方法只接受两个参数,这时候可以考虑让函数返回一个闭包 :

const store = new Vuex.Store({
    state:{
        name:'Sam'
    },
    getters:{
        change(state){
            return function(string){
                return state.name + string
            }
        }
    }
})
<div>{{$store.getters.change('and Tom')}}</div>  <!--Sam and Tom-->

2.3. mutations

Vuex 中的 mutations 相当于组件中的 methods 属性,要更改 Vuex 的 store 中的状态,唯一方法就是提交 mutation 。这里要注意,mutations 只负责处理同步的事件。

mutations 里面的方法接受两个参数,一个是 state,一个是从外面传进来的 payload(载荷),这个 payload 具体是什么,要看 commit 的风格。

下面的操作会把两个组件中的 “Sam” 都同步修改为 “Jack”:

const store = new Vuex.Store({
    state:{
        name:'Sam'
    },
    mutations:{
        change(state,payload){
            state.name = payload
        }
    }
})
<button @click="changeName">点击修改名字</button>
<div>{{$store.state.name}}</div>  
<hello-vuex></hello-vuex>  

<script>
import HelloVuex from './components/HelloVuex' 
export default {
    components:{
        HelloVuex
    }
    methods:{
        changeName(){
            this.$store.commit('change','Jack')
        }
    }
}
</script>

关于提交风格。我们可以在 commit 的时候直接传入一个对象:

<script>
export default {
    methods:{
        changeName(){
            this.$store.commit({
                type:'change',
                newName:'Jack'
            })
        }
    }
}
</script>

此时,我们接受的 payload 是提交时的这一整个对象,所以对应代码修改为:

const store = new Vuex.Store({
    state:{
        name:'Sam'
    },
    mutations:{
        change(state,payload){
            state.name = payload.newName
        }
    }
})

上面的修改是响应式的,视图和 devtools 中都能看到发生了相应的同步改变。但要注意,下面的代码不会发生响应式的改变:

const store = new Vuex.Store({
    state:{
        obj:{
            name:'Sam'
        }
    },
    mutations:{
        change(state,payload){
            state.obj.age = 24
        }
    }
})

这是因为,我们根本就没有在 obj 上初始化 age 这个属性。要做到响应式,可以修改为:

const store = new Vuex.Store({
    state:{
        obj:{
            name:'Sam'
        }
    },
    mutations:{
        change(state,payload){
            Vue.set(state.obj,age,24)
        }
    }
})

2.4. actions

mutations 中的方法必须是同步的,这样 devtools 才可以进行追踪,依次捕获状态前后的快照;而异步的方法是无法追踪的,因为并不清楚回调函数的执行时机。因此,异步的方法要写在 actions 中。下面的操作可以在 2s 后将 Sam 修改为 Jack:

// store.js

state:{
    name:'Sam'
}
mutations:{
    change(state,payload){
        state.name = payload
    }
},
actions:{
   Achange(context,payload){
       setTimeout(() => {
           context.commit('change',payload)
       },2000)
   } 
}
<!-- App.vue-->

<button @click="changeName">点击修改名字</button>    
<div>{{$store.state.name}}</div> 
<hello-vuex></hello-vuex>

<script>
import HelloVuex from './components/HelloVuex' 
export default {
    components:{
        HelloVuex
    },
    methods:{
        changeName(){
            this.$store.dispatch('Achange','Jack')  // 也接受 payload 参数 
        }
    }
}
</script>

因为是异步操作,所以我们很可能需要回调一个函数告知我们异步操作的结果,这时候可以用 Promise。也就是,将 actions 中的异步操作包裹在 Promise 中,并返回这个 Promise,之后在 dispatch 后调用 then(因为 dispatch 后返回的是一个 Promise 实例)。代码如下:

// store.js

state:{
    name:'Sam'
}
mutations:{
    change(state,payload){
        state.name = payload
    }
},
actions:{
   Achange(context,payload){
       return new Promise((resolve,reject) => {
           setTimeout(() => {
              context.commit('change',payload)
              resolve()
           },2000)
       })
   } 
}
<!--App.vue-->

<button @click="changeName">点击修改名字</button>
<div>{{$store.state.name}}</div>
<hello-vuex></hello-vuex>

<script>
import HelloVuex from './components/HelloVuex' 
export default {
    components:{
        HelloVuex
    },
    methods:{
        changeName(){
            this.$store
              .dispatch('Achange','Sam')
              .then(() => {
                console.log('异步操作成功')
            })     
        }
    }
}
</script>

2.5. modules

由于使用单一状态树(单一数据源),应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成若干个 module,每个 module 可以看作一个小的单元,它们各自都拥有自己的 state、mutations、actions、getters(甚至是 modules)。

1. state

单个 module (假定是a)最终还是会挂载到 store 的 state 上的:

所以实际上仍然还是单一状态树。可以通过 $store.state.a 拿到这个 module,再拿到它的 state。

// store.js

const moduleA = {
    state:{ age:20 }
}
const store = new Vuex.Store({
    state:{
        name:'Sam'
    },
    getters:{},
    mutations:{},
    actions:{},
    modules:{
        a: moduleA
    }
})
<!--App.vue-->

<div>{{$store.state.a.age}}</div>    <!--20-->
2 getters

用法和之前一致,首先会到 store 的 getters 下找对应方法,找不到再去单个 module 下的 getters 寻找。getters 中的方法接受的参数,一个是当前 module 下的 state,一个是当前 module 下的 getters

3 mutations

用法与之前一致,commit 的时候首先会到 store 的 mutations 下找对应方法,找不到再去单个 module 下的 mutations 寻找。

4 actions

用法和之前类似,依然是直接 dispatch,不过 actions 中的方法的参数 context 不再指向 store ,而是指向单个 module。

4. 类型常量

mutations 中的方法名实际上是一个字符串,也就是说,下面两种写法是等效的:

mutations:{
    change(){
        .....
    }
    // 等价于
    ['change'](){
        .....
    }
}

可以将所有的方法名各自用一个常量表示,并将它们写在一个统一的文件中,之后在 mutations 和 commit 中表示方法名字的时候,都用这个常量表示。

5. 项目结构

如果把所有的 mutations、getters、actions 等都放在 store.js 文件中,代码会变得很臃肿,所以我们可以进行分离。state 仍然保留在 store.js 文件中,actions、getters、mutations 分离到对应的文件中,各个 module 分离到 modules 文件夹对应的文件中。

import moduleA from './modules/moduleA'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'

const store = new Vuex.Store({
  state:{...},
  getters,
  actions,
  mutations,
  modules:{
    a:moduleA
  }
})

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 「译」创建一个Hexo主题-Part2:其他页面

    在这个系列教程中,你将学习怎么从零开始制作一个 Hexo 主题。 在 part1 中,我们已经着手动工并创建了首页。在这篇文章中,我们将运用所学完成剩余的页面。

    Chor
  • 如何用开源项目申请 JetBrains 产品的 license

    起因:一直以来敲代码的工具都是 Vscode,最近想试一试 WebStorm 了。

    Chor
  • 编译原理学习笔记-5:自顶向下语法分析

    在词法分析中,我们扫描输入源程序的每个字符,得到多种类型的单词(token),一系列的单词就构成了一条单词流。可以设想,单词流的某个部分有多个并排的单词,它们可...

    Chor
  • Web前端学习 第7章 Vue基础教程11 vuex

    我们不能直接修改state,需要定义mutation来操作state,示例代码如下所示:

    学习猿地
  • 【融职培训】Web前端学习 第7章 Vue基础教程11 vuex

    我们不能直接修改state,需要定义mutation来操作state,示例代码如下所示:

    学习猿地
  • 韩国公司KT将飞艇无人机与5G网络结合,用于搜救幸存者

    韩国电信公司KT公布了其5G应急网络服务Skyship,将该服务结合飞艇无人机在灾难发生后搜寻幸存者。

    AiTechYun
  • C#上位机串口控制12864显示

    实现的效果 ? ? ? ? ? 上面是用Proteus仿真的,,对了如果自己想用proteus仿真需要安装下面这个软件 ? 再看一下实物显示效果 ? ? ? ?...

    杨奉武
  • Amazon EC2 虚拟化技术演进:从 Xen 到 Nitro

    今年2月,由光环新网运营的AWS 中国(北京)区域和由西云数据运营的 AWS 中国(宁夏)区域发布新的实例类型,新的实例类型包括C5、C5d、R5、R5d。除了...

    SammyLiu
  • Java中使用Math.abs你入坑了?

    Math.abs函数是jdk中提供的一个用来返回入参绝对值的函数,也就是你输入一个负数,它会返回其对应绝对值正数,这个在大部分情况下是这样,但是特殊情况下,还是...

    加多
  • 国外高度重视云计算,出台政府采购云服务战略

    日前由工业和信息化部电信研究院发布的《云服务白皮书》指出,全球云服务市场目前呈现出规模尚小、发展趋势良好两大特点。 一是规模尚小。数据显示,2012年全球云计算...

    静一

扫码关注云+社区

领取腾讯云代金券