| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> JavaScript知识库 -> Vue2.x - Vuex -> 正文阅读 |
|
[JavaScript知识库]Vue2.x - Vuex |
目录 ? 基于Vuex工作流程来实现store.state的初始化和持久化 初识Vuex什么是VuexVuex是基于Vue开发的插件,Vuex可以集中保存和管理多个组件共享的数据。 为什么要用Vuex我们已经了解了Vuex的功能是:集中保存和管理多个组件共享的数据。 那么在没有Vuex之前,我们是如何保存和管理多个组件共享数据的呢? Vue2.x - TodoList案例_伏城之外的博客-CSDN博客https://blog.csdn.net/qfc_128220/article/details/125193079?spm=1001.2014.3001.5502在前面做的TodoList案例中,MyHeader、MyContent、MyFooter共享一个数据源,并且这些组件都需要对数据源数据进行操作。
这样一来,App组件就不仅是一个管理员组件,而且还是存放共享数据的仓库,另外还要定义和暴露各种操作共享数据的方法。但是,本质上来说,App组件并不需要使用这些共享数据。 如果项目过大,则App管理的组件就越多,此时就出现了如下问题:
综上所述,App组件作为共享数据的仓库适合小型项目,对于大型项目而言,App组件并不适合保存和管理共享数据。 而Vuex是一个独立于组件之外,可以被所有组件访问到的保存和管理共享数据的仓库。 Vuex的基本使用计数器案例计数器是一个很适合学习Vuex的案例,下面是未使用Vuex的计数器实现: 演示效果: ?下面我们将一边学习Vuex,一边改造计数器案例。 在项目中使用Vuex安装Vuex到项目中(注意:vue2对应vuex3,vue3对应vuex4)
引入vuex插件并注册创建store仓库Vuex插件的目的是创建一个存放和管理共享数据的仓库,而在引入的Vuex对象上有一个Store构造函数,该构造函数可以创建一个store实例,store实例就是存放和管理共享数据的仓库,共享数据存放在创建store实例的Vuex.Store的入参配置对象的state属性中。比如计数器案例中,count值就可以看出一个共享数据。 实现全局组件可访问的store仓库现在我们已经有了store仓库,接下来就是需要让所有组件实例都能访问到这个store仓库。Vuex的实现方案是:在Vue构造函数入参配置对象中扩展一个配置属性store,该配置属性接收一个Vuex.Store实例,一旦该配置被解析,则vm管理的所有组件实例上都会挂载一个$store属性,值就是new Vue时传入的options.store值。
在组件中访问store仓库中的共享数据而在组件实例的$store属性对象上可以访问到 store仓库实例的state属性,这意味着所有组件实例都可以访问到共享数据了 所以计数器案例中,我们可以将共享数据count从App组件中转移到Vuex仓库中定义,然后App组件也支持直接从自身$store.state上获取到count 至此我们已经实现了:将共享数据存放到基于Vuex实现的store仓库中,并且在任意组件中访问到store仓库中的共享数据。 管理共享数据我们知道Vuex的功能是保存和管理多个组件共享的数据。目前保存已经实现了,但是如何管理呢? Vuex工作流程图简析上面图示,就是Vuex的工作流程,首先我们对其中各个要素进行解释: 从图中虚线可以看出,Vuex是由三大核心组成的:Actions、Mutations、State。 Actions、Mutations、State其实都是Vuex.Store构造函数入参配置对象的属性,即它们都是创建store仓库实例的重要配置。 Actions对象中存放了各种action方法,Mutations对象中存放了各种mutation方法。 从图示中可以看出,action方法的职责有三个:
而mutation方法的的职责有三个:
State对象是保存具体共享数据的地方,是实际意义的仓库。另外,我们需要注意的是: state对象中保存的共享数据都是响应式的 它们的响应式逻辑和组件的data属性响应式原理一致,只要数据发生改变,使用数据的组件的模板就会被重新解析渲染。这也就是流程图中,State对象的render线的工作。 通过上面对于Vuex三大核心的工作内容解释,我们可以隐约知道:actions和mutations就是Vuex管理共享数据的方式。 深入理解Vuex工作流程组件实例如何触发dispatch目前我们在组件中已经可以通过$store.state.count访问到共享数据了,那么如何操作共享数据呢?按照流程图看,组件实例需要触发一个dispatch,那么dispatch是啥,又在哪呢? dispatch是一个Vuex.Store.prototype上的方法 所以只要有store实例,就可以访问到disptch方法,而组件实例上的$store属性就是一个store实例。 dispatch方法有两个入参:
action方法定义其实定义在actions中的action方法,类似于事件绑定的回调函数,action方法名就是事件类型。 而组件实例调用dispatch方法就是,触发指定事件类型(type),并传入数据(payload)给事件回调(action方法)。 action方法有两个入参:
action方法是Vuex底层自动调用的,所以context入参和payload入参也是自动传的,其中payload没有什么问题,而context是什么?又来用干什么工作呢? 打印发现context是一个Object对象,它身上有:
context将action方法中可能用到的东西都准备好了,所以称为它为执行上下文。 commit方法定义commit方法可以提交action方法的处理结果到mutation方法中,commit方法入参有两个:
mutation方法定义为了区别action和mutation方法,通常将mutation方法名定义为action方法名的全大写形式,如果action方法名由多个单词组成,则多个单词间由"_"连接。 mutation方法的入参有两个:
mutation方法的作用是将commit提交的处理结果保存到$store.state中。 整体流程过一遍dipatch => action => commit => mutation 当mutation完成state的数据修改后,就会触发响应式行为,引起使用该数据的组件模板的重新渲染。 Vuex工作流程图的拓展知识组件实例直接commit mutation按照Vuex工作流程图,组件实例必须通过dispatch分发数据给action,然后由action提交数据给mutation。 但是我们发现某些action其实只是做了一个数据透传的工作,这意味着action的定义变得冗余。 此时组件实例可以直接commit数据给mutation,即跳过action。 action和mutation的区别action和mutation都是用于管理共享数据的,二者的区别主要在于:
将异步操作放到mutation中,将导致Vue DevTools中记录的mutation动作的state结果和实际结果不一致。? 直接在action中修改state,即不经过mutation,将导致Vue DevTools无法记录state的修改。 从上面两个测试结果来看,在action中修改state、在mutation中进行异步操作对于实际效果无影响,影响的只是Vue DevTools中的mutation记录。 我们可以在Vuex.Store入参配置对象中新增一个属性strict,设为true,表示严格按照 action中不能修改state,mutation中不能进行异步操作的标准进行检查,若不符合标准,则报错。 这里state的修改是在action中进行的,所以报错state不能在mutation以外执行。? ?这里延迟加也是报错 state不能在mutation之外执行,因为state的修改是在setTimeout的异步回调 ()=>{} 中执行的。而不是在INCREMENT_ASYNC中执行的。 在action中继续dispatch其他actionaction方法的入参context上,除了有state、commit外,还有dispatch,这意味着action方法中可以继续dispatch其他action。 实际业务场景中,我们经常会遇到一些复杂功能的业务,此时将整个复杂功能定义在一个action中,不仅降低了代码可读性,可维护性,而且一些可复用的功能也无法复用。所以我们需要将复杂功能分解为多个独立的简单功能,然后将每个简单功能定义为一个action,然后依次在action中dispatch到下一个action,像一个链条一样。 Vuex第四个核心:getters现在我们需要让计数器新增展示一个count * 10的值。 按照一般的思路,我们直接在组件内新增一个computed计算属性即可。 ?那么,如果这个count * 10值如果也是一个共享数据呢?此时需要在store.state中新定义一个属性吗? 答案是不需要,在store中还有一个配置属性getters,它相当于共享数据的计算属性。 组件实例需要通过 $store.getters.count10访问 Vuex辅助函数Vuex中冗余的代码在模板语法中访问store仓库中的共享数据,则必须要带$store.state或$store.getters前缀,形式上很冗余,此时我们可以使用计算属性来取出模板语法中的代码冗余 但是此时冗余度来到了计算属性中。这些代码形式非常公式化。 另外,dispatch和commit方法调用也是非常公式化的。 Vuex提供了四个辅助函数来帮助开发者自动生成上述公式化的代码。 mapStatemapState的作用由于将 store.state.xxx 投射为 组件内 计算属性 yyy?的形式过于固定。如:
其中yyy,xxx为动态的,其他的都是静态的。所以Vuex提供了mapState方法用于生成组件内计算属性函数,我们只需要给mapState提供xxx,yyy信息即可。 mapState的入参对象式写法mapState可以接收一个对象,对象中可以定义多个属性:
mapState函数的返回值是一个对象,该返回值对象包含自动生成的(组件内)计算属性方法。 其实mapState的实现自动生成计算属性逻辑很简单,大致如下:
得到了mapState返回值对象后,我们只需要将mapState返回值对象展开,合并入组件的computed对象即可。 ?mapState的入参数组式写法mapState入参对象式写法适用于:【组件内计算属性名字】和【store.state仓库内共享数据属性名字】不一样时使用。 如:...mapState({myCount: 'count'}) ,计算属性名字为myCount,仓库中共享数据属性名字为count 当【组件内计算属性名字】和【store.state仓库内共享数据属性名字】一样时,这种对象式写法也存在一点冗余:
此时mapState还支持传入一个数组,数组元素就是【组件内计算属性名字】和【store.state仓库内共享数据属性名字】一样时的名字。
mapState的入参数组式写法 算是 对象式写法 的 简写形式。 实现也很简单:
mapGettersmapGetters的作用由于将 store.getters.xxx 投射为 组件内 计算属性 yyy?的形式过于固定。如:
其中yyy,xxx为动态的,其他的都是静态的。所以Vuex提供了mapGetters方法用于生成组件内计算属性函数,我们只需要给mapGetters提供xxx,yyy信息即可。? mapGetters入参对象式写法当【组件内计算属性名字】和【store.getters仓库内共享计算属性的名字】不一样时,使用mapGetters入参对象式写法 mapGetters入参数组式写法当【组件内计算属性名字】和【store.getters仓库内共享计算属性的名字】一样时,使用mapGetters入参数组式写法 mapActionsmapActions的作用由于组件实例调用dispatch方法分发action的形式过于固定,如
?其中 incrementOdd,'incrementOdd'是动态的,{num:this.num}是动态的,其余都是固定的。 当我们需要独立生成函数时,函数内参数只能来源于函数入参,所以上述代码可以改造为:
则此时,只有incrementOdd,'incrementOdd'是动态的,其余都是静态的。 所以Vuex提供了mapActions来生成这段代码
yyy是组件内method方法名子,xxx是store.actions中action方法名字 mapActions入参对象式写法mapActions入参对象式写法适用于:【组件内method方法名】 和 【store.actions中方法名】不一致时
需要注意的是,mapActions返回的对象中的method方法(即自动生成的methods方法),其入参将作为dispatch的第二个参数。 所以我们需要在使用自动生成的mtehods方法时,按需传入数据。如上例中,incrementOdd作为事件回调时,传入了对象{num}作为事件回调入参。 mapActions入参数组式写法mapActions入参数组式写法适用于:【组件内method方法名】 和 【store.actions中方法名】一致时,该种写法算是对象式写法的简写形式。 mapMutationsmapMutations的作用?由于组件实例调用commit方法提交mutation的形式过于固定,如
其中increment和“INCREMENT”是动态的,{num:this.num}也是动态的,其余是静态的。 当我们需要独立生成函数时,函数内参数只能来源于函数入参,所以上述代码可以改造为:
所以只有increment和“INCREMENT”是动态的,其余是静态的。 所以Vuex提供了mapMutations来生成这段代码
yyy是组件内method方法名字,xxx是store.mutations中mutation方法名字 mapMutations的入参对象式写法mapMutations入参对象式写法适用于:【组件内method方法名】 和 【store.mutations中方法名】不一致时? 需要注意的是,利用mapMutations自动生成的组件内method,需要给定入参。 mapMutations的入参数组式写法mapMutations入参数组式写法适用于:【组件内method方法名】 和 【store.mutations中方法名】一致时,此种写法算是对象式写法的简写形式。 需要注意的是,利用mapMutations自动生成的组件内method,需要给定入参。 最终改造后效果如下Vuex模块化什么是Vuex模块化当前我们将Vuex仓库创建在了main.js中,并且所有业务的共享数据及管理共享数据的行为都定义了一起。我们可以将计数器案例和TodoList案例合并在一起。 虽然此时store仓库只有两个共享数据,及它们的管理行为,但是一眼看去也非常乱。 所以,我们需要将store仓库中数据及数据管理行为 按照 业务?进行解耦,定义到不同模块中,然后将不同模块引入到store仓库中,而这就是Vuex的模块化。 如何实现Vuex模块化Vuex模块化项目结构划分按照Vuex官方建议,我们需要在src目录下创建一个store文件夹用于存放Vuex仓库实现及模块化相关代码。 index.js的工作及注意事项首先我们需要将store仓库地生成转移到 src/store/index.js中完成,然后再在 src/main.js 中导入??src/store/index.js 暴露出去的store实例。 需要注意的是,Vuex插件的注册,即Vue.use(Vuex)需要在new Vuex.Store之前执行,否则报错。 所以下面这种书写方式会报错: ?有人可能认为将 import store from xxx 放到 Vue.use(Vuex)之后即可修复错误,其实不然,实际上依旧会报同样的错误, 原因是: ES6的import是在静态编译时执行的,即在main.js尚未执行前,main.js中所有import就已经完成了和export接出接口对接,当main.js开始执行时,优先获取import接入接口变量,然后才运行其他非import语句。所以,import有提升作用。关于ES6模块化可以参考下: 随笔-深入理解ES6模块化(三)_伏城之外的博客-CSDN博客https://blog.csdn.net/qfc_128220/article/details/121950458?spm=1001.2014.3001.5501或者阮一峰大神的ES6模块化教程说明: Module 的语法 - ECMAScript 6入门 (ruanyifeng.com)https://es6.ruanyifeng.com/#docs/module Vuex第五大核心配置modulesVuex模块化旨在将原本多个业务数据源杂糅存在在一起的store,根据不同业务进行数据分离,分成多个不同模块,每个模块都有自己的actions、mutations、state、getters。 而src/store/modules文件夹就是用于收集模块文件的。 模块文件最终需要对外暴露一个包含actions、mutations、state、getters等的配置对象,暴露的配置对象最终被index.js引入,并以模块化的方式传给Vuex.Store入参配置对象的modules配置。 Vuex.Store入参配置对象的modules属性是Vuex的第五大核心,它的作用是:让我们访问store仓库中共享数据时,或者使用store仓库中管理共享数据的action或mutation行为时也要按照模块化方式进行。 举个简单的例子: 没有使用Vuex模块化前,我们在组件模板语法中访问共享数据,一般如下:
而使用了Vuex模块化后,我们需要这样访问:
这里countAbout、todosAbout就是我们创建store实例时,传入Vuex.Store入参配置对象的modules属性对象的属性 同样地,不仅是state数据访问受到了模块化影响,其他actions、mutations、getters中方法的使用也一样收到模块的影响,调用action方法、mutation方法、getters属性时同样要添加countAbout、todosAbout前缀。 对于state属性的访问,只需要将以前的 $store.state.xxx 变为 $store.state.模块名.xxx 即可 对于action、mutation方法和getters方法的调用,则需要改变组件实例调用dispatch和commit方法传入type参数,如以前为 'CHECK_ALL_TODO' ,现在则要变为 '模块名/CHECK_ALL_TODO'? 但是我们目前已经使用mapXxx辅助函数来生成对应state、action、mutation、getters的访问或调用代码,那么mapXxx是否也能适配模块化呢? Vuex辅助函数的模块化适配mapState、mapActions、mapMutations、mapGetters这些方法之前说明时,一般都是一个对象入参,或者一个数组入参。但是实际上,他们还有一个首位可选参数:命名空间。
这个可选的namespace入参就是用于进行模块化适配的,mapXxx的入参namespace值为Vuex.Store入参配置对象的modules属性设置给模块的名称 但是想要使mapXxx的入参namespace值起作用,模块的暴露的配置对象的必须开启命名空间,即需要配置namespaced:true 此时mapXxx辅助函数就可以根据namespace入参值进行模块化适配后的自动生成行为了。? 我们以mapState为例,实现下:
如何实现store.state的初始化以及持久化之前的TodoList案例中,所有的todo都是从浏览器的localStorage中读取的,最终也是保存在浏览器的localStorage中,实现过程是:
使用Vuex后,我们将todos保存在了store.state中,那么如何实现store.state的初始化,以及当store.state数据改变时将其持久化呢? 目前,store.state中的数据只能预置,即提前写死,并且一旦网页刷新,则store.state就会被重置。 基于Vuex工作流程来实现store.state的初始化和持久化我们知道 store.state的数据可以被 组件实例调用dispatch或commit,且最终通过mutation方法修改掉,所以我们只需要找到App组件,在其mounted钩子中 读取localStorage中todos数据并通过dispatch或commit传给store.state? 并且在App组件中深度监听store.state.todos的变动,一旦改变,则取出?store.state.todos持久化到localStorage中 此时就能完成store.state.todos的初始化和持久化。 但是这种方式给App组件增加了负担,不值得推荐。 Vuex第六大核心 plugins区分Vuex插件和Vuex的插件首先我们要区别下:【Vuex插件】 和 【Vuex的插件】 【Vuex插件】是指Vuex本身是一个基于Vue开发的插件,即Vuex是Vue的插件。 【Vuex的插件】是指Vuex插件的插件。 我们一般将【Vuex的插件】配置为 创建store实例的Vuex.Store入参配置对象的plugins数组的元素。 Vuex的插件函数的入参以及执行时机?【Vuex的插件】是一个函数,该函数会接收到一个所在的store实例作为入参,且该函数的调用时机是store实例初始化完成后。 所以,在?【Vuex的插件】函数中我们可以完成store.state的初始化。 ?但是此时报错提示,我们不能在插件函数中修改store.state,那不完犊子了吗.... Vuex的插件函数中初始化state的两种方式此时我们需要借助一个store实例方法 这个方法可以在mutation方法外替换整个store.state,而不会触发state只能在mutation中修改的报错。 这里先是利用JSON.parse(JSON.stringify)来进行了store.state对象的深度拷贝,然后基于拷贝对象初始化了todos,最后将拷贝对象替换掉原来的store.state。 又或者使用下面这种方式:即严格按照只能在mutation中修改state的标准来搞。 store.subscribe方法store实例还可以调用一个实例方法subscribe,该方法接收一个handler函数作为参数。 而handler函数:会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态state作为参数 由于handler函数的调用时机是在每个mutation完成后,即store.state改变后,相当于handler函数深度监听了store.state的变化,一旦store.state发生变化,则handler被调用。 所有我们可以在handler函数中完成store.state的持久化动作。 但是上面逻辑存在一个问题,即handler是监听整个state的深度变化,所以不管是state.todosAbout.todos的变动,还是state.countAbout.count的变动,都会触发handler的执行,这将会造成严重的性能问题。 此时,我们需要借助handler的mutation入参,该入参是一个对象,具有如下属性:
我们可以根据mutation.type的前缀来判断是哪个模块的state数据发生了改变,比如上面type值为“todosAbout/ADD_TODO”,则说明是todosAbout模块的state发生改变。 此时handler引发的性能问题将得到缓解。但是仍然需要注意防抖和节流。? 案例代码分享 |
|
JavaScript知识库 最新文章 |
ES6的相关知识点 |
react 函数式组件 & react其他一些总结 |
Vue基础超详细 |
前端JS也可以连点成线(Vue中运用 AntVG6) |
Vue事件处理的基本使用 |
Vue后台项目的记录 (一) |
前后端分离vue跨域,devServer配置proxy代理 |
TypeScript |
初识vuex |
vue项目安装包指令收集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 16:49:04- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |