当前位置: 首页 > Web前端 > vue.js

原生javascript实现了类似redux的状态管理器

时间:2023-04-01 11:33:59 vue.js

react,vue将一个应用分成了不同的组件,一个组件的状态可能会影响另一个组件。随着项目越来越复杂,组件之间的通信令人头疼……我:qiottomatte!项目越来越复杂,状态管理难?导致模型变化的因素可能有很多:服务器响应、缓存数据、本地数据、ui状态、用户事件……我们如何处理这些状态?多个组件依赖同一个状态,组件如何更新和同步状态?那就是使用redux。说起redux,厉害了,她就是来解决这个问题的,所以不懂redux的同学要好好研究一下。为了紧跟前端新技术的潮流,提高个人的竞争力,我们必须明白一些道理,成为一个知道为什么的聪明码农。我心目中的状态管理其实就是一个身处前端社会底层的砖头小子。我心里没有任何状态。我觉得redux很好。他妈的,太棒了;其实我心里真的觉得很神奇。如果我能自己实现一个,我是不是也一样厉害?是的!现在,假设我实现了这个statemanager,我应该怎么用,用什么姿势爽,值得思考...首先创建一个moduleconstapp={namespace:'app',state:{num:0},actions:{change(state,value){this.setState({num:value})}}}然后创建storeconststore=createStore({modules:[app]})这个store是我们全局的state,你可以把她比作redux的store,但是请不要说是copy。那么store也要暴露获取和修改数据的方法。store应该包含的apiconstactions=store.mapActions({change:'app/change'})conststates=store.mapStates({num:'app/num'})至此,一切都是那么自然,用法简单明了,一度怀疑自己是天才。constapp={namespace:'app',state:{num:0},actions:{change(state,value){this.setState({num:value})}}}const{mapActions,mapStates}=createStore({modules:[app]})constactions=store.mapActions({change:'app/change'})conststates=store.mapStates({num:'app/num'})动作。改变(1)状态。num//1突然之间,创建逻辑好像没有了方向。面对自己痴迷的假代码示例,无法使用cc和cv的挫败感油然而生。这种感觉就像失恋一样。没办法,生活还得继续。依稀记得老师们说过的话:面对苦难,化繁为简,一一突破……于是你列举了几个实现步骤:store是一个需要安装app模块的类。注意modules可能有多个modules按命名空间划分实现mapActions实现mapStates喝一杯水微信问候久违的女神,过上精致的生活store类Store{constructor(options)的实现{this._state={}this._modules={}this.setModules(options.modules)}setModules(modules){[].concat(modules).forEach(m=>{letns=m.namespacelet_m=newModule(m,this)this._module[ns]=_mthis._state[ns]=_m.state})}}functioncreateStore(options){returnnewStore(options)}你发现Store的逻辑可以通过简单的组合来简化,所以你打算实现一个Module类,方便扩展Module类实现classModule{constructor(m,_store){this._store=_storethis.state=m.statethis.actions=m.actionsthis.namespace=m.namespace}setState(state){Object.assign(this.state,state)}}现在,store的结构就可以整理出来了,but方法(actions)和状态(state)还没有暴露,所以现在我们需要实现mapActions,mapState。dispatch的必要性想想之前调用mapActions:constactions=store.mapActions({change:'app/change'})难道只是为了找到Module对应的action并返回?这个太low了,而且redux的中间件是通过修改dispath来增强扩展的,太强大了,可以借鉴一下。所以mapActions应该是dispatch的封装实现;dispatchclassStore的实现{//...dispatch=(path,...args)=>{let{ns,key}=normalizePath(path);ns=this._module[ns]if(!ns){return}letaction=ns.actions[key]returnaction.call(ns,ns.state,...args)}}functionnormalizePath(path){常量[ns,key]=path.split('/')return{ns,key}}mapActions和mapStates的实现mapActions返回的所有方法应该是dispatch的封装,这样所有的方法都使用dispatch,所以我们将稍后添加中间件非常方便。class{//...mapActions=map=>{letres={}forEachValue(map,(path,fkey)=>{letfn=(...args)=>{this.dispatch(path,...args)}res[fkey]=fn})returnres}mapStates=map=>{letres={}forEachValue(map,(path,fkey)=>{const{ns,key}=normalizePath(path);constm=this._module[ns]res[fkey]=m.state[key]})returnres}}functionforEachValue(obj,fn){Object.keys(obj).forEach(key=>fn(obj[key],key))}顺利实现了mapStates,你看着列表的最后一项坏坏地笑了笑,心里想:科技改变世界,改变自己,觉得自己是人生赢家。刚才的创举给了你十足的勇气,你兴高采烈地打开微信,熟练地打开了与女神的对话框,小心翼翼地发了一句:在吗?看完女神可能又忙了,你打算测试一下自己的代码:constactions=mapActions({change:'app/change'})conststates=mapStates({num:'app/num'})change(1)console.log(states.num)//0console.log(store._store._state.app.state.num)//1constadd=val=>actions.change(states.num+1)constreudce=val=>actions.change(states.mum-1)发现代码有问题:states不能响应式修改,mapActions太受限;上面的动作应该支持以下优化constactions=store.mapActions({change:'app/change',add(dispatch){dispatch('app/change',states.val+1)},reduce(dispatch){dispatch('app/change',states.val-1)}})将mapStates返回的值代理到类Store上的模块状态{//...mapStates=map=>{letres={}forEachValue(map,(path,fkey)=>{const{ns,key}=normalizePath(path);constm=this._module[ns]if(!m){return}proxyGetter(res,fkey,m.state,key)})returnres}}functionproxyGetter(target,key,source,sourceKey){sharedPropertyDefinition.get=function(){returnsource[sourceKey]}Object.defineProperty(target,key,sharedPropertyDefinition)}修改mapActions以支持扩展类Store{//...mapActions=map=>{letres={}forEachValue(map,(path,fkey)=>{letfnif(typeofpath==='function'){fn=(...args)=>{path(this.dispatch,...args)}}else{fn=(...args)=>{this.dispatch(path,...args)}}res[fkey]=fn})returnres}}这里,前端状态managementcontroller的功能已经基本实现,可以去这里查看她的简单用法和源码;查看undo和redo的简单例子,她可以满足自己的需求,但是她还是有局限和不足:dispath对setState是不可预测的,例如:constapp={namespace:'app',state:{num:0},actions:{asyncgetNumer(){//waiting...this.setState({num:'xxx'})}}}可以查看到如果action是异步方法,那我们不知道什么时候setState会叫做代码中没有错误处理的逻辑模块不支持多级模块,比如vuex;但是你可以通过命名空间自己定义它:constapp={namespace:'app'}constapp={namespace:'app:user'}constapp={namespace:'app:system'}这样也可以使数据更扁平,不是吗?在模块的action中调用其他actionconstother={namespace:'other',actions:{foo(){this.dispatch('app/getNumber')}}}