>

API及事件监听,Fiber的优先级调度机制与事件系统

- 编辑:www.bifa688.com -

API及事件监听,Fiber的优先级调度机制与事件系统

时间: 2019-12-30阅读: 55标签: 机制

放自己用DOM写的作品链接
写于2017.08.07
一、关于DOM

事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信,DOM支持大量的事件;

经典的事件系统分成两大块,绑定事件与分派事件,在浏览器中,分派事件很少人会直接dispatchEvent。因为创建一个DOM 事件是非常复杂的事情,不同的事件对象对应不同的事件构造器,传参也五花八门。因为分派事件基本上用户行为触发,比如我们点击了某个元素,恰逢在这上方绑定了点击事件,于是触发了。

  • 什么是DOM
    DOM是JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JavaScript对象,从而可以用脚本进行各种操作(比如增删内容)。
    浏览器会根据DOM模型,将结构化文档(比如HTML和XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。所以,DOM可以理解成网页的编程接口。
    严格地说,DOM不属于JavaScript,但是操作DOM是JavaScript最常见的任务,而JavaScript也是最常用于DOM操作的语言
  • DOM的节点
    DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
    节点的类型有七种。
    *Document:整个文档树的顶层节点
    *DocumentType:doctype标签(比如<!DOCTYPE html>)
    *Element:网页的各种HTML标签(比如<body>、<a>等)
    *Attribute:网页元素的属性(比如class="right")
    *Text:标签之间或标签包含的文本
    *Comment:注释
    *DocumentFragment:文档的片段
    DOM树如下图:

本文通过这几点向大家详细解析事件处理的基本原理:事件类型、事件目标、事件处理程序、事件对象、事件传播

React的绑定事件是在JSX 中进行,换言之, render时,props的onXXX事件就被收集起来,进行绑定。在jQuery时,人们发明了事件委托,将可以冒泡的事件挂在document或window对象上,进行统一的监听,实现高性能的事件系统。现在我们可以无视IE8 ,因此对不可冒泡的事件可以使用事件捕获,对能冒泡的事件使用事件冒泡,也是放在顶层对象 上进行监听。

必发88官网 1

最后再向大家介绍Event对象;

function trapBubbledEvent(topLevelType, element) { //by 司徒正美 trapEventForPluginEventSystem(element, topLevelType, false);}function trapCapturedEvent(topLevelType, element) { trapEventForPluginEventSystem(element, topLevelType, true);}

image.png

一、事件类型:是一个用来说明发生了什么类型事件的全小写的字符串,如‘mouseover'传统事件类型:表单事件,Window事件,鼠标事件,键盘事件,DOM事件, HTML5事件,触摸屏和移动设备事件等

大家可能觉得很奇怪,浏览器的dom.addEventListener(type, fn, capture)怎么也要三个参数, 原因是fn是一个统一调度的方法,因此可省掉一个。而冒泡与捕获则通过不同的方法做区分,实际干活的是trapEventForPluginEventSystem。

具体DOM学习建议参考链接:http://javascript.ruanyifeng.com/#dom
二、事件模型

二、事件目标:触发事件的对象

trapEventForPluginEventSystem根据事件名进行分大三类,DiscreteEvent,UserBlockingEvent,ContinuousEvent,选出不同的事件派发器。

  • 什么是事件
    事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信。一般来讲,有一下特点:
    1、某某订阅了/关注/监听了×××
    2、×××发生变化
    3、某某得到通知
    体现在DOM机制里就是:用户的操作发生变化,代码得到通知

  • 事件EventTarget接口
    DOM的事件操作(监听和触发),都定义在EventTarget接口。Element节点、document节点和window对象,都部署了这个接口。此外,XMLHttpRequest、AudioNode、AudioContext等浏览器内置对象,也部署了这个接口。
    该接口就是3个方法:
    1、addEventListener:绑定事件的监听函数
    例:
    target.addEventListener(type, listener[, useCapture]);
    type:事件名称,大小写敏感。
    listener:监听函数。事件发生时,会调用该监听函数。
    useCapture:布尔值,默认为false(监听函数只在冒泡阶段被触发)。true是在捕获阶段触发
    addEventListener方法可以为当前对象的同一个事件,添加多个监听函数。这些函数按照添加顺序触发,即先添加先触发。如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。
    2、removeEventListener:移除事件的监听函数
    例:
    div.addEventListener('click', listener, false);
    removeEventListener方法移除的监听函数,必须与对应的addEventListener方法的参数完全一致,而且必须在同一个元素节点,否则无效。
    3、dispatchEvent:触发事件
    dispatchEvent方法在当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),则返回值为false,否则为true
    例:
    para.addEventListener('click', hello, false);
    var event = new Event('click');
    para.dispatchEvent(event);
    上面代码在当前节点触发了click事件

  • 3种绑定监听函数的方法
    1、HTML标签的on-属性
    例:元素标签的属性中,直接定义某些事件的监听代码
    <body onload="doSomething()">
    <div onclick="console.log('触发事件')">
    上面代码为body节点的load事件、div节点的click事件,指定了监听函数。
    使用这个方法指定的监听函数,只会在冒泡阶段触发

    另外,Element元素节点的setAttribute方法,其实设置的也是这种效果。
    el.setAttribute('onclick', 'doSomething()');
    2、Element节点的事件属性
    Element节点对象有事件属性,同样可以指定监听函数。
    例:
    window.onload = doSomething;
    div.onclick = function(event){
    console.log('触发事件');
    };
    使用这个方法指定的监听函数,只会在冒泡阶段触发。
    3、addEventListener方法
    window.addEventListener('load', doSomething, false);(详解如上)
    总结:第一种“HTML标签的on-属性”,违反了HTML与JavaScript代码相分离的原则;第二种“Element节点的事件属性”的缺点是,同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。

  • 事件的捕获与冒泡阶段
    1、操作系统最先知道用户点击了鼠标,浏览器次之
    2、child被点击了,意味着parent也被点击了
    3、那么如果同时监听了child和parent,谁先通知我
    例子:如果对parent和child同时设置了监听,那么当我点击child,谁先通知我
    捕获阶段:parent先通知,child后通知(一般不用)
    冒泡阶段:child先通知,parent后通知

  • e.target与e.currentTarget
    e.target代表你点击的那个元素,而e.currentTarget代表你监听的那个元素,如果你处于捕获或者冒泡阶段,二者是不一样的

  • 阻止默认事件
    document.querySelector("a").addEventListener('click',function(e){
    e.preventDefault()
    })
    这样的话a就不会跳转了。如果在父元素阻止默认事件,那a也不跳转了,所以一般不建议这样做

  • 停止冒泡
    e.stopPropagation( )(在子元素加入,则不通知爸爸了,不会再向上传播)

  • 事件委托
    由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件委托
    例:
    HTML代码:
    <ul>
    <li>选项1</li>
    <li>选项2</li>
    <li>选项3</li>
    <li>选项4</li>
    </ul>
    JS代码:
    var ul =document.querySelector('ul')
    function fn(e){
    if(e.target.tagName ==="LI"){
    console.log("ok")(如果li里面裹一层span,点击span就不能执行监听函数)
    }
    }
    ul.addEventListener('click',fn)
    优化执行函数:
    function fn(e){
    var el = e.target
    while(el.tagName !=="LI"){
    el =el.parentNode(向上遍历父元素)
    }
    if(el){
    console.log('yes')
    }
    }
    但是实际上我只需要遍历到我监听的元素就可以了,如果直到遍历到监听的元素还没有的话,就认为没有了
    例:
    HTML代码:
    <div>
    <p>我是<span>p</span></p>
    <hi>我是<span>h1</span></hi>
    </div>
    JS代码:
    必发88官网,var div =document.querySelector('div')
    function fn(e){
    var el = e.target
    while(el.tagName !=="H1"){
    el =el.parentNode(向上遍历父元素)
    if(el===div){
    el=null
    break;
    }
    }
    if(el){
    console.log('yes')
    }
    }
    div.addEventListener('click',fn(e))

三、事件处理程序:处理或响应事件的函数。当某对象触发某事件时,浏览器将自动调用在该对象上注册的函数;注册事件处理程序:1.作为HTML属性注册如

function trapEventForPluginEventSystem(element, topLevelType, capture) { //by 司徒正美 var listener; switch (getEventPriority(topLevelType)) { case DiscreteEvent: listener = dispatchDiscreteEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; case UserBlockingEvent: listener = dispatchUserBlockingUpdate.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; case ContinuousEvent: //by 司徒正美 default: listener = dispatchEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM); break; } var rawEventName = getRawEventName(topLevelType); if (capture) { addEventCaptureListener(element, rawEventName, listener); } else { addEventBubbleListener(element, rawEventName, listener); }}

;而某些事件类型通常直接在浏览器上触发,而非任何特定文档元素上触发,把这些事件处理程序放在标签上,但浏览器会在Window对象上注册它们,如,这些事件有:onafterprint onfocus ononline onresize onbeforeprint onhashchangeonpagehide onstorage onbeforeunload onload onpageshow onundoonblur onmessage onpopstate onunload onerror onoffline onredo作为HTML属性的事件的值是JS代码字符串,是处理函数的主体,不含{},注意:尽量不要在任何其他HTML标签上注册事件,它违反了HTML与JavaScript代码相分离的原则,倘若事件函数可能还没加载进来就点击了事件对象元素,这会导致错误;

DiscreteEvent 离散事件. 例如blur、focus、 click、 submit、 touchStart. 这些事件都是离散触发的。

2.作为DOM元素的属性来注册,此时的事件处理程序属性的名字需加‘on'前缀,这种方式兼容所有浏览器,唯一的缺点是只能注册一个事件处理函数,如果定义两次onclick属性,后一次定义会覆盖前一次;如:window.onload = function(){...};

UserBlockingEvent 用户阻塞事件. 例如touchMove、mouseMove、scroll、drag、dragOver等等。这些事件会'阻塞'用户的交互。

3.除了IE8及之前版本外的所有浏览器中,DOM的事件操作,都定义在EventTarget接口。Element节点、document节点和window对象,都部署了这个接口。此外,XMLHttpRequest、AudioNode、AudioContext等浏览器内置对象,也部署了这个接口。该接口有三个方法,addEventListener和removeEventListener用于绑定和移除监听函数,dispatchEvent用于触发事件;addEventListener(type,listener,boolean)方法来注册listener,第三个参数设置事件的传播方式,通常使用默认值false,表示监听函数只在冒泡阶段被触发,当设为true时,表示监听函数在捕获阶段触发;可以为同一对象上的同一类型事件注册任意多个listener,所有listener会按照注册顺序触发(注册重复的listener将会被浏览器忽略);如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数,如elm.addEventListener{listen;当注册的listener是一个对函数的引用变量,就可以用removeEventLestener(type,listener,boolean)在事件目标上删除该listener,对同一监听事件的冒泡事件和捕获事件需要分别删除,两者互不干扰;

ContinuousEvent 连续事件。例如load、error、loadStart、abort、animationEnd. 这个优先级最高,也就是说它们应该是立即同步执行的,这就是Continuous的意义,是持续地执行,不能被打断。

var div = document.getElementById;var listener = function  { /* do something here */ }; div.addEventListener('click', listener, false); div.removeEventListener('click', listener, false);

源码里有一个怒长的表,对所有常用事件进行归类

dispatchEvent方法在当前节点上手动触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),就返回false,否则为true,参数是一个Event对象的实例,该参数不能为空,且必须是一个有效的事件对象,否则报错;btn.addEventListener('click', listener, false);var e = new Event;btn.dispatchEvent; //在btn上立即触发click事件,将立即调用listener

var eventTuples = [// Discrete events[TOP_BLUR, 'blur', DiscreteEvent], [TOP_CANCEL, 'cancel', DiscreteEvent], [TOP_CLICK, 'click', DiscreteEvent], [TOP_CLOSE, 'close', DiscreteEvent], [TOP_CONTEXT_MENU, 'contextMenu', DiscreteEvent], [TOP_COPY, 'copy', DiscreteEvent], [TOP_CUT, 'cut', DiscreteEvent], [TOP_AUX_CLICK, 'auxClick', DiscreteEvent], [TOP_DOUBLE_CLICK, 'doubleClick', DiscreteEvent], [TOP_DRAG_END, 'dragEnd', DiscreteEvent], [TOP_DRAG_START, 'dragStart', DiscreteEvent], [TOP_DROP, 'drop', DiscreteEvent], [TOP_FOCUS, 'focus', DiscreteEvent], [TOP_INPUT, 'input', DiscreteEvent], [TOP_INVALID, 'invalid', DiscreteEvent], [TOP_KEY_DOWN, 'keyDown', DiscreteEvent], [TOP_KEY_PRESS, 'keyPress', DiscreteEvent], [TOP_KEY_UP, 'keyUp', DiscreteEvent], [TOP_MOUSE_DOWN, 'mouseDown', DiscreteEvent], [TOP_MOUSE_UP, 'mouseUp', DiscreteEvent], [TOP_PASTE, 'paste', DiscreteEvent], [TOP_PAUSE, 'pause', DiscreteEvent], [TOP_PLAY, 'play', DiscreteEvent], [TOP_POINTER_CANCEL, 'pointerCancel', DiscreteEvent], [TOP_POINTER_DOWN, 'pointerDown', DiscreteEvent], [TOP_POINTER_UP, 'pointerUp', DiscreteEvent], [TOP_RATE_CHANGE, 'rateChange', DiscreteEvent], [TOP_RESET, 'reset', DiscreteEvent], [TOP_SEEKED, 'seeked', DiscreteEvent], [TOP_SUBMIT, 'submit', DiscreteEvent], [TOP_TOUCH_CANCEL, 'touchCancel', DiscreteEvent], [TOP_TOUCH_END, 'touchEnd', DiscreteEvent], [TOP_TOUCH_START, 'touchStart', DiscreteEvent], [TOP_VOLUME_CHANGE, 'volumeChange', DiscreteEvent], // User-blocking events[TOP_DRAG, 'drag', UserBlockingEvent], [TOP_DRAG_ENTER, 'dragEnter', UserBlockingEvent], [TOP_DRAG_EXIT, 'dragExit', UserBlockingEvent], [TOP_DRAG_LEAVE, 'dragLeave', UserBlockingEvent], [TOP_DRAG_OVER, 'dragOver', UserBlockingEvent], [TOP_MOUSE_MOVE, 'mouseMove', UserBlockingEvent], [TOP_MOUSE_OUT, 'mouseOut', UserBlockingEvent], [TOP_MOUSE_OVER, 'mouseOver', UserBlockingEvent], [TOP_POINTER_MOVE, 'pointerMove', UserBlockingEvent], [TOP_POINTER_OUT, 'pointerOut', UserBlockingEvent], [TOP_POINTER_OVER, 'pointerOver', UserBlockingEvent], [TOP_SCROLL, 'scroll', UserBlockingEvent], [TOP_TOGGLE, 'toggle', UserBlockingEvent], [TOP_TOUCH_MOVE, 'touchMove', UserBlockingEvent], [TOP_WHEEL, 'wheel', UserBlockingEvent], // Continuous events[TOP_ABORT, 'abort', ContinuousEvent], [TOP_ANIMATION_END, 'animationEnd', ContinuousEvent], [TOP_ANIMATION_ITERATION, 'animationIteration', ContinuousEvent], [TOP_ANIMATION_START, 'animationStart', ContinuousEvent], [TOP_CAN_PLAY, 'canPlay', ContinuousEvent], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough', ContinuousEvent], [TOP_DURATION_CHANGE, 'durationChange', ContinuousEvent], [TOP_EMPTIED, 'emptied', ContinuousEvent], [TOP_ENCRYPTED, 'encrypted', ContinuousEvent], [TOP_ENDED, 'ended', ContinuousEvent], [TOP_ERROR, 'error', ContinuousEvent], [TOP_GOT_POINTER_CAPTURE, 'gotPointerCapture', ContinuousEvent], [TOP_LOAD, 'load', ContinuousEvent], [TOP_LOADED_DATA, 'loadedData', ContinuousEvent], [TOP_LOADED_METADATA, 'loadedMetadata', ContinuousEvent], [TOP_LOAD_START, 'loadStart', ContinuousEvent], [TOP_LOST_POINTER_CAPTURE, 'lostPointerCapture', ContinuousEvent], [TOP_PLAYING, 'playing', ContinuousEvent], [TOP_PROGRESS, 'progress', ContinuousEvent], [TOP_SEEKING, 'seeking', ContinuousEvent], [TOP_STALLED, 'stalled', ContinuousEvent], [TOP_SUSPEND, 'suspend', ContinuousEvent], [TOP_TIME_UPDATE, 'timeUpdate', ContinuousEvent], [TOP_TRANSITION_END, 'transitionEnd', ContinuousEvent], [TOP_WAITING, 'waiting', ContinuousEvent]];

下面例子根据dispatchEvent方法的返回值,判断事件是否被取消了var canceled = !btn.dispatchEvent { console.log; }else { console.log; }}

addEventBubbleListener与addEventCaptureListener就是对DOM 的addEvenListener的简单封装。但要注意的是,这两个方法都是使用注入方式实现的。换言之,react-native中,由于打包的文件不一样,它对应的实现也不一样

4.IE8及之前版本仅支持attachEvent 和detachEvent,它们的用法和addEventListener的区别:a.参数只有两个;b.参数type必须加'on'前缀;c.它允许对同一监听事件进行重复注册,且都会被调用;d.使用attachEvent方法有个缺点,是this的值会变成 window 对象而不是触发事件的元素;调用顺序问题:1).通过设置对象属性或HTML属性注册的处理程序一直优先调用; 2).使用addEventListener 注册的处理程序按照它们的注册顺序调用; 3).旧版IE中使用attachEvent注册的处理程序可能按照任何顺序调用。返回值问题:

