/ Vue

实现 VUE 中 MVVM - step7 - Event

虽然实现 VueMVVM 已经走到第 7 步,但这节内容与之前的内容相关性不大,但若想要了解整个 MVVM 的实现,还是先移步看之前已经实现的 6

  1. 实现 VUE 中 MVVM - step1 - defineProperty
  2. 实现 VUE 中 MVVM - step2 - Dep
  3. 实现 VUE 中 MVVM - step3 - Watcher
  4. 实现 VUE 中 MVVM - step4 - 优化Watcher
  5. 实现 VUE 中 MVVM - step5 - Observe
  6. 实现 VUE 中 MVVM - step6 - Array

事件是什么?

在标准浏览器中,我们经常使用:addEventListener 来为一个 DOM 添加一个事件(clickmousemovetap等)。

在我看来,一个事件是一种行为(或情况),当发生这种行为(或情况)时,我们要去做的事,比如今天下雨了,那我就得去找伞;闹钟响了,那我就得起床等等。

仔细看这些情况,归结到代码中,无非就是一个行为(或情况)的名称,和一些列的动作,而在 js 中动作就是 function,一系列的动作就是一个函数的集合。

实现

如上所说,我们把事件抽象成一个类

类下属性 & 方法

  • _events/Object: 用于存放事件名称(key/String)和一些列的动作(value/Array<Function,Function...>)
  • $on(eventName, func)/Function: 用于添加具体事件的处理函数
  • $off(eventName)/Function: 既然有添加肯定是有取消
  • $emit(eventName, data1, data2, ...)/Function: 用于触发事件
  • $once(eventName, func)/Function: 用于添加仅仅触发一次的事件,作为一个语法糖的存在,比如你想在特定时间设置一个闹钟,显然这个闹钟并不是你的起床闹钟,仅仅需要触发一次就够了

ok 根据我们的构想,在来看这个实现好的 Event

let uid = 0

export class Event {
    constructor() {
        this.id = ++uid
        this._events = {}
    }

    $on(eventName, fn) {
        let object = this;
        // 若 _events 对象下无对应事件名,则新建一个数组,然后将处理函数推入数组
        (object._events[eventName] || (object._events[eventName] = [])).push(fn)
        return object
    }

    $once(eventName, fn) {
        let object = this

        function on() {
            // 先取消,然后触发,确保仅触发一次
            object.$off(eventName)
            fn.apply(object, arguments)
        }

        on.fn = fn
        object.$on(eventName, on)
        return object
    }

    $off(eventName) {
        let object = this
        const cbs = object._events[eventName]
        if (cbs) {
            // 取消置空即可
            object._events[eventName] = null
        }
        return object
    }

    $emit(eventName, ...args) {
        let object = this
        let cbs = object._events[eventName]
        if (cbs) {
            cbs.forEach(func => func.apply(object, args))
        }
        return object
    }

}

一个简单的事件管理的类便实现好了,让我们来测试一下:

import {Event} from "./Event";

let eventTest = new Event()

eventTest.$on('testEvent', function (event) {
    console.log('测试事件添加,传入参数为' + event)
})

eventTest.$emit('testEvent', '事件触发成功')
// 测试事件添加,传入参数为事件触发成功

eventTest.$emit('testEvent', '事件再次触发成功')
// 测试事件添加,传入参数为事件再次触发成功

eventTest.$off('testEvent')

eventTest.$emit('testEvent', '事件取消,不会有输出')
// 无输出

eventTest.$once('testOnce', function (event) {
    console.log('事件仅仅触发一次,传入参数为' + event)
})

eventTest.$emit('testOnce', '事件触发成功')
// 事件仅仅触发一次,传入参数为事件触发成功

eventTest.$emit('testOnce', '事件取消,不会有输出')
// 无输出

ok 一个简易的事件管理实现了,由于这节内容与上几节关系不大,所以这里再次说下测试代码的运行方式:

  1. node 环境 8.11.1 往上,不然不能够支持 import 语法
  2. 为了不转码支持 import 语法,文件后缀为 .mjs
  3. 进入到 test.mjs 所在目录命令行运行:node --experimental-modules test.mjs 即可

点击查看相关代码

更多内容,可以访问http://blog.acohome.cn