pico-pubsub

尽可能小的 PubSub 库。零依赖。149 字节。

之前我写过这篇文章。但我意识到...为什么不直接发布代码呢?

比竞争对手更小。

构建时考虑了 JS13K 游戏。比如 cred,不幸的是,它很快就需要减肥了,现在几乎有 25KB 了。

如果你有任何可以减少哪怕一个字节的想法,请分享。创建一个 issue!我不介意。

源码

这是完整的源码 (index.js):

let t = new EventTarget();
sub = (e, c) => (t.addEventListener(e, c), () => t.removeEventListener(e, c));
pub = (n, d) => t.dispatchEvent(new CustomEvent(n, { detail: d }));

用法

npm install pico-pubsub
import "pico-pubsub"
const unsub = sub('jump', function (anything) {
 console.log("someone jumped - " + anything.detail)
});
pub('jump', "a_user_id")
>> "someone jumped - a_user_id"
unsub()
pub('jump', "another_user_id")
>> Nothing happens now

问题排查

declare global {
 function pub(event: string, data: any): VoidFunction;
 function sub(event: string, callback: (data: CustomEvent) => void): void;
}

验证

以下命令将生成一个 149b 文件:

npx esbuild index.js --bundle --minify --format=esm --outfile=bundle.js

竞争对手

排名第二的是 nano-pubsub,它缩小到了令人印象深刻的 194b... 相当不错!只大了 ~30%

/**
 * @public
 */
export interface Subscriber<Event> {
 (event: Event): void;
}
/**
 * @public
 */
export interface PubSub<Message> {
 publish: (message: Message) => void;
 subscribe: (subscriber: Subscriber<Message>) => () => void;
}
/**
 * @public
 */
export default function createPubSub<Message = void>(): PubSub<Message> {
 const subscribers: { [id: string]: Subscriber<Message> } =
  Object.create(null);
 let nextId = 0;
 function subscribe(subscriber: Subscriber<Message>) {
  const id = nextId++;
  subscribers[id] = subscriber;
  return function unsubscribe() {
   delete subscribers[id];
  };
 }
 function publish(event: Message) {
  for (const id in subscribers) {
   subscribers[id](event);
  }
 }
 return {
  publish,
  subscribe,
 };
}

排名第三的是 tiny-pubsub,它引入了一个非关键函数,以及一个处理取消订阅的额外函数!太痛苦了!它达到了惊人的 401b,是 nano-pubsub 的两倍多!

let subscriptions = Object.create(null);
function subscribe(evt, func) {
 if (typeof func !== "function") {
  throw "Subscribers must be functions";
 }
 const oldSubscriptions = subscriptions[evt] || [];
 oldSubscriptions.push(func);
 subscriptions[evt] = oldSubscriptions;
}
function publish(evt) {
 let args = Array.prototype.slice.call(arguments, 1);
 const subFunctions = subscriptions[evt] || [];
 for (let i = 0; i < subFunctions.length; i++) {
  subFunctions[i].apply(null, args);
 }
}
function unsubscribe(evt, func) {
 const oldSubscriptions = subscriptions[evt] || [];
 const newSubscriptions = oldSubscriptions.filter((item) => item !== func);
 subscriptions[evt] = newSubscriptions;
}
function cancel(evt) {
 delete subscriptions[evt];
}
module.exports = { subscribe, publish, unsubscribe, cancel };

注意事项

如果你不想使用 window 对象,只需这样做,只是要知道这将花费你 7 个字节:

let t = new EventTarget();
export default {
 s: (e, c) => (t.addEventListener(e, c), () => t.removeEventListener(e, c)),
 p: (n, d) => t.dispatchEvent(new CustomEvent(n, { detail: d })),
};