6
头图

Written at the beginning

After some time of preparation, the author has recently successfully joined shopee. So there is no update content recently, and the output will continue after it stabilizes.

Write about what you will do after entering the job

After joining shopee, the instructor has given an entry task, which is to implement an event mechanism. Implement addEventListener , removeEventListener , dispatchEvent these three methods.

The request looks like this:

  • Compatible with W3C's event bubbling and event capture model (useCapture parameter of addEventListener)
  • Each event will have a priority (set by the developer, the highest is 0, the larger the number, the lower the priority, if not set, the default is 0), if multiple events need to be executed at the same time, follow Priority is executed from high to low; if multiple events have the same priority, the first defined event will be executed first
  • When the priority conflicts with the event bubbling/capture model, priority is given to ensuring the execution order of event bubbling/capture
  • You need to write your code as a TypeScript module. Please refer to the documentation for the introduction method and API
  • If the same Listener of the same type is bound to the same element multiple times, the event will only be triggered once when the conditions are met. The priority of the event trigger is subject to the priority of the final registration.

There is something interesting about the bonus item: try to ensure that the sum of the execution time of all events in a frame of time (16ms) does not exceed 10ms (for the time being, there is no need to consider a single event exceeding 10ms). It is necessary to make it too late in this frame The executed event will be executed in the next frame (it still needs to be executed according to the priority)

My thoughts

W3C's event model is to capture first and then bubbling

For addEventListener:

The input parameters are the dom node, the name of the monitoring method, the callback method, and other configurations can be accepted with...opt.

implementation ideas:

The registered events are processed in the same way, and a weakmap object is created. The data structure is as follows:

{
    Dom:{
        handleName(监听方法名):{
        // 存放处理捕获和冒泡的数组
            bubble:[{ // 冒泡数组
                cb:()=>void // 事件触发回调
                range:0 // 此事件优先级
                once:false // 兼容opt的once参数
            }],
            capture:[] // 捕获数组,同上
        }
    }
}

Inside the method, the original dom node needs to be monitored once for the event when the user manually clicks the trigger. At this time, this event needs to be cached to cancel the monitoring removeEventListener

For removeEventListener:

The input parameter is the dom node, the name of the monitoring method, the callback, whether to use the capture model (optional, the default is false)

realization ideas:

The first is robustness processing, and then the corresponding event of the corresponding node in the weakmap object is deleted. Then remove the monitor added addEventListener

For dispatchEvent:

This method should be regarded as the key, his input parameters are the dom node and the name of the monitoring method.

realization ideas:

  • First, perform robustness processing, and then recursively store the capture and bubbling arrays of the current node and the parent node into the array.
  • Call the callbacks in the array in turn.
  • For the 10ms implementation, the api used is requestAnimationFrame, rAF passes in a callback, and a time parameter can be obtained in the callback. The time parameter indicates the time when the callback function sorted by requestAnimationFrame() is triggered. For multiple callback functions in the same frame, each of them will receive the same timestamp, even if some time has been consumed during the calculation of the workload of the previous callback function. The timestamp is a decimal number, in milliseconds, with a minimum precision of 1ms (1000μs) (--from MDN). Then compare the timestamp obtained by the performance.now() operation with the time received by the current rAF callback, if you can continue to fetch events from the array for calling within 10ms. Otherwise, put the next frame
  • The realization of once, if there is an once parameter, the current object reference is set to an empty object

Here can provide me with some ideas, you can also try to write it yourself.

// 递归的去将当前节点和父节点存入数组
function recurrenceFindNodeList(
  caps: callbackType[],
  bubs: callbackType[],
  node: nodeType,
  handleName: string
) {
  const parent: any = node.parentNode;
  if (eventMap.has(parent)) {
    const parentObj = eventMap.get(parent)[handleName];
    caps = [...parentObj.capture, ...caps];
    bubs = [...bubs, ...parentObj.bubble];
    recurrenceFindNodeList(caps, bubs, parent, handleName);
  }
  return [...caps, ...bubs];
}

// 对于10ms 的实现

requestAnimationFrame(handler);
function handler(time: number) {
    let taskFinishTime: number = window.performance.now();
    while (taskFinishTime - time < 10) {
      const nextTask = tasklist.shift();
      if (nextTask?.cb) {
        nextTask.cb();
      }
      taskFinishTime = window.performance.now();
    }
    if (tasklist.length > 0) {
      requestAnimationFrame(handler);
    }
  }

Written at the end

Shopee is a very young company. From a technical point of view, you can learn a lot of new technologies and participate in the process of their projects from 0 to 1. I believe that there will be great progress here. If you want to know about Xiaopi, please add me to WeChat: zhi794855679.

I am willing to live up to my efforts, and all I want is what I want.


greet_eason
482 声望1.4k 粉丝

技术性问题欢迎加我一起探讨:zhi794855679