function addEventBubbleListener(element, eventType, listener) { element.addEventListener(eventType, listener, false); //by 司徒正美}function addEventCaptureListener(element, eventType, listener) { element.addEventListener(eventType, listener, true); //by 司徒正美}

1).事件处理程序的返回值只对通过属性注册的处理程序才有意义,通过设置对象属性或HTML属性注册 事件处理程序的返回值为false,就是告诉浏览器不要执行这个事件相关的默认操作。当浏览器要跳转到新页面时触发Window对象的onbeforeunload事件,若它的的返回值为字符串,则它将出现在询问确认对话框中;

然后我们终于有机会看dispatchDiscreteEvent,dispatchUserBlockingUpdate与dispatchEvent。

2).addEventListener注册事件处理程序若要取消浏览器的默认操作必须调用preventDefault()方法或设置事件对象的returnValue属性。this指向问题:

function dispatchDiscreteEvent(topLevelType, eventSystemFlags, nativeEvent) { flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp); discreteUpdates(dispatchEvent, topLevelType, eventSystemFlags, nativeEvent);} //by 司徒正美function dispatchUserBlockingUpdate(topLevelType, eventSystemFlags, nativeEvent) { runWithPriority(UserBlockingPriority, dispatchEvent.bind(null, topLevelType, eventSystemFlags, nativeEvent));}

