本文收录于githubgithub.com/Michael-lzg...同文掘金:https://juejin.im/post/6866964944634511368数据状态管理层先说说什么叫“前端态”。所有的程序都有“状态”,在代码中用各种类型的变量来表示,在程序运行过程中发生变化的过程,我们写的程序控制着这些“状态”如何变化。为什么需要数据状态管理?数据状态管理是近年来随着React/Vue等现代前端框架流行起来的,主要应用于单页应用SPA(SinglePageApplication)。在前端“刀耕火种”的时代,没有这个概念。前端技术发展如火如荼,前端工作也越来越复杂。这个阶段,前端不再只是传统意义上的“切图”,更多的是负责页面数据逻辑处理。许多独创的技术系统和解决方案已经落地。这些日益复杂的需求得不到很好的支持。而且现在Vue/React等前端框架都采用了MVVM设计模式,依赖数据驱动的视图更新。比如Vue就使用了VirtualDOM的思想。将DOM放在内存中,当数据发生变化时,生成一个新的VirtualDOM,然后通过diff算法与之前的VirtualDOM进行比较,在浏览器中渲染变化的内容,大大减少了对DOM操作的改进前端性能。其次,数据管理逻辑和页面渲染逻辑的分离使得代码更易于维护。操作数据的地方不关心页面如何显示,页面显示的地方也不关心数据从哪里来。数据状态管理的方法localStorage和sessionStorage:适合存储少量简单数据prop和$emit:适合父子组件传值provide和inject:允许一个祖先组件注入一个依赖到它的所有后代vuex:全局数据状态管理,适用于数据复杂的大型应用VuexVuex是专门为Vue.js应用开发的状态管理模式。它使用集中存储来管理应用程序所有组件的状态,并使用相应的规则来确保状态以可预测的方式变化。Vuex解决什么问题?当多个组件依赖同一个状态时,给多层嵌套的组件传递参数会非常麻烦,并且与兄弟组件之间的状态传递无关。来自不同组件的行为需要改变相同的状态。以往都是通过父子组件直接引用或者使用事件来改变和同步多份状态。上面的这些模式非常脆弱,通常会导致代码无法维护。五个核心属性state:存储状态(变量),相当于vue的datagetters:数据获取前的重新编译,可以理解为state的计算属性。我们在组件中使用$sotre.getters.fun()突变:修改状态,并且是同步的。在组件中使用$store.commit('',params)。相当于vue的方法。动作:异步操作。组件中使用的是$store.dispath('')modules:store的子模块,用于开发大型项目,便于状态管理。statestate是存储状态,是一个对象。conststore=newVuex.Store({state:{count:10,price:10,},})当组件需要获取多个状态时,将这些状态声明为计算属性有些重复和多余。为了解决这个问题,我们可以使用mapState辅助函数来帮助我们生成计算属性。//单独构建版本中的辅助函数是Vuex.mapStateimport{mapState}from'vuex'exportdefault{computed:mapState({count:(state)=>state.count,price:(state)=>state.price,}),}//当映射的计算属性的名称与状态的子节点名称相同时,我们也可以传递一个字符串数组给mapState。computed:mapState(['count','price'])getter有时候我们需要从store中的state中导出一些state,比如对列表进行过滤和计数,那么我们就使用getter属性。exportdefaultnewVuex.Store({state:{list:[1,2,3,4]},getters:{//这里主要是状态处理,相当于把状态处理方法抽取出来管理filterArr的公共部分(state){//广义的getterreturnstate.list.filter((item,index,arr)=>{returnitem%2===0;})},getLength(state,getter){//传递getter在方法中,调用modifyArr来计算长度returngetter.filterArr.length;}})然后使用在组件中计算的计算属性来访问这些派生的转换。computed:{list(){returnthis.$store.getters.filterArr},}mapGetters辅助函数只是将商店中的getter映射到本地计算属性。当我们想在组件中引入多个getter时,可以使用mapGetters:import{mapGetters}from'vuex'exportdefault{computed:{...mapGetters(['filterArr','getLength']),},}mutation更改Vuex商店中的状态的唯一方法是提交突变。Vuex中的突变与事件非常相似:每个突变都有一个字符串事件类型(type)和一个回调函数(handler)。它将接受状态作为第一个参数,将提交的有效负载(Payload)作为第二个参数。conststore=newVuex.Store({state:{count:1,},mutations:{increment(state,n){//改变状态state.count+=n},},})store.commit('increment',10)大多数情况下,payload应该是一个对象,可以包含多个字段,记录的mutation会更易读:mutations:{increment(state,payload){state.count+=payload.count}}store.commit('increment',{count:10})提交突变的另一种方法是直接使用包含type属性的对象:store.commit({type:'increment',count:10,})在组件中SubmitMutation可以在组件中使用this.$store.commit('xxx')提交mutation,或者使用mapMutations辅助函数将组件中的方法映射到store.commit调用(需要在根注入store节点)。import{mapMutations}from'vuex'exportdefault{//...方法:{...mapMutations(['increment',//将`this.increment()`映射到`this.$store.commit('increment')`]),},}ActionAction类似于mutation,不同的是action提交mutation,而不是直接改变state。突变可以直接改变状态。action可以包含任何异步操作。突变只能是同步操作。提交方式不同,action是用this.$store.dispatch('ACTION_NAME',data)提交的。突变与this.$store.commit('SET_NUMBER',10)一起提交。接收参数不同。mutation的第一个参数是state,而action的第一个参数是context。conststore=newVuex.Store({state:{count:0,},mutations:{increment(state){state.count++},},actions:{increment(context){context.commit('increment')},},})注意:在vuexmutations中不能进行异步操作。更新vuex中所有状态的唯一方法是提交突变。异步操作需要通过action提交mutations(dispatch)。这使我们能够轻松地跟踪每个状态变化,使我们能够实现一些工具来帮助我们更好地使用vuexModuleVuex使我们能够将商店拆分为模块。每个模块都有自己的状态、突变、动作、getter,甚至嵌套子模块——从上到下以相同的方式拆分:constmoduleA={state:{...},getters:{...},突变:{...},动作:{...},}constmoduleB={状态:{...},吸气剂:{...},突变:{...},动作:{...}}conststore=newVuex.Store({modules:{a:moduleA,b:moduleB}})store.state.a//->moduleA的状态store.state.b//->moduleB的状态实现了avuex的简单版本。我们来看看如何使用vueximportVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)exportdefaultnewVuex.Store({state:{},mutations:{},actions:{},})上面可以看到,vuex是通过Vue.use()注入到Vue中的。使用Vue.use()的插件,如果插件是一个对象,它必须提供install方法。如果插件是一个函数,它将被用作安装方法。在调用install方法时,会将Vue作为参数传入。实现Store类首先实现一个Store类,代码如下options.actionsoptions.getters&&this.handleGetters(options.getters)}commit=(type,arg)=>{this.mutations[type](this.state,arg)}dispatch(type,arg){this.actions[type]({commit:this.commit,state:this.state,},arg)}//getters是参数,this.getters是实例化的handleGetters(getters){this.getters={}Object.keys(getters).forEach((key)=>{Object.defineProperty(this.getters,key,{get:()=>{returngetters[key](this.state)},})})}}使用Vue实现install方法.带有use()的插件必须提供安装方法。并将Vue作为参数传入。让Vuefunctioninstall(_Vue){Vue=_VueVue.mixin({beforeCreate(){if(this.$options.store){Vue.prototype.$store=this.$options.store}},})}vuexfinal一个对象被导出。该对象包括一个install方法和一个Store类,注意对应的使用方法。exportdefault{Store,install}使用简单版本的vuex1,新建store.jsimportVuefrom'vue'importVuexfrom'./store'Vue.use(Vuex)conststate={count:0,}constgetters={getCount(state){returnstate.count},}const突变={addCount(state,payload){state.count+=payload},}constactions={asyncAdd(context,payload){context.commit('addCount',payload)},}conststore=newVuex.Store({state,mutations,getters,actions,})exportdefaultstore2,main.jsimportimportVuefrom'vue'从'./App.vue导入App'importrouterfrom'./router'importstorefrom'./store/index'Vue.config.productionTip=falsenewVue({router,store,render:(h)=>h(App),}).$mount('#app')3.页面使用数据状态管理
