如何处理小程序独立分包的数据共享问题

3,398 阅读3分钟

小程序2.3.0版本开始支持独立分包。对于短期的活动落地页,我们会选择使用独立分包,这可以大大提升活动落地页的加载速度。

但与此同时,由于独立分包中不能依赖主包和其他分包中的内容,独立分包的使用也带来了一些数据共享问题。

遇到的问题

公用数据处理复杂

对于页面间的公用数据,我们原本的处理方式是将数据挂在App对象上。但引入了独立分包后,判断的逻辑就变得复杂了。

例如,我们想设计一个计数器counter,能够在小程序的各个地方调用。我们将结果记录在app.globalData.count,这时候需要分三种场景考虑:

  1. 主包和普通分包页面中,通过getApp()来获取App对象;
  2. 在App对象中,通过this来获取App对象;
  3. 在独立分包或App对象注册前,通过getApp({ allowDefault: true })来获取App对象(新的逻辑);

另外,对于3的情况,由于App对象可能未初始化,还要判断globalData、count属性是否存在

事件被重复绑定

通过wx.onError、wx.onPageNotFound等方法可以监控小程序的运行情况,我们把这些能力封装在npm包中。

export function report() {
  // 各种处理逻辑
  // ....
}

wx.onError(report);

当独立分包和主包都引入了这个npm包,而npm包中调用wx.onXXXX方法进行了绑定,我们可以想到,当用户在独立分包和主包页面之间跳转时,事件的处理函数会被绑定不止一次(主包一次,每个独立分包一次)。

解决思路

  1. 对于需要在主包、独立分包公用数据的情况,我们考虑将不同场景下读写app对象的能力封装起来,这类似于一个SessionStorage,我们可以在任意场景操作SessionStorage里的公用数据,而数据会在小程序从冷启动到销毁的运行过程中一直保留。

  2. 对于事件重复绑定的问题,我们使用sessionStorage中的一个key来加锁。实现一个once方法,保证同一个key的逻辑只执行一次,通过如下的方式来调用

    export function report() {
      // 各种处理逻辑
      // ....
    }
    
    once('_wx_onerror_key_', () => { wx.onError(report); });
    

具体实现

  1. 为了不与原有的globalData冲突,我们使用一个新的BASIC_KEY,作为App对象上的属性名,来存储sessionStorage的内容。
  2. 默认以getApp({ allowDefault: true })获取App对象,为了满足在App()内调用时也能取到正确的App对象,我们可以在App.onLaunch方法时将this传入sessionStorage.setApp(this)
  3. 暴露的api对齐浏览器的sessionStorage,实现如下:
const BASIC_KEY = '_imwxutils_sessionStorageData_';
let app = getApp({ allowDefault: true }) || {};
app[BASIC_KEY] = app[BASIC_KEY] || {};

export function setApp(customApp) {
  customApp[BASIC_KEY] = customApp[BASIC_KEY] || app[BASIC_KEY] || {};
  app = customApp;
}

export function setItem(key, value) {
  app[BASIC_KEY][key] = value;
}

export function getItem(key) {
  return app[BASIC_KEY][key];
}

export function removeItem(key) {
  app[BASIC_KEY][key] = null;
}

export function clear() {
  app[BASIC_KEY] = {};
}

基于sessionStorage我们又可以实现once方法:

import * as sessionStorage from './session-storage';

const ONCE_BASIC_KEY = '_imwxutils_once_record_';
/**
 * 全局只执行一次的方法
 */
function once(key, func) {
  if (!key) {
    return;
  }
  const record = sessionStorage.getItem(ONCE_BASIC_KEY) || {};

  if (record[key]) { // 之前执行过了
    return;
  }
  record[key] = true;
  sessionStorage.setItem(ONCE_BASIC_KEY, record);
  func();
}

以上,通过实现sessionStorage和once方法,我们解决了独立分包与主包之间数据共享以及事件绑重复定的问题。

欢迎交流👏