手写 Vue 事件机制

实现 Vue 的 $on、$off、$emit、$once 方法

问题

Vue 的事件机制允许组件之间通信,需要实现 $on$offemit$once 方法来理解其工作原理。

解答

Vue 事件机制基于发布订阅模式,通过一个事件中心来管理事件的注册、触发和销毁。

class EventEmitter {
  constructor() {
    this.events = {};
  }

  // 监听事件
  $on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
    return this;
  }

  // 取消监听
  $off(event, callback) {
    if (!this.events[event]) return this;
    
    // 如果没有传递回调,移除该事件的所有监听
    if (!callback) {
      this.events[event] = [];
      return this;
    }
    
    // 移除特定回调
    this.events[event] = this.events[event].filter(cb => cb !== callback);
    return this;
  }

  // 触发事件
  $emit(event, ...args) {
    if (!this.events[event]) return this;
    
    this.events[event].forEach(callback => {
      callback(...args);
    });
    return this;
  }

  // 只监听一次
  $once(event, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.$off(event, wrapper);
    };
    this.$on(event, wrapper);
    return this;
  }
}

使用示例:

const emitter = new EventEmitter();

// 监听事件
emitter.$on('test', (msg) => {
  console.log('收到消息:', msg);
});

// 触发事件
emitter.$emit('test', 'Hello Vue'); // 输出: 收到消息: Hello Vue

// 只监听一次
emitter.$once('login', (user) => {
  console.log('用户登录:', user);
});

emitter.$emit('login', 'Alice'); // 输出: 用户登录: Alice
emitter.$emit('login', 'Bob');   // 无输出

// 取消监听
const handler = () => console.log('clicked');
emitter.$on('click', handler);
emitter.$off('click', handler);

关键点

  • $on 将回调函数存储到事件数组中,支持同一事件注册多个回调
  • $off 不传回调时移除该事件所有监听,传回调时只移除匹配的回调
  • $emit 遍历事件数组,依次执行所有回调并传递参数
  • $once 通过包装函数实现,执行后立即调用 $off 移除自身
  • 所有方法返回 this 支持链式调用