1).addEventListener方法指定的监听函数,内部的this对象总是指向触发事件的那个节点;2).IE8及以前的attachEvent方法注册的事件处理函数的this指向全局对象;以下写法的this对象都指向Element节点。 element.onclick = print; element.addEventListener('click', print, false) element.onclick = function () {console.log;} 以下写法的this对象,都指向全局对象。 element.onclick = function }; element.setAttribute('onclick', 'doSomething; element.attachEvent('onclick',doSomething) //IE8内存问题:对如下代码,每个循环中都会创建一个新的匿名函数,占用的内存越来越多;由于没有保持到匿名函数的引用,它不可能被调用 removeEventListener;所以应当把第二参数listener保持为对处理事件函数的引用;

派发离散事件分两部分,第一个是flushDiscreteUpdatesIfNeeded,你跟踪进去是flushDiscreteUpdates,它会搞定之前积攒的DiscreteEvent与useEffect回调。第二个是discreteUpdates,它会为React 的调度叠加一个DiscreteEventContext 上下文,并执行runWithPriority,这时看来它与dispatchUserBlockingUpdate无异,只是做了一个前置处理。

 for(i=0 ; i通用的兼容旧版IE的工具函数:确保事件处理程序的this指向事件的目标对象的工具函数addEvent function addEvent{ if(target.addEventListener){ target.addEventListener; }else{ target.attachEvent{ //这里attachEvent注册的处理函数未绑定引用,所以无法用detachEvent删除 return func.call; } }
function discreteUpdates(fn, a, b, c) { var prevExecutionContext = executionContext; executionContext |= DiscreteEventContext; try { // Should this return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c)); } finally { executionContext = prevExecutionContext; if (executionContext === NoContext) { // Flush the immediate callbacks that were scheduled during this batch flushSyncCallbackQueue(); } }}

通用的事件处理程序(因为IE8及以前版本,作为事件目标的on-属性的处理程序需要window.event来获得事件对象,且触发事件的目标节点对象通过event.srcElement属性获得)

runWithPriority对调度器的影响主要有两个,一个是fiber节点的expirationTime属性,涉及到fiber对DOM的刷新,另一个是fiber节点上的回调的执行(setState, forceUpdate, hooks),它们会转交scheduleCallback方法处理。scheduleCallback相当于一个setTimeout,根据priorityLevel,延迟执行事件。scheduleCallback详看这里:​

function func{ var event = event||window.event; var target = event.target || event.srcElement; //......处理程序代码 }

priorityLevel能立即转换成callbackPriority,默认为NoPriority 90。然后运行过程中变成以下5种。

四、事件传播:是浏览器决定哪个对象触发其事件处理程序的过程。“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段==>处于目标阶段==>事件冒泡阶段。首先发生的是事件捕获阶段,为事件传播经过的所有节点截获事件提供了机会。然后是实际的目标接收事件。最后一个阶段是冒泡阶段。

ImmediatePriority 99IMMEDIATE_PRIORITY_TIMEOUT=-1;UserBlockingPriority 98USER_BLOCKING_PRIORITY=250NormalPriority 97NORMAL_PRIORITY_TIMEOUT=5000LowPriority 96LOW_PRIORITY_TIMEOUT=10000IdlePriority 85LOW_PRIORITY_TIMEOUT=10000

当容器元素及嵌套元素,即在捕获阶段又在冒泡阶段调用事件处理程序时:事件按DOM事件流的顺序执行事件处理程序,且当事件处于目标阶段时,事件调用顺序决定于绑定事件的书写顺序

每一个fiber都分配一个expirationTime属性(其实有多种expirationTime属性),它是大于当时的毫秒数。但调度器执行时,就计算出当前的毫秒数now, 然后now - fiber.expirationTime = 0,那么这fiber就可以更新了,其priorityLevel会改成ImmediatePriority。否则它只会创建一个effect记录用户的操作(如更新了某个属性啦,对它做删除啦)。

如果希望事件到某个节点为止,不再传播,有两种方式:

好了,我们知道runWithPriority重要性了,那么我们需要获取其他priorityLevel值。

1.使用事件对象的event.stopPropagation()方法来阻止当前监听函数的传播;

我们会发现flushControlled,deferredUpdates,flushSync会包含runWithPriority(NormalPriority, fn)逻辑。syncUpdates, flushSyncCallback则包含runWithPriority(ImmediatePriority, fn)的逻辑。**

2.使用事件对象的event.stopImmediatePropagation()方法来阻止当前事件在其事件对象上的所有监听函数的传播;

此外,SuspenseComponent也会影响到expirationTime的计算(默认分配为LOW_PRIORITY_EXPIRATION),最后通过 内部 inferPriorityFromExpirationTime的方法计算priorityLevel值。

事件的代理:由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件;

本文React版本为 16.10.2总结:

五、事件对象:事件发生以后,会生成一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。Event对象本身就是一个构造函数,可以用来生成新的实例。

多年之前,人们说到fiber,只是模糊地联想到Time slicing与Suspense这两个单词。而经过多次迭代,我们终于能揭开fiber的真面目。 React的时间切片,只是它更新的一种表现,实质上是由每个fiber的expirationTime所决定,而fiber的expirationTime又来自priorityLevel,priorityLevel则来自用户的UI操作,不同的事件,带来三种不同的priorityLevel。而悬停,则只为某个fiber带来第四种priorityLevel——LowPriority。用户代码出现问题,被catch住时,出现第五种priorityLevel——IdlePriority。

var ev = new Event("look", {"bubbles":true, "cancelable":false});document.dispatchEvent;

作者:司徒正美原文:

Event构造函数接受两个参数。第一个参数是字符串,表示事件的名称;第二个参数是一个对象,表示事件对象的配置。该参数可以有以下两个属性。bubbles:布尔值,可选,默认为false,表示事件对象是否冒泡。cancelable:布尔值,可选,默认为false,表示事件是否可以被取消。

Event对象的属性:1.与事件的阶段有关:bubbles: 只读属性,返回一个布尔值,表示当前事件是否会冒泡,可根据事件是否会冒泡来调用不同的函数。eventPhase:返回一个整数值,表示事件目前所处的状态<0,事件目前没有发生。<1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。该过程是从Window对象到Document节点,再到HTMLHtmlElement节点,直到目标节点的父节点为止。<2,事件到达目标节点,即target属性指向的那个节点。<3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。该过程是从父节点一直到Window对象。只有bubbles属性为true时,这个阶段才可能发生

2.与事件的默认行为有关:cancelable:返回一个布尔值,表示事件是否可以取消。如果要取消某个事件,需要在这个事件上面调用preventDefault方法defaultPrevented:返回一个布尔值,表示该事件是否调用过preventDefault方法。

3.与事件的目标节点有关:currentTarget:返回事件执行的监听函数所绑定的那个节点。target:返回触发事件的那个节点。在IE6—IE8之中,该属性的名字不是target,而是srcElement

4.与事件对象的其他信息相关:type:返回一个字符串,表示事件类型detail:返回一个数值,表示事件的某种信息。具体含义与事件类型有关,对于鼠标事件,表示鼠标按键在某个位置按下的次数,比如对于dblclick事件,detail属性的值总是2timeStamp:返回一个毫秒时间戳,表示事件发生的时间。从PerformanceTiming.navigationStart开始计算,即表示距离用户导航至该网页的时间。如果想将这个值转为Unix纪元时间戳,就要计算event.timeStamp performance.timing.navigationStartisTrusted:返回一个布尔值,表示该事件是否可以信任。用处不大,不同浏览器的支持不一样。

Event对象的方法:preventDefault():取消浏览器对当前事件的默认行为,该方法生效的前提是,事件的cancelable属性为true,如果为false,则调用该方法没有任何效果。stopPropagation():终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。注意:该方法不能阻止同一个 Document 节点上的其他事件句柄被调用,但是它可以阻止把事件分派到其他节点stopImmediatePropagation():阻止同一个事件的其他监听函数被调用,只要其中有一个监听函数调用了该方法,其他的监听函数就不会再执行了。

参考链接:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

本文由必发88官网发布,转载请注明来源:API及事件监听,Fiber的优先级调度机制与事件系统