失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Vue全家桶之VueX(六)

Vue全家桶之VueX(六)

时间:2020-06-16 07:13:22

相关推荐

Vue全家桶之VueX(六)

🔥Vue🔥

配套视频讲解: CoderWhy老师的Vuejs讲解

1、Vuex

1.1、什么是Vuex

官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

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

状态管理到底是什么?

状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?

等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?

当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。

1.2、单界面的状态管理

我们知道,要在单个组件中进行状态管理是一件非常简单的事情。

这图片中的三种东西如下:

State:状态View:视图层,可以针对 State 的变化,显示不同的信息Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的变化

<template><div><div>当前计数:{{counter}}</div><button @click="counter+=1">+1</button> <button @click="counter-=1">-1</button></div></template><script>export default {name: "HelloWorld",data() {return {counter: 0}}}</script><style scoped></style>

在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。

counter需要某种方式被记录下来,也就是我们的State。

counter目前的值需要被显示在界面中,也就是我们的View部分。

界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions

这不就是上面的流程图了吗?

ok,先安装 vuex

npm install vuex --save

1.3、多界面状态管理

Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?

多个视图都依赖同一个状态(一个状态改了,多个界面需要进行更新)不同界面的 Actions 都想修改同一个状态(Home.vue 需要修改,Profile.vue 也需要修改这个状态)

也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个视图,但是也有一些状态(状态a/状态b/状态c)属于多个视图共同想要维护的

状态1/状态2/状态3 你放在自己的房间中,你自己管理自己用,没问题。但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!没错,Vuex就是为我们提供这个大管家的工具。

全局单例模式(大管家)

我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。

之后,你们每个视图,按照我规定好的规定,进行访问和修改等操作。

这就是Vuex背后的基本思想。

我们创建一个文件夹 src/store,并且在其中创建一个 index.js 文件,代码如下:

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {count: 0},mutations: {increment(state){state.count++},decrement(state){state.count--}},actions: {},getters: {},modules: {}})// 导出store对象export default store

其次,我们让所有的Vue组件都可以使用这个store对象

来到 src/main.js文件,导入store对象,并且挂载到new Vue中

import Vue from 'vue'import App from './App'// 1.导入store对象import store from './store'new Vue({el: '#app',// 2.挂载 storestore,render: h => h(App)})

这样,在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了

<template><div id="app"><p><button @click="increment">+1</button><button @click="decrement">-1<</button></div></template><script>export default {name: 'App',components: {},computed: {count: function() {return this.$store.state.count}},methods: {increment: function() {this.$mit('increment')},decrement: function() {this.$mit('decrement')}}}</script>

使用步骤小结:

提取出一个公共的 store 对象,用于保存在多个组件中共享的状态将 store 对象放置在 new Vue 对象中,这样可以保证在所有的组件中都可以使用到在其他组件中使用 store 对象中保存的状态即可 通过this.$store.state属性的方式来访问状态通过this.$mit('mutation中方法')来修改状态 我们通过提交 mutation 的方式,而非直接改变 store.state.count这是因为 Vuex 可以更明确的追踪状态的变化,所以不要直接改变 store.state.count 的值

1.4、安装vue-devtools

在 Chrome 网上应用商店 搜索 devtools选择 vue.js devtools ,添加即可

1.5、Vuex核心概念

Vuex有几个比较核心的概念:

StateGettersMutationActionModule

1.5.1、State 单-状态树

Vuex提出使用单一状态树, 什么是单一状态树呢?

英文名称是Single Source of Truth,也可以翻译成单一数据源。

我用一个生活中的例子做一个简单的类比,我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。

这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当然国家目前已经在完善我们的这个系统了)。

这个和我们在应用开发中比较类似:如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难,所以Vuex也使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。

1.5.2、Getters基本使用

