vuex初步认识

简介: Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

文章目录

Vuex 是什么?

什么是“状态管理模式”?

State单一状态树

在 Vue 组件中获得 Vuex 状态

mapState 辅助函数

对象展开运算符

组件仍然保有局部状态

Getter

通过属性访问

通过方法访问

mapGetters 辅助函数

Mutation

提交载荷(Payload)

对象风格的提交方式

使用常量替代 Mutation 事件类型

Mutation 必须是同步函数

在组件中提交 Mutation

Action

Module

Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。


什么是“状态管理模式”?



Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。


如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它。


State单一状态树

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。


在 Vue 组件中获得 Vuex 状态

Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:


// Vue 的原型上面添加一个属性,$store=> 指向仓库部分

// this.$store 访问到仓库数据以及方法

Vue.use(Vuex);

export default new Vuex.Store({

 // Vuex 五大模块

 // vue=> data 定义页面之间共享数据 => 响应式的 => 定义方式和vue data是一样的

 // state 本身是一个JS对象,页面刷新的时候,如果你没有做持久化处理,数据重新清空

 state: {

   count: 666,

 },

 // vue 中的计算属性

 getters: {

   total: function (state) {

     return state.count * 100;

   },

 },

 mutations: {},

 actions: {},

 modules: {},

});



<!-- 测试页面 -->

<template>

 <div class="">

   <button @click="count++">+1</button>

   <h1>{{ count }}</h1>

   <h2>{{ $store.state.count }}</h2>

   <h3>{{ $store.getters.total }}</h3>

 </div>

</template>


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

mapState 辅助函数

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:


// 在单独构建的版本中辅助函数为 Vuex.mapState

import { mapState } from 'vuex'


