微信小程序全局状态的深入讲解
前言
在微信小程序中,可以利用App.js的globalData作为中间桥梁,在Page,Component之间,包括页面与页面,页面与组件,组件与组件之间传递需要传递的信息。但是,我们不能及时的知道globalData下的变化,在新建小程序的官方的默认事例中,获取UserInfo这一网络操作有延迟的,为此写了很多不必要的代码。就连官方案例都存在这一情况,相信在开发中你也会遇到类似的情况。在本文中将介绍如何解决这一类问题。
需求分析
相信以下情况是我们在没有全局状态管理下常有的操作:
- 在Page,Component的OnLoad,Attached两个生命周期钩子函数中,进行一些从App的globalData赋值一些已经存在的属性到页面或组件中的data中。
- 在最开始就存在一些异步的网络请求,获取的数据用于全局,刚开始可能这个globalData还没有相关属性,直到请求成功,才把相关属性添加到globalData,而这时Page的从globalData的赋值操作可能已经完成了,只不过是undefined,为此需要进一步的判断再进行赋值到Page,Component中。如果只是一两个这个还说很简单的,但是多个页面,或者多个变量都需要赋值的话,我想你会拒绝并寻找偷懒的办法。
- 一些在页面和组件从globalData赋值的变量不仅是用于判断、展示,我们可能还需要依据用户交互而改变变量的值,那么在其他页面,其他组件中同样的变量也需要统一改变。
以上情况我们可提出以下几点需求:
- 在页面,组件初始加载时,尽早的从globalData获取并赋值到页面,组件所需要的一些属性
- 及时的获取一些globalData某一属性的变化,并进行一些后续相关操作
- 在改变Page,Component的值的同时,其他页面,组件也进行一样的改变
下面是需求的原始代码
//app.js App({ globalData:{ userInfo:null }, onLaunch(){ wx.getSetting({ success:res=>{ if(res.authSetting['scope.userInfo']){ wx.getUserInfo({ success:res=>{ this.globalData.userInfo=res.userInfo //需求2 if(this.userInfoReadyCallback){ //存在此回调函数,意味着page执行了onLoad //且没有获取到userInfo并赋值到page的data中 //执行此回调函数,赋值到相应的页面中 this.userInfoReadyCallback(res) } } }) } } }) } })
//Pages/index/index.js constapp=getApp() Page({ //... onLoad(options){ //需求1 constuserInfo=app.globalData.userInfo userInfo&&this.setData({useInfo}) //需求2 //如果没有获取到app.globalData.userInfo //意味还未执行wx.getUserInfo的回调函数 //给app添加响应的一个回调函数,绑定此时的this到回调函数 userInfo||app.userInfoReadyCallback=res=>{ this.setData({ userInfo:res.userInfo }) deleteapp.userInfoReadyCallback } } })
这是官方小程序案例的代码,我只做了一点修改,这里只是展示了需求2,globalData属性从无到有时执行页面设置的回调函数,并没有实现每一次都会执行回调函数,需求3的代码比较复杂,不在此展示。
我们可以思考,以上几点需求需要实现的,一定要有的代码有哪些。可以发现,需求1和需求3主要就是页面,组件初始化,和globalData属性被改变时都需要使用this.setData方法,只不过每次this的指向的实例不同。而需求2则是应该存在一个回调函数,且回调函数的this也应该指向相应的实例,在globalData属性被改变时执行这些回调函数。
从时间点来看,我们有两个,一个是页面,组件初始化,一个是globalData属性改变时,那么第一个时间点,我们可以考虑到小程序的生命周期的钩子函数,onLoad和attached,在这两个时间点执行this.setData的操作。而globalData属性的改变都是我们主动或者用户事件而产生的,就是可以看作这一操作是一个对globalData某个属性的事件,而这个事件发生后再去执行一些写好的回调函数。
从操作对象来看,基本都是页面和组件的实例this,以及app.globalData。
需求理论性总结
综上,我们可以在初始化时,进行自动的this.setData(不用自己手动),和保存this(用于事件执行时指向相应的实例),存储相应的回调函数为事件(事件就是未执行的函数),在需要时主动触发这个事件即可。那么可以看到,整个流程下来,我们需要一个横跨app,page,component之间的一个变量,用于劫持初始化的钩子函数,进行自动化赋值,存储相应的事件,暴露一个事件触发的接口。
纸上得来终觉浅,绝知此事要躬行
看到这里,相信你已经有一定的了解全局状态管理,那么到底如何实现呢?在这里,我要强调,如果你阅读此文后对此有一定的了解了,我说的思路,那么你一定要自己尝试实现出代码,不管是否好坏,总是比没有实现的好,在自己实现中也许有更多的收获。下面以上上面案例展示一下简单的实现代码,给没看太明白的一个思路。在下次我会写一遍相关代码实现的讲解,应该会有。
//app.js classStore{ constructor(app){ this['event']={} this.app=app } autoSet(globalData,instance){ constinstanceData={} for(letpropofglobalData){ instanceData[prop]=this.app.globalData[prop] constcallBack=(newValue)=>{ instance.setData({[prop]:newValue}) instance.watch[prop]&&instance.watch[prop].call(instance,newValue) } this.addEvent(prop,callBack) instance.setData(instanceData) callBack(instanceData[prop]) deleteinstance.watch deleteinstance.globalData } } addEvent(eventName,callBack){ this.event[eventName]=this.event[eventName]||[] this.event[eventName].push(callBack) } dispatch(eventName,newValue){ this.app.globalData[eventName]=newValue this.event[eventName]&&this.event[eventName].forEach(item=>item(newValue)) } } App({ globalData:{ userInfo:null }, onLaunch(){ //new一个实例并保存到小程序app中,用于全局调用 this.store=newStore(this) wx.getSetting({ success:res=>{ if(res.authSetting['scope.userInfo']){ wx.getUserInfo({ success:res=>{ //获取到userInfo后,触发事件 this.store.dispatch('userInfo',res.userInfo) } }) } } }) } })
//Pages/index/index.js constapp=getApp() Page({ //... data:{ userName:null }, //globalData数组用于自动赋值 globalData:['userInfo'], //监听相应的globalData属性,设置回调函数 watch:{ userInfo(userInfo){ console.log('userInfo更新啦',this) this.setData({userName:userInfo.nickName}) } }, onLoad(options){ //传入此globalData,和实例,设置该实例需要的data,创建事件 app.store.autoSet(this.globalData,this) //其他你想做的... } })
上面的代码并没有劫持钩子函数,只是额外在函数开始时执行了绑定函数,而且也没有页面销毁时,释放内存的操作。还是有许多可优化的地方,这些都留到下一次讲解。
总结
到此这篇关于微信小程序全局状态的文章就介绍到这了,更多相关小程序全局状态内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。