小程序超级简单的跨页面跨组件通讯方式
发布于 4 年前 作者 juan11 3309 次浏览 来自 分享

非常简单小巧的跨页面跨组件通讯方式, 未压缩版4kb

实现很简单,

  1. 借助订阅发布模式实现跨页面跨组件的通讯
  2. 基于__wxWebviewId__和__wxExparserNodeId__实现只在当前的页面更新数据,减少性能消耗,并且切换到对应页面的时候,自动触发页面下的绑定方法
  3. 实现了哪怕先emit,后on的顺序也能正确触发绑定的函数,所以不用关心执行顺序

使用方式:

  1. 在合适的位置使用 note.on(‘scope’, method, this),监听字段变化

  2. 在任意位置使用 note.emit(‘scope’, data) 触发数据变更

API

note.get('scope') 获取数据,如果scope为空时获取所有数据

note.on(scope,method,this) 监听scope数据变化

note.emit(scope,data) 更改数据,并触发监听的方法

note.off(scope,this) 取消监听


话不多说,上代码

function isEmpty(obj: any) {
  for (let key in obj) return false
  return true
}

type Fn = (...args: any) => any;
interface NoteData {
  [scopeName: string]: {
    value: any;
    watch: {
      [webid: string]: {
        [nodeid: string]: (never | Fn)[];
      };
    };
  };
}

/**
 * 小程序消息通知
 * [@default](/user/default) 使用方法
 * 1. 在合适的位置使用 note.on('scope', method, this),监听字段变化
 * 2. 在任意位置使用 note.emit('scope', data) 触发数据变更
 * 
 * [@method](/user/method) note.get('scope') 获取数据,如果scope为空时获取所有数据
 * 
 * [@method](/user/method) note.on(scope,method,this) 监听scope数据变化
 * 
 * [@method](/user/method) note.emit(scope,data) 更改数据,并触发监听的方法
 * 
 * [@method](/user/method) note.off(scope,this) 取消监听 
 */

class Note {
  data: NoteData; // 数据存放
  constructor() {
    this.data = {};
  }
  /**
   * 获取数据
   * [@param](/user/param) scope 获取指定scope的value,不传时返回所有数据
   */
  get(scope?: string) {
    if (scope) return this.data[scope] ? this.data[scope].value : undefined;
    let res: any = {};
    Object.keys(this.data).map(key => {
      res[key] = this.data[key].value;
    });
  }

  /**
   * 修改数据,会触发绑定的方法
   * [@param](/user/param) scope 修改的节点
   * [@param](/user/param) value 修改的数据
   */
  emit(scope: string, value?: any) {
    const self = this;
    return new Promise((resolve: any, reject: any) => {
      try {
        // 数据中节点不存在
        if (!self.data[scope]) self.data[scope] = { value, watch: {} };
        else {
          const page: any = getCurrentPages().pop();
          Object.keys(self.data[scope].watch[page.__wxWebviewId__]).forEach(
            nodeId => {
              self.data[scope].watch[page.__wxWebviewId__][nodeId].forEach(
                fn => {
                  fn(value, self.data[scope].value);
                  self.data[scope].value = value;
                }
              );
            }
          );
        }

        resolve(self.data[scope].value, value);
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 绑定监听数据变化
   * [@param](/user/param) scope 监听的节点
   * [@param](/user/param) fn 监听的函数
   * [@param](/user/param) ctx 当前页面或者自定义组件的this
   */
  on(
    scope: string | (string[]),
    fn: any,
    ctx: WxComponent | Page.PageInstance
  ): any {
    const type = Object.prototype.toString.call(scope);
    if (type === "[object String]") return this._on(scope as string, fn, ctx);
    if (type === "[object Array]") {
      return (scope as string[]).map((sc: string) => this._on(sc, fn, ctx));
    }
  }
  /**
   * 取消绑定监听
   * @param scope 取消绑定的节点
   * @param ctx  当前页面或者自定义组件的this
   */
  off(scope: string | string[], ctx: WxComponent | Page.PageInstance): void {
    const type = Object.prototype.toString.call(scope);
    if (type === "[object String]") this._off(scope as string, ctx);
    if (type === "[object Array]") {
      (scope as string[]).map((sc: string) => this._off(sc, ctx));
    }
  }

  _on(scope: string, fn: Fn, ctx: WxComponent | Page.PageInstance): any {
    if (!this.data[scope]) this.data[scope] = { value: null, watch: {} };
    if (!this.data[scope].watch[ctx.__wxWebviewId__])
      this.data[scope].watch[ctx.__wxWebviewId__] = {};
    if (!this.data[scope].watch[ctx.__wxWebviewId__][ctx.__wxExparserNodeId__])
      this.data[scope].watch[ctx.__wxWebviewId__][
        ctx.__wxExparserNodeId__
      ] = [];
    this.data[scope].watch[ctx.__wxWebviewId__][ctx.__wxExparserNodeId__].push(
      fn.bind(ctx)
    );
    return this.data[scope].value;
  }

  _off(scope: string, ctx: WxComponent | Page.PageInstance) {
    try {
      delete this.data[scope].watch[ctx.__wxWebviewId__][
        ctx.__wxExparserNodeId__
      ];
      if (isEmpty(this.data[scope].watch)) delete this.data[scope].watch;
      if (isEmpty(this.data[scope].watch[ctx.__wxWebviewId__]))
        delete this.data[scope].watch[ctx.__wxWebviewId__];
    } catch (err) {}
  }
}
const notice = new Note()
export default notice;

注: 其他小程序如字节小程序中只要有__wxExparserNodeId__和__wxWebviewId__类似的属性都可以使用

1 回复

唉…奈何自己没文化

回到顶部