概述

在上一篇《微信小程序实现类似Vue的全局路由,实现日志记录》中,通过重写wx.navigateTo,wx.switchTab,wx.navigateBack这三个方法实现类似全局路由的功能,但重写这三个方法后仍有一些局限性,无法拦截底部tab的点击事件和安卓手机的物理返回键的返回事件。

如果不需要在跳转前进行拦截,那么有一个更好的方案,重写Page函数。

解决方案

新建myPage.js文件,代码如下:

let data
// 这里data从app.js中传入,为了获取data的一些参数
function init(_data) {
  data = _data
  // 重写page函数,增加阿里云监控和日志记录
  let oldPage = Page
  Page = function(obj) {
    // 重写onShow方法,用一个变量保存旧的onShow函数
    let oldOnShow = obj.onShow
    obj.onShow = function() {
      enterPageLog()
      // 此处不能写成oldOnShow(),否则没有this,this.setData等方法为undefined。这里的this在Page构造函数实例化的时候才会指定
      // 在Page构造函数实例化的时候,小程序会将当前的Page对象的原型链(__proto__)增加很多方法,例如setData。当前的obj没有setData
      // 上面一段是我猜的
      oldOnShow.call(this)
    }
    // 重写onHide方法,用一个变量保存旧的onHide函数
    let oldOnHide = obj.onHide
    obj.onHide = function() {
      leavePageLog()
      // 此处不能写成oldOnHide(),否则没有this,this.setData等方法为undefined。这里的this在Page对象实例化的时候才会指定
      oldOnHide.call(this)
    }
    return oldPage(Monitor.hookPage(obj))
  }
}

将这一串代码封在一个js文件中,然后在app.js中引入,在app.js的onLaunch方法中调用该init方法。

import myPage from '/utils/myPage'
App({
  data: {},
  onLaunch: function() {
    // 重写page函数
    myPage.init(this.data)
  },
  ...
})

以上代码中,

enterPageLog()和leavePageLog()是为了记录日志,这样就实现了公共的日志记录,不需要在每个页面写重复的记录日志的代码。

Monitor.hookPage(obj)是阿里云的前端监控,官方的方案是在每个Page函数的传参对象包一层,如下:

Page(Monitor.hookPage(options));

重写Page后,不必要在每个Page对象都加这一串。

关键代码解读

let oldOnShow = obj.onShow
obj.onShow = function() {
  enterPageLog()
  oldOnShow.call(this)
}

重写onShow函数,做完自定义事情,调用旧的onShow函数时,不能写成oldOnShow()。若写成oldOnShow(),在运行时会报错,this.setData、this.createSelectorQuery等方法为undefined。

原因是在app的onLaunch方法中,传入Page函数的对象还没有setData等方法,就是以上代码onShow中的this,也就是obj。在Page构造函数实例化的时候,小程序会将当前的Page函数的原型链(prototype)增加很多方法,例如setData。可以查看这一篇《从源码看微信小程序启动过程》了解小程序启动和加载各对象的过程。

此处使用 oldOnShow.call(this) 来延迟指定this,这个this只有在Page实例化的时候才会指定,也就是上面说的Page实例化后增加了很多方法的this。这就避开了当前Page没有setData、createSelectorQuery等方法的问题。call的原理和用法可以查看这一篇《深入理解JavaScript中的Call、Apply、This》

结论

这里只是实现了简单的日志记录功能,通过重写Page,也可以实现其他一些公共功能,包括增加公共函数、参数、事件,权限判断等。

JS语言的特性可以重写很多原生的或者系统的方法,合理的重写可以实现很多公共的功能,减少开发量。