export default {

 // ...

 computed: mapState({

   // 箭头函数可使代码更简练

   count: state => state.count,


   // 传字符串参数 'count' 等同于 `state => state.count`

   countAlias: 'count',


   // 为了能够使用 `this` 获取局部状态,必须使用常规函数

   countPlusLocalState (state) {

     return state.count + this.localCount

   }

 })

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。


computed: mapState([

 // 映射 this.count 为 store.state.count

 'count'

])

1

2

3

4

对象展开运算符

mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:


computed: {

 localComputed () { /* ... */ },

 // 使用对象展开运算符将此对象混入到外部对象中

 ...mapState({

   // ...

 })

}

1

2

3

4

5

6

7

组件仍然保有局部状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。


Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。


 getters: {

   total: function (state) {

     return state.count * 100;

   },

 },

1

2

3

4

5

通过属性访问

Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:


<h3>{{ $store.getters.total }}</h3>

1

Getter 也可以接受其他 getter 作为第二个参数:


getters: {

 // ...

 doneTodosCount (state, getters) {

   return getters.doneTodos.length

 }

}


store.getters.doneTodosCount // -> 1

1

2

3

4

5

6

7

8

通过方法访问

你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。


getters: {

 // ...

 getTodoById: (state) => (id) => {

   return state.todos.find(todo => todo.id === id)

 }

}



store.getters.getTodoById(2) // -> { id: 2, text: '...', done: fa

1

2

3

4

5

6

7

8

9

mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:


import { mapGetters } from 'vuex'


export default {

 // ...

 computed: {

 // 使用对象展开运算符将 getter 混入 computed 对象中

   ...mapGetters([

     'doneTodosCount',

     'anotherGetter',

     // ...

   ])

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:


const INCREMENT_BY = 'INCREMENT_BY';

mutations: {

   // increment: function (state) {},

   // 同步提交

   increment(state, payload) {

     // console.log(payload);

     // state.count += payload;

     // console.log(payload);

     // state.count += payload.amount;

     console.log(payload);

     state.count += payload;

   },

   [INCREMENT_BY](state, payload) {

     state.count += payload;

   },

},


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

你不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:


methods: {

   ...mapMutations(["increment", "INCREMENT_BY"]),

   handleClick: function () {

     //   this.increment(10);

     this.INCREMENT_BY(10);

   },

 },

1

2

3

4

5

6

7

提交载荷(Payload)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:


mutations: {

 increment (state, payload) {

   state.count += payload.amount

 }

}

store.commit('increment', {

 amount: 10

})

1

2

3

4

5

6

7

8

对象风格的提交方式

store.commit({

 type: 'increment',

 amount: 10

})

1

2

3

4

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此处理函数保持不变


使用常量替代 Mutation 事件类型

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然


Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数


在组件中提交 Mutation

你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。


 methods: {

   ...mapMutations(["increment", "INCREMENT_BY"]), // this.increment => this.$store.commit

   handleClick: function () {

     //   this.increment(10);

     this.INCREMENT_BY(10);

   },

 },

1

2

3

4

5

6

7

Action

Action 类似于 mutation,不同在于:

●Action 提交的是 mutation,而不是直接变更状态。

●Action 可以包含任意异步操作。


 actions: {

   // 获取用户的个人信息的

   getUserInfo: function (store) {

   //发送请求

     myAxios.get('user').then((res) => {

       // console.log(res);

       // res.data

       store.commit(SAVE_USER, res.data);

     });

   },

 },

1

2

3

4

5

6

7

8

9

10

11

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。


Module

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

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:


import myAxios from '@/api/instance';

const SAVE_USER = 'SAVE_USER';

const login = {

 namespaced: true,

 state: () => ({

   user: {},

 }),

 getters: {},

 mutations: {

   [SAVE_USER](state, payload) {

     state.user = payload;

   },

 },

 actions: {

   // 获取用户的个人信息的

   getUserInfo: function (store) {

     myAxios.get('user').then((res) => {

       // console.log(res);

       // res.data

       store.commit(SAVE_USER, res.data);

     });

   },

 },

};

export default login;



1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

const INCREMENT_BY = 'INCREMENT_BY';

const test = {

 namespaced: true,

 state: () => ({

   count: 666,

 }),

 getters: {

   total: function (state) {

     return state.count * 100;

   },

 },

 mutations: {

   // increment: function (state) {},

   // 同步提交

   increment(state, payload) {

     // console.log(payload);

     // state.count += payload;

     // console.log(payload);

     // state.count += payload.amount;

     console.log(payload);

     state.count += payload;

   },

   // ['INCREMENT_BY'](state, payload) {

   //   state.count += payload;

   // },

   [INCREMENT_BY](state, payload) {

     state.count += payload;

   },

 },

 actions: {},

};

export default test;


目录
相关文章
|
6月前
|
JavaScript 前端开发 算法
< 在Vue中,为什么 v-if 和 v-for 不建议一起使用 ? >
在Vue指令中,最经常被用于做逻辑操作的指令,莫过于 `v-if` 和 `v-for`。但是它们之间的能否一起使用呢? 这个问题有时候会被面试官问起,用于测试应试者对这两个指令的了解。 接下来,对 “ `为什么 v-if 和 v-for 不建议一起使用 ?` ” 问题进行讲解!
< 在Vue中,为什么 v-if 和 v-for 不建议一起使用 ? >
|
6月前
|
前端开发 JavaScript API
Vue3 五天速成(下)
Vue3 五天速成(下)
67 1
|
6月前
|
JavaScript 前端开发 API
vue3没有this怎么办?
vue3没有this怎么办?
260 1
|
6月前
|
JavaScript
selectpicker 与vue整合
selectpicker 与vue整合
|
资源调度 JavaScript Linux
VUE使用总结
VUE使用总结
50 0
|
监控 JavaScript 前端开发
vue v-for
vue v-for
62 0
|
JavaScript
vue3常用的东西
vue3常用的东西
36 0
|
存储 缓存 JavaScript
每天vue一下
每天vue一下
97 0
|
缓存 JavaScript
Vue3中name有什么用呢?
<script setup> 只要在script开启setup语法糖模式 单文件组件会自动根据文件名生成对应的 name 选项 例如 Tree.vue 那他的name 就是 Tree 自动生成,这样做有一个弊端如果想修改name需要修改组件名称如果有地方import 该组件需要一并修改。
261 0
Vue3中name有什么用呢?
|
JavaScript
vue使用中的问题总结
vue使用中的问题总结