17,18年的時候,我當時主要開發小程序,那時候領導想看一下小程序的訪問量,還有一些埋點的需求,於是我們的小程序就接入了阿拉丁統計。
阿拉丁的接入方式除了配置以外,主要就一行引入代碼。官方要求將以下代碼寫在app.js第一行代碼。
const ald = require('./utils/ald-stat.js')
將代碼放到app.js第一行代后,神奇的事情發生了,簡簡單單的一行代碼,就可以統計到頁面的訪問量,分享量。而且每次觸發分享都能在Network里面看到有接口請求。我出於好奇想知道它是怎么做到的,所以就扒了一下它的源代碼,發現核心代碼是通過重寫Page方法實現的。
切入這篇文章的正題,如何重寫Page方法?首先我們先看一下新建一個Page,編輯器生成的代碼
Page({ /** * 頁面的初始數據 */ data: { }, /** * 生命周期函數--監聽頁面加載 */ onLoad: function (options) { } /** * 其他生命周期 省略... */ })
通過以上代碼我們可以發現,Page實際上是一個全局的方法,參數是一個對象,該對象有一些data和其他的生命周期。我們如果要重寫Page方法,一定要在小程序初始化最早的時期重新賦值Page方法為我們自己方法,且要將原來的Page方法保存以備未來調用。
(function(){ // 小程序原來的Page方法 let originalPage = Page; // 我們自定義的Page方法 Page = (config) => { // todo 在這里我們可以給配置對象進行加工 // 將配置對象繼續想下傳遞給小程序原來的Page方法 originalPage (config); } })();
將以上代碼放到app.js頂部,即可實現了對Page對象的重寫。
然而對Page對象重寫有什么好處呢?我簡單的列舉兩條
1) 可以實現全局頁面的生命周期的監控和處理(埋點、分享統計、全局分享設置)
2) 可以實現為所有的配置對象,增加函數和屬性,來實現更好的封裝。結果類似調用Vue的Vue.prototype。
3) 可以統一處理掃小程序二維碼,獲取小程序二維碼參數的邏輯
再舉兩個實際的業務場景例子
1)實現一個ajax方法,掛載到所有的配置對象上,所有的頁面調用this.ajax即可以請求接口
2) 攔截所有的小程序分享,如果頁面有設置分享信息,就用頁面的分享信息,如果沒有分享信息的話,就用全局統一的分享信息;且所有的鏈接后面追加分享人ID
以下是實現代碼,注意:需要將代碼放到app.js頂部
(function(){ // 小程序原來的Page方法 let originalPage = Page; // 我們自定義的Page方法 Page = (config) => { // 頁面里可以通過this.ajax調用請求接口的方法 config.ajax = function(){ // 寫wx.request的相關代碼 } // 默認分享信息 let defaultShareInfo = { path: appendInviteId('/pages/index/index'), title: '全局設置的分享標題' } // 追加邀請人Id 參數: path(頁面路徑) function appendInviteId(path){ // 分享人Id寫成靜態的 實際可能要讀取緩存 let inviteId = '9998877'; // 路徑是否包含inviteId 包含就返回路徑 if(path.includes('inviteId')){ return path; // 路徑不包含inviteId 則追加 } else { return path.includes('?') ? `${path}&inviteId=${inviteId}` : `${path}?inviteId=${inviteId}`; } } // 重寫onShareAppMessage方法 let originalShareMethod = config.onShareAppMessage; config.onShareAppMessage = function(e) { // 配置對象有onShareAppMessage方法 if( originalShareMethod ){ // 配置對象實際返回的分享信息 let returnVal = originalShareMethod.call(this, e) // 如果有返回信息 if(returnVal) { // 頁面的分享信息沒有邀請人id 則追加 returnVal.path = appendInviteId(returnVal.path) return returnVal // 如果頁面對象沒有返回信息 } else { return defaultShareInfo } // 配置對象沒有onShareAppMessage方法 直接返回默認的分享信息 } else { return defaultShareInfo } } // 將配置對象繼續想下傳遞給小程序原來的Page方法 originalPage (config) } })();