相關學習推薦:小程式開發教學課程
#在做各種各樣的業務時,我們不可避免的需要在業務中進行埋點,這些埋點通常包含但不限於曝光、點擊、停留時長、離開頁面等場景,而在小程式中因為其和瀏覽器不同的架構,導致了監聽頁面變的更加困難,通常我們都會透過重寫Page
方法來達到對小程式原生生命週期的攔截代理,從而進行業務埋點,但是在Taro
中這一切變得不同了。
在多端統一的Taro
中,我們不再能看到顯式的Page
調用,甚至Taro
打包之後的程式碼裡也不再存在任何Page
的跡象,取而代之的則是小程式原生的Component
(這一點大家透過觀察打包後的內容可以得知),所以為了實作微信小程式在Taro
中的自動埋點,我們需要換一個策略:重寫Component
。
在微信小程式中,其暴露的Component
和Page
能夠直接被重寫並進行賦值:
const _originalComponent = Component;const wrappedComponent = function (options) { ...do something before real Component return _originalComponent(options); }复制代码
這樣可以很快的解決問題,但是當我們在另一個小程式做這件事情的時候,我們就又需要手動做一次這些處理,難免有些麻煩,為什麼不找一個更通用的方案,我們只用關注我們需要關注的業務(埋點)就行了呢?
重中之重,從零開始思考,掌握真正問題,接近問題本質
在解決問題之前,不如讓我們先看看這個問題的本質是什麼。想在小程式中進行自動的埋點,其實要做的就是在小程式指定的生命週期裡做一些固定的處理,所以我們自動埋點的問題實際上是如何劫持小程式的生命週期,而要劫持小程式的生命週期,我們需要做的就是重寫options
。
在解決這個問題之前,我們要把自己需要解決的問題拆分出來:
options就已經有了答案,我們只需要在原始小程式提供的方法外再包裹一層即可解決,同時為了確保我們的解決方案能適用於原生小程式和
Taro這種多端統一的小程式方案,我們應該同時支援重寫
Component和
Page,而對於最後一個問題,我們可以思考一下
js中的事件系統,相似的我們也可以實現一套發布訂閱的邏輯,只需要自訂觸發事件(生命週期)和
listeners,再針對生命週期原有邏輯進行包裝即可;
ComponentPage
之前應保存原始的方法,避免原始方法被污染我們無法回退,這之後再去將小程式中的所有生命週期進行枚舉生成一個預設的事件物件中,保證我們在註冊了對應生命週期的
listeners後能透過尋址找到並對原生命週期方法進行重寫。
export const ProxyLifecycle = { ON_READY: 'onReady', ON_SHOW: 'onShow', ON_HIDE: 'onHide', ON_LOAD: 'onLoad', ON_UNLOAD: 'onUnload', CREATED: 'created', ATTACHED: 'attached', READY: 'ready', MOVED: 'moved', DETACHED: 'detached', SHOW: 'show', HIDE: 'hide', RESIZE: 'resize', };public constructor() { this.initLifecycleHooks(); this.wechatOriginalPage = getWxPage(); this.wechatOriginalComponent = getWxComponent(); }// 初始化所有生命周期的钩子函数private initLifecycleHooks(): void { this.lifecycleHooks = Object.keys(ProxyLifecycle).reduce((res, cur: keyof typeof ProxyLifecycle) => { res[ProxyLifecycle[cur]] = [] as WeappLifecycleHook[]; return res; }, {} as Record<string, WeappLifecycleHook[]>); }复制代码
public addLifecycleListener(lifeTimeOrLifecycle: string, listener: WeappLifecycleHook): OverrideWechatPage { // 针对指定周期定义Hooks this.lifecycleHooks[lifeTimeOrLifecycle].push(listener); const _Page = this.wechatOriginalPage; const _Component = this.wechatOriginalComponent; const self = this; const wrapMode = this.checkMode(lifeTimeOrLifecycle); const componentNeedWrap = ['component', 'pageLifetimes'].includes(wrapMode); const wrapper = function wrapFunc(options: IOverrideWechatPageInitOptions): string | void { const optionsKey = wrapMode === 'pageLifetimes' ? 'pageLifetimes' : ''; options = self.findHooksAndWrap(lifeTimeOrLifecycle, optionsKey, options); const res = componentNeedWrap ? _Component(options) : _Page(options); options.__router__ = (wrapper as any).__route__ = res; return res; }; (wrapper as any).__route__ = ''; if (componentNeedWrap) { overrideWxComponent(wrapper); } else { overrideWxPage(wrapper); } return this; }/** * 为对应的生命周期重写options * @param proxyLifecycleOrTime 需要拦截的生命周期 * @param optionsKey 需要重写的 optionsKey,此处用于 lifetime 模式 * @param options 需要被重写的 options * @returns {IOverrideWechatPageInitOptions} 被重写的options */private findHooksAndWrap = ( proxyLifecycleOrTime: string, optionsKey = '', options: IOverrideWechatPageInitOptions, ): IOverrideWechatPageInitOptions => { let processedOptions = { ...options }; const hooks = this.lifecycleHooks[proxyLifecycleOrTime]; processedOptions = OverrideWechatPage.wrapLifecycleOptions(proxyLifecycleOrTime, hooks, optionsKey, options); return processedOptions; };/** * 重写options * @param lifecycle 需要被重写的生命周期 * @param hooks 为生命周期添加的钩子函数 * @param optionsKey 需要被重写的optionsKey,仅用于 lifetime 模式 * @param options 需要被重写的配置项 * @returns {IOverrideWechatPageInitOptions} 被重写的options */private static wrapLifecycleOptions = ( lifecycle: string, hooks: WeappLifecycleHook[], optionsKey = '', options: IOverrideWechatPageInitOptions, ): IOverrideWechatPageInitOptions => { let currentOptions = { ...options }; const originalMethod = optionsKey ? (currentOptions[optionsKey] || {})[lifecycle] : currentOptions[lifecycle]; const runLifecycleHooks = (): void => { hooks.forEach((hook) => { if (currentOptions.__isPage__) { hook(currentOptions); } }); }; const warpMethod = runFunctionWithAop([runLifecycleHooks], originalMethod); currentOptions = optionsKey ? { ...currentOptions, [optionsKey]: { ...options[optionsKey], ...(currentOptions[optionsKey] || {}), [lifecycle]: warpMethod, }, } : { ...currentOptions, [lifecycle]: warpMethod, }; return currentOptions; };复制代码
listeners,使用被重寫過
Component或
Page就會自動觸發這些
listeners。
Taro等多端統一方案進行這套通用的解決方案,我實作了一個外掛程式來解決這個問題(私心安利)
npm install weapp-lifecycle-hook-plugin 或者 yarn add weapp-lifecycle-hook-plugin复制代码
import OverrideWechatPage, { setupLifecycleListeners, ProxyLifecycle } from 'weapp-lifecycle-hook-plugin'; // 供 setupLifecycleListeners 使用的 hook 函数,接受一个参数,为当前组件/页面的options function simpleReportGoPage(options: any): void { console.log('goPage', options); } // setupListeners class App extends Component { constructor(props) { super(props); } componentWillMount() { // ... // 手动创建的实例和使用 setupLifecycleListeners 创建的实例不是同一个,所以需要销毁时需要单独对其进行销毁 // 直接调用实例方式 const instance = new OverrideWechatPage(this.config.pages); // 直接调用实例上的 addListener 方法在全局增加监听函数,可链式调用 instance.addLifecycleListener(ProxyLifecycle.SHOW, simpleReportGoPage); // setupListeners 的使用 setupLifecycleListeners(ProxyLifecycle.SHOW, [simpleReportGoPage], this.config.pages); // ... } // ... }复制代码
setup 就能解決以前需要手動書寫一大堆的重寫邏輯,何樂而不為呢
想了解更多程式設計學習,請關注php培訓專欄!
#
以上是了解微信小程式 Taro 的自動埋點的詳細內容。更多資訊請關注PHP中文網其他相關文章!