【问题标题】:How do I call a method on a future instance inside of a method decorator?如何在方法装饰器内的未来实例上调用方法?
【发布时间】:2020-01-19 13:55:21
【问题描述】:

我想实现一个@listen(eventType) 装饰器工厂。当您希望在触发eventType 时调用该方法时,它将返回一个装饰器,您可以将其放置在 Web 组件类的方法上。

用法

class MyElement extends HTMLElement {
  //...
  @listen('click')
  log() {
    console.log('log');
  }
}

每次单击组件实例时,上面的代码都会将“日志”打印到控制台。

实施思路

在类构造函数中添加对addEventListener 的调用,并将disconnectedCallback 的当前定义替换为调用removeEventListenerdisconnectedCallback 版本,以避免内存泄漏。

实施

export function listen (
  eventName,
) {
  return function (
    proto, 
    methodName, 
    descriptor
  ) {
    // add listener when element is constructed
    const oldConstructor= proto.constructor;
    proto.constructor = function (...args) {
      this.addEventListener(eventType, descriptor.value);
      return oldConstructor.apply(this, ...args);
    };

    // remove listener to avoid leaking memory
    const oldDisconnectedCallback = proto.disconnectedCallback;
    proto.disconnectedCallback = function (...args) {
      this.removeEventListener(eventType, descriptor.value);
      return oldChange.apply(this, ...args);
    };
  };
}

尽管方法替换技巧适用于 disconnectedCallback(和任何其他)方法,但它不适用于构造函数。

REPL

这是一个交互式版本的实现:https://stackblitz.com/edit/lit-element-hello-world-ec1lwz?file=listen.js

【问题讨论】:

  • 你不需要像那样清理听众。浏览器会同时收集元素和监听器。

标签: typescript decorator web-component typescript-decorator


【解决方案1】:

我会在类中添加一个方法来注册静态监听器。

export const listen = (eventName) =>
  (proto, methodName, descriptor) => {
    const ctor = proto.constructor;
    if (!('addStaticEventListener' in ctor)) {
      throw new Error('The decorated class must have an ' + 
          'addStaticEventListener static method');
    }
    ctor.addStaticEventListener(eventName, proto[methodName]);
  };
};

export const StaticListeners = (base) => class extends base {
  static __staticEventListeners = [];
  static addStaticEventListener(eventName, method) {
    this.__staticEventListeners.push({eventName, method});
  }

  constructor(...args) {
    super(...args);
    for (const {eventName, method} of this.constructor.__staticEventListeners) {
      this.addEventListener(eventName, method.bind(this));
    }
  }
};

用法

class MyElement extends StaticListeners(HTMLElement) {
  @listen('click')
  log() {
    console.log('log');
  }
}

【讨论】:

  • 如果 mixin 是出于性能原因避免使用方法链,我制作了一个新版本,既避免了方法链,也不需要 mixin。你怎么看? stackblitz.com/edit/…
  • 我只是不认为属性装饰器应该像那样弄乱类原型。其他类成员是为类及其超类定义的。变异原型对虚拟机也不友好。
猜你喜欢
  • 1970-01-01
  • 2019-02-03
  • 2013-02-12
  • 2019-01-03
相关资源
最近更新 更多