最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 微信小程序全局状态

    正文概述 掘金(明月依旧)   2021-01-11   591

    前言

    在微信小程序中,可以利用 App.js 的 globalData 作为中间桥梁,在 Page, Component 之间,包括页面与页面,页面与组件,组件与组件之间传递需要传递的信息。但是,我们不能及时的知道 globalData 下的变化,在新建小程序的官方的默认事例中,获取 UserInfo 这一网络操作有延迟的,为此写了很多不必要的代码。就连官方案例都存在这一情况,相信在开发中你也会遇到类似的情况。在本文中将介绍如何解决这一类问题。

    需求分析

    相信以下情况是我们在没有全局状态管理下常有的操作:

    1. 在 Page,Component 的 OnLoad,Attached 两个生命周期钩子函数中,进行一些从 App 的 globalData 赋值一些已经存在的属性到页面或组件中的 data 中。

    2. 在最开始就存在一些异步的网络请求,获取的数据用于全局,刚开始可能这个 globalData 还没有相关属性,直到请求成功,才把相关属性添加到 globalData,而这时 Page 的从 globalData 的赋值操作可能已经完成了,只不过是 undefined,为此需要进一步的判断再进行赋值到 Page,Component 中。如果只是一两个这个还说很简单的,但是多个页面,或者多个变量都需要赋值的话,我想你会拒绝并寻找偷懒的办法。

    3. 一些在页面和组件从 globalData 赋值的变量不仅是用于判断、展示,我们可能还需要依据用户交互而改变变量的值,那么在其他页面,其他组件中同样的变量也需要统一改变。

    以上情况我们可提出以下几点需求:

    1. 在页面,组件初始加载时,尽早的从 globalData 获取并赋值到页面,组件所需要的一些属性

    2. 及时的获取一些 globalData 某一属性的变化,并进行一些后续相关操作

    3. 在改变 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
    const app = getApp()
    Page({
        // ...
        onLoad(options){
            // 需求1
            const userInfo = app.globalData.userInfo
            userInfo && this.setData({useInfo})
            // 需求2
            // 如果没有获取到 app.globalData.userInfo
            // 意味还未执行 wx.getUserInfo 的回调函数
            // 给 app 添加响应的一个回调函数,绑定此时的 this 到回调函数
            userInfo || app.userInfoReadyCallback = res => {
                this.setData({
                    userInfo: res.userInfo
                })
                delete app.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
    class Store {
      constructor(app){
        this['event'] = {}
        this.app = app
      }
      autoSet(globalData, instance){
        const instanceData = {}
        for (let prop of globalData){
          instanceData[prop] = this.app.globalData[prop]
          const callBack = (newValue) => {
            instance.setData({[prop]: newValue})
            instance.watch[prop] && instance.watch[prop].call(instance, newValue)
          }
          this.addEvent(prop, callBack)
          instance.setData(instanceData)
          callBack(instanceData[prop])
          delete instance.watch
          delete instance.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 = new Store(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
    const app = 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)
            // 其他你想做的...
        }
    })
    

    上面的代码并没有劫持钩子函数,只是额外在函数开始时执行了绑定函数,而且也没有页面销毁时,释放内存的操作。还是有许多可优化的地方,这些都留到下一次讲解。


    起源地下载网 » 微信小程序全局状态

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元