有时候,我们需要从 store 中获取一些 state 变异后的状态,比如下面的 Store

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {students: [{id: 100,name: 'why',age: 18},{id: 111,name: 'kobe',age: 21},{id: 112,name: 'lucy',age: 25},{id: 113,name: 'lilei',age: 2},]},mutations: {},actions: {},getters: {// 获取年龄大于20的学生对象more20stu(state){return state.students.filter(s => s.age >20)} },modules: {}})// 导出store对象export default store

这样我们在其他 .vue 组件中也可以拿到年龄大于20的学生对象

<template><div><h2>{{$store.getters.more20stu}}</h2></div></template><script>// @ is an alias to /srcimport HelloWorld from '@/components/HelloWorld.vue'export default {name: 'Home',components: {HelloWorld}}</script>

①、Getters作为参数和传递参数

如果我们已经有了一个获取所有年龄大于20岁学生的列表 getters,那么代码可以这样来写

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {students: [{id: 100,name: 'why',age: 18},{id: 111,name: 'kobe',age: 21},{id: 112,name: 'lucy',age: 25},{id: 113,name: 'lilei',age: 2},]},mutations: {},actions: {},getters: {// 获取年龄大于20的学生对象more20stu(state){return state.students.filter(s => s.age >20)},// 获取年龄大于20的学生个数more20stuLength(state,getters) {return getters.more20stu.length}},modules: {}})// 导出store对象export default store

getters 默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {students: [{id: 100,name: 'why',age: 18},{id: 111,name: 'kobe',age: 21},{id: 112,name: 'lucy',age: 25},{id: 113,name: 'lilei',age: 2},]},mutations: {},actions: {},getters: {// 获取年龄大于20的学生对象more20stu(state){return state.students.filter(s => s.age >20)},// 获取年龄大于20的学生个数more20stuLength(state,getters) {return getters.more20stu.length},// 让用户自己决定获取年龄大于多少moreAgeStu(state) {return function(age){return state.students.filter(s => s.age > age)}}},modules: {}})// 导出store对象export default store

这样我们在其他 .vue 组件中就可以传入年龄数值筛选了

<template><div><h2>{{$store.getters.more20stu}}</h2><h2>{{$store.getters.more20stuLength}}</h2><h2>{{$store.getters.moreAgeStu(8)}}</h2></div></template><script>// @ is an alias to /srcimport HelloWorld from '@/components/HelloWorld.vue'export default {name: 'Home',components: {HelloWorld}}</script>

1.5.4、Mutation状态更新

Vuex的store状态的更新唯一方式:提交Mutation

Mutation主要包括两部分:

字符串的事件类型(type)一个回调函数(handler)该回调函数的第一个参数就是 state mutation 的定义方式

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {count: 0},mutations: {increment(state){state.count++},decrement(state){state.count--}},actions: {},getters: {},modules: {}})// 导出store对象export default store

通过 mutation 更新

<template><div id="app"><button @click="increment">+1</button><button @click="decrement">-1<</button></div></template><script>export default {name: 'App',components: {},computed: {count: function() {return this.$store.state.count}},methods: {increment: function() {this.$mit('increment')},decrement: function() {this.$mit('decrement')}}}</script>

①Mutation传递参数

在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数,参数被称为 mutation 的载荷(Payload)

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {count: 0},mutations: {increment(state){state.count++},decrement(state){state.count--},incrementCount(state,count){state.counter += count}},actions: {},getters: {},modules: {}})// 导出store对象export default store

我们在其他 .vue 组件中来修改状态

<template><div id="app"><button @click="increment">+1</button><button @click="decrement">-1<</button><button @click="addCount(5)">+5</button><button @click="addCount(10)">+5</button></div></template><script>export default {name: 'App',components: {},methods: {increment: function() {this.$mit('increment')},decrement: function() {this.$mit('decrement')},addCount(count){this.$mit('incrementCount',count)}}}</script>

但是如果参数不是一个呢?

比如我们有很多参数需要传递,这个时候,我们通常会以对象的形式传递,也就是 payload 是一个对象。

这个时候可以再从对象中取出相关的信息。

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {students: [{id: 100,name: 'why',age: 18},{id: 111,name: 'kobe',age: 21},{id: 112,name: 'lucy',age: 25},{id: 113,name: 'lilei',age: 2},]},mutations: {},actions: {},getters: {},modules: {addStudent(state,stu){state.students.push(stu)}}})// 导出store对象export default store

我们在其他 .vue 组件中来修改状态

<template><div id="app"><button @click="increment">+1</button><button @click="decrement">-1<</button><button @click="addCount(5)">+5</button><button @click="addCount(10)">+5</button><button @click="addStudent">添加学生</button></div></template><script>export default {name: 'App',components: {},methods: {increment: function() {this.$mit('increment')},decrement: function() {this.$mit('decrement')},addCount(count){this.$mit('incrementCount',count)},addStudent(){const stu = {id: 114, name: 'alan',age: 35}this.$mit('addStudent',stu)}}}</script>

②Mutation提交风格

上面的通过commit进行提交是一种普通的方式

Vue 还提供了另外一种风格,它是一个包含 type 属性的对象

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {students: [{id: 100,name: 'why',age: 18},{id: 111,name: 'kobe',age: 21},{id: 112,name: 'lucy',age: 25},{id: 113,name: 'lilei',age: 2},]},mutations: {},actions: {},getters: {},modules: {incrementCount(state,payload){state.counter += payload.count}}})// 导出store对象export default store

<template><div id="app"><button @click="increment">+1</button><button @click="decrement">-1<</button><button @click="addCount(5)">+5</button><button @click="addCount(10)">+5</button></div></template><script>export default {name: 'App',components: {},methods: {increment: function() {this.$mit('increment')},decrement: function() {this.$mit('decrement')},addCount(count){// 1.普通的提交封装// this.$mit('incrementCount',count)// 2.特殊的提交封装this.&mit({type: 'incrementCount',count})},}}</script>

③Mutation响应规则

Vuex 的 store 中的 state 是响应式的,当 state 中的数据发生改变时,Vue组件会自动更新

这就要求我们必须遵守一些Vuex对应的规则:

提前在 store 中初始化好所需的属性当给 state 中的对象添加新属性时,使用下面的方式 方式一:使用Vue.set(obj,'newProp',123)方式二:用新对象给就旧对象重新赋值

例如:

我们在 index.js 中增加 info 状态

const store = new Vuex.Store({state: {info: {name: 'why', age: 18}},mutations: {// 方式一:Vue.set()Vue.set(state.info,'height',payload.height)// 方式二:给 info 赋值一个新的对象state.info = {...state.info,'height':payload.height}}})

我们在其他 .vue 组件中修改状态

<template><div id="app"><p>我的个人信息: {{info}}</p><button @click="updateInfo">更新信息</button></div></template><script>export default {name: 'App',components: {},computed: {info(){return this.$store.state.info} },methods: {updateInfo(){this.$mit('updateInfo',{height: 1.88})}}}</script>

④Mutation常量类型-概念

我们来考虑下面的问题:

在 mutation 中,我们定义了很多事件类型(也就是其中的方法名称)当我们的项目增大时,Vuex 管理的状态越来越多,需要更新状态的情况越来越多, 那么意味着 Mutation 中的方法越来越多方法过多,使用者需要花费大量的精力的经历去记住这些方法,甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.

如何避免上述问题呢?

在各种Flux实现中, 一种很常见的方案就是使用常量替代 Mutation事件的类型我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.

具体怎么做呢?

我们可以创建一个文件:mutation-types.js, 并且在其中定义我们的常量定义常量时, 我们可以使用ES中的风格, 使用一个常量来作为函数的名称.

我们在src/store 下新建 mutation-types.js

exports const UPDATA_INFO = 'UPDATE_INFO'

这样的话我们在 index.js 中可以导入

import Vuex from 'vuex'import Vue from 'vue'import * as types from './mutation-types'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {info: {name: 'why',age: 18}},mutations: {[tyoes.UPDATE_INFO](state,payload){state.info = {...state.info,'height':payload.height}}},actions: {},getters: {},modules: {}})// 导出store对象export default store

我们在其他 .vue 组件中也可以使用

<template><div id="app"><p>我的个人信息: {{info}}</p><button @click="updateInfo">更新信息</button></div></template><script>import {UPDATE_INFO} from "./store/mutation-types"export default {name: 'App',components: {},computed: {info(){return this.$store.state.info} },methods: {updateInfo(){this.$mit('UPDATE_INFO',{height: 1.88})}}}</script>

⑤Mutation同步函数

通常情况下,Vuex 要求我们 Mutation 中的方法必须是同步方法

主要的原因是当我们使用 devtools 时,devtools 可以帮助我们捕捉 mutation 的快照但是如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候会被完成。So,通常情况下,不要在 mutation 中进行异步的操作

1.5.5、Action的基本定义

我们强调,不要在 Mutation 中进行异步操作,但是某些情况,我们确实希望在 Vuex 中进行一些异步操作,比如网络请求,必然是异步的,Action 类似于 Mutation,但是是用来代替 Mutation 进行异步操作的。

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {count: 0 },mutations: {increment(state){state.count++}},actions: {increment(context){mit('increment')}},getters: {},modules: {}})// 导出store对象export default store

context 是什么?

context 是 store 对象具有相同方法和属性的对象也就是说,我们可以通过 context 去进行 commit 相关的操作,也可以获取 context.state 等。

①Action的分发

在Vue组件中,如果我们调用 action 中的方法,那么就需要使用 dispatch

methods: {increment(){this.$store.dispatch('increment')}}

同样的,也是支持传递 payload

methods: {increment(){this.$store.dispatch('increment', {cCount: 5})}}

import Vuex from 'vuex'import Vue from 'vue'// 1.安装插件Vue.use(Vuex)// 2.创建对象const store = new Vuex.Store({state: {count: 0 },mutations: {increment(state,payload){state.count += ount}},actions: {increment(context,payload){setTimeout(() => {mit('increment',payload)},1000)}},getters: {},modules: {}})// 导出store对象export default store

②Action返回的Promise

在 Action 中,我们可以将异步操作放在一个 Promise 中,并且在成功或者失败之后,调用对应的 resolve 或 reject

actions: {increment(context){return new Promise((resolve) => {setTimeout(() => {mit('increment')resolve()},1000)})}},

在其他 Vue 组件中

methods: {increment() {this.$store.dispatch('increment').then(res => {console.log('完成了更新操作');})}}

1.6、认识 Module

Module 是模块的意思,为什么在 Vuex 中我们要使用模块呢?

Vue 使用单一状态树,那么也意味着很多状态都会交给 Vuex 来管理当应用变得非常复杂时, store 对象就有可能变得相当臃肿为了解决这个问题,Vuex 允许我们将 store 分割成某块,而每个模块拥有自己的 state、mutations、action、getters等

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 的状态

如果觉得《Vue全家桶之VueX(六)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。