Match-id-fda5629a5eba25fc511063c1cebf7752506caf2d

This commit is contained in:
* 2022-02-14 17:40:20 +08:00 committed by *
commit 5b6f0a11d0
42 changed files with 369 additions and 729 deletions

View File

@ -1,13 +1,13 @@
import { import {
asyncUpdates, createVNode, getFirstCustomDom, asyncUpdates, getFirstCustomDom,
syncUpdates, startUpdate, syncUpdates, startUpdate,
createTreeRootVNode,
} from '../renderer/Renderer'; } from '../renderer/Renderer';
import {createPortal} from '../renderer/components/CreatePortal'; import {createPortal} from '../renderer/components/CreatePortal';
import type {Container} from './DOMOperator'; import type {Container} from './DOMOperator';
import {isElement} from './utils/Common'; import {isElement} from './utils/Common';
import {listenDelegatedEvents} from '../event/EventBinding'; import {listenDelegatedEvents} from '../event/EventBinding';
import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils'; import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
import {TreeRoot} from '../renderer/vnode/VNodeTags';
import {Callback} from '../renderer/UpdateHandler'; import {Callback} from '../renderer/UpdateHandler';
function createRoot(children: any, container: Container, callback?: Callback) { function createRoot(children: any, container: Container, callback?: Callback) {
@ -19,7 +19,7 @@ function createRoot(children: any, container: Container, callback?: Callback) {
} }
// 调度器创建根节点并给容器dom赋vNode结构体 // 调度器创建根节点并给容器dom赋vNode结构体
const treeRoot = createVNode(TreeRoot, container); const treeRoot = createTreeRootVNode(container);
container._treeRoot = treeRoot; container._treeRoot = treeRoot;
// 根节点挂接全量事件 // 根节点挂接全量事件

View File

@ -19,7 +19,6 @@ const prefix = '_horizon';
const internalKeys = { const internalKeys = {
VNode: `${prefix}VNode`, VNode: `${prefix}VNode`,
props: `${prefix}Props`, props: `${prefix}Props`,
events: `${prefix}Events`,
nonDelegatedEvents: `${prefix}NonDelegatedEvents`, nonDelegatedEvents: `${prefix}NonDelegatedEvents`,
}; };
@ -58,6 +57,7 @@ export function getNearestVNode(dom: Node): null | VNode {
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例 if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
return vNode; return vNode;
} }
// 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过 // 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过
let parentDom = dom.parentNode; let parentDom = dom.parentNode;
let nearVNode = null; let nearVNode = null;
@ -82,20 +82,11 @@ export function updateVNodeProps(dom: Element | Text, props: Props): void {
dom[internalKeys.props] = props; dom[internalKeys.props] = props;
} }
export function getEventListeners(dom: EventTarget): Set<string> { export function getNonDelegatedListenerMap(dom: Element | Text): Map<string, EventListener> {
let elementListeners = dom[internalKeys.events]; let eventsMap = dom[internalKeys.nonDelegatedEvents];
if (!elementListeners) {
elementListeners = new Set();
dom[internalKeys.events] = elementListeners;
}
return elementListeners;
}
export function getEventToListenerMap(target: EventTarget): Map<string, EventListener> {
let eventsMap = target[internalKeys.nonDelegatedEvents];
if (!eventsMap) { if (!eventsMap) {
eventsMap = new Map(); eventsMap = new Map();
target[internalKeys.nonDelegatedEvents] = eventsMap; dom[internalKeys.nonDelegatedEvents] = eventsMap;
} }
return eventsMap; return eventsMap;
} }

View File

@ -226,15 +226,6 @@ export function unHideDom(tag: string, dom: Element | Text, props: Props) {
} }
} }
export function clearContainer(container: Container): void {
if (isElement(container)) {
container.textContent = '';
}
if (isDocument(container) && container.body != null) {
container.body.textContent = '';
}
}
export function prePortal(portal: Element): void { export function prePortal(portal: Element): void {
listenDelegatedEvents(portal); listenDelegatedEvents(portal);
} }

View File

@ -14,8 +14,11 @@ function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
} else if (propName === 'dangerouslySetInnerHTML') { } else if (propName === 'dangerouslySetInnerHTML') {
dom.innerHTML = propVal.__html; dom.innerHTML = propVal.__html;
} else if (propName === 'children') { // 只处理纯文本子节点其他children在VNode树中处理 } else if (propName === 'children') { // 只处理纯文本子节点其他children在VNode树中处理
if (typeof propVal === 'string' || typeof propVal === 'number') { const type = typeof propVal;
dom.textContent = String(propVal); if (type === 'string') {
dom.textContent = propVal;
} else if (type === 'number') {
dom.textContent = propVal + ''; // 这种数字转字符串的方式效率最高
} }
} else if (isEventProp(propName)) { } else if (isEventProp(propName)) {
// 事件监听属性处理 // 事件监听属性处理

View File

@ -5,7 +5,7 @@ import {Props} from '../DOMOperator';
* input textarea * input textarea
* @param doc document * @param doc document
*/ */
export function getFocusedDom(doc?: Document): HorizonDom | void { export function getFocusedDom(doc?: Document): HorizonDom | null {
let currentDocument; let currentDocument;
if (doc) { if (doc) {
currentDocument = doc; currentDocument = doc;

View File

@ -5,6 +5,13 @@ export const NSS = {
svg: 'http://www.w3.org/2000/svg', svg: 'http://www.w3.org/2000/svg',
}; };
const div = document.createElement('div');
const span = document.createElement('span');
const tr = document.createElement('tr');
const td = document.createElement('td');
const a = document.createElement('a');
const p = document.createElement('p');
// 创建DOM元素 // 创建DOM元素
export function createDom( export function createDom(
tagName: string, tagName: string,
@ -16,6 +23,18 @@ export function createDom(
if (ns !== NSS.html) { if (ns !== NSS.html) {
dom = document.createElementNS(ns, tagName); dom = document.createElementNS(ns, tagName);
} else if (tagName === 'div') {
dom = div.cloneNode(false);
} else if (tagName === 'span') {
dom = span.cloneNode(false);
} else if (tagName === 'tr') {
dom = tr.cloneNode(false);
} else if (tagName === 'td') {
dom = td.cloneNode(false);
} else if (tagName === 'a') {
dom = a.cloneNode(false);
} else if (tagName === 'p') {
dom = p.cloneNode(false);
} else { } else {
dom = document.createElement(tagName); dom = document.createElement(tagName);
} }

View File

@ -21,6 +21,7 @@ import {
getTextareaPropsWithoutValue, getTextareaPropsWithoutValue,
updateTextareaValue, updateTextareaValue,
} from './TextareaValueHandler'; } from './TextareaValueHandler';
import {getDomTag} from "../utils/Common";
// 获取元素除了被代理的值以外的属性 // 获取元素除了被代理的值以外的属性
function getPropsWithoutValue(type: string, dom: HorizonDom, properties: IProperty) { function getPropsWithoutValue(type: string, dom: HorizonDom, properties: IProperty) {
@ -72,7 +73,8 @@ function updateValue(type: string, dom: HorizonDom, properties: IProperty) {
} }
} }
function resetValue(dom: HorizonDom, type: string, properties: IProperty) { function resetValue(dom: HorizonDom, properties: IProperty) {
const type = getDomTag(dom);
switch (type) { switch (type) {
case 'input': case 'input':
resetInputValue(<HTMLInputElement>dom, properties); resetInputValue(<HTMLInputElement>dom, properties);

View File

@ -1,15 +1,13 @@
import {getVNodeProps} from '../dom/DOMInternalKeys'; import {getVNodeProps} from '../dom/DOMInternalKeys';
import {resetValue} from '../dom/valueHandler'; import {resetValue} from '../dom/valueHandler';
import {getDomTag} from '../dom/utils/Common';
let updateList = null; let updateList: Array<any> | null = null;
// 受控组件值重新赋值 // 受控组件值重新赋值
function updateValue(target: Element) { function updateValue(target: Element) {
const props = getVNodeProps(target); const props = getVNodeProps(target);
if (props) { if (props) {
const type = getDomTag(target); resetValue(target, props);
resetValue(target, type, props);
} }
} }

View File

@ -1,50 +1,60 @@
/** /**
* *
*/ */
import {allDelegatedNativeEvents} from './EventCollection'; import {allDelegatedNativeEvents} from './EventCollection';
import {isDocument} from '../dom/utils/Common'; import {isDocument} from '../dom/utils/Common';
import { import {
getEventListeners, getNearestVNode,
getEventToListenerMap, getNonDelegatedListenerMap,
} from '../dom/DOMInternalKeys'; } from '../dom/DOMInternalKeys';
import {createCustomEventListener} from './WrapperListener';
import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
import {runDiscreteUpdates} from '../renderer/TreeBuilder';
import {isMounted} from '../renderer/vnode/VNodeUtils';
import {SuspenseComponent} from '../renderer/vnode/VNodeTags';
import {handleEventMain} from './HorizonEventMain';
const listeningMarker = const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
'_horizonListening' +
Math.random()
.toString(36)
.slice(4);
// 获取节点上已经委托事件名称 // 触发委托事件
function getListenerSetKey(nativeEvtName: string, isCapture: boolean): string { function triggerDelegatedEvent(
const sufix = isCapture ? 'capture' : 'bubble'; nativeEvtName: string,
return `${nativeEvtName}__${sufix}`; isCapture: boolean,
targetDom: EventTarget,
nativeEvent, // 事件对象event
) {
// 执行之前的调度事件
runDiscreteUpdates();
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
let targetVNode = getNearestVNode(nativeEventTarget);
if (targetVNode !== null) {
if (isMounted(targetVNode)) {
if (targetVNode.tag === SuspenseComponent) {
targetVNode = null;
}
} else {
// vNode已销毁
targetVNode = null;
}
}
handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom);
} }
// 监听委托事件
function listenToNativeEvent( function listenToNativeEvent(
nativeEvtName: string, nativeEvtName: string,
delegatedElement: Element, delegatedElement: Element,
isCapture: boolean, isCapture: boolean,
): void { ): void {
let target: Element | Document = delegatedElement; let dom: Element | Document = delegatedElement;
// document层次可能触发selectionchange事件为了捕获这类事件selectionchange事件绑定在document节点上 // document层次可能触发selectionchange事件为了捕获这类事件selectionchange事件绑定在document节点上
if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) { if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) {
target = delegatedElement.ownerDocument; dom = delegatedElement.ownerDocument;
} }
const listenerSet = getEventListeners(target); const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, dom);
const listenerSetKey = getListenerSetKey(nativeEvtName, isCapture); dom.addEventListener(nativeEvtName, listener, isCapture);
if (!listenerSet.has(listenerSetKey)) {
const listener = createCustomEventListener(
target,
nativeEvtName,
isCapture,
);
target.addEventListener(nativeEvtName, listener, !!isCapture);
listenerSet.add(listenerSetKey);
}
} }
// 监听所有委托事件 // 监听所有委托事件
@ -54,11 +64,12 @@ export function listenDelegatedEvents(dom: Element) {
return; return;
} }
dom[listeningMarker] = true; dom[listeningMarker] = true;
allDelegatedNativeEvents.forEach((eventName: string) => {
allDelegatedNativeEvents.forEach((nativeEvtName: string) => {
// 委托冒泡事件 // 委托冒泡事件
listenToNativeEvent(eventName, dom, false); listenToNativeEvent(nativeEvtName, dom, false);
// 委托捕获事件 // 委托捕获事件
listenToNativeEvent(eventName, dom, true); listenToNativeEvent(nativeEvtName, dom, true);
}); });
} }
@ -77,7 +88,7 @@ function getNativeEvtName(horizonEventName, capture) {
} }
// 是否捕获事件 // 是否捕获事件
function getIsCapture(horizonEventName) { function isCaptureEvent(horizonEventName) {
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
return false; return false;
} }
@ -86,7 +97,7 @@ function getIsCapture(horizonEventName) {
// 封装监听函数 // 封装监听函数
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
return (event) => { return event => {
const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement); const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement);
listener(customEvent); listener(customEvent);
}; };
@ -98,23 +109,24 @@ export function listenNonDelegatedEvent(
domElement: Element, domElement: Element,
listener, listener,
): void { ): void {
const isCapture = getIsCapture(horizonEventName); const isCapture = isCaptureEvent(horizonEventName);
const nativeEvtName = getNativeEvtName(horizonEventName, isCapture); const nativeEvtName = getNativeEvtName(horizonEventName, isCapture);
// 先判断是否存在老的监听事件,若存在则移除 // 先判断是否存在老的监听事件,若存在则移除
const eventToListenerMap = getEventToListenerMap(domElement); const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement);
if (eventToListenerMap.get(horizonEventName)) { const currentListener = nonDelegatedListenerMap.get(horizonEventName);
domElement.removeEventListener(nativeEvtName, eventToListenerMap.get(horizonEventName)); if (currentListener) {
domElement.removeEventListener(nativeEvtName, currentListener);
nonDelegatedListenerMap.delete(horizonEventName);
} }
if (typeof listener !== 'function') { if (typeof listener !== 'function') {
eventToListenerMap.delete(nativeEvtName);
return; return;
} }
// 为了和委托事件对外行为一致将事件对象封装成CustomBaseEvent // 为了和委托事件对外行为一致将事件对象封装成CustomBaseEvent
const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener); const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener);
// 添加新的监听 // 添加新的监听
eventToListenerMap.set(horizonEventName, wrapperListener); nonDelegatedListenerMap.set(horizonEventName, wrapperListener);
domElement.addEventListener(nativeEvtName, wrapperListener, isCapture); domElement.addEventListener(nativeEvtName, wrapperListener, isCapture);
} }

View File

@ -8,6 +8,7 @@ export const allDelegatedNativeEvents = new Set();
horizonEventToNativeMap.forEach((dependencies, horizonEvent) => { horizonEventToNativeMap.forEach((dependencies, horizonEvent) => {
allDelegatedHorizonEvents.set(horizonEvent, dependencies); allDelegatedHorizonEvents.set(horizonEvent, dependencies);
allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies); allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies);
dependencies.forEach(d => { dependencies.forEach(d => {
allDelegatedNativeEvents.add(d); allDelegatedNativeEvents.add(d);
}); });

View File

@ -1,24 +0,0 @@
// 处理事件的错误
let hasError = false;
let caughtError = null;
// 执行事件监听器,并且捕捉第一个错误,事件执行完成后抛出第一个错误
export function runListenerAndCatchFirstError(listener, event) {
try {
listener(event);
} catch (error) {
if (!hasError) {
hasError = true;
caughtError = error;
}
}
}
export function throwCaughtEventError() {
if (hasError) {
const err = caughtError;
caughtError = null;
hasError = false;
throw err;
}
}

View File

@ -1,4 +1,4 @@
import type { AnyNativeEvent, ProcessingListenerList } from './types'; import type { AnyNativeEvent } from './Types';
import type { VNode } from '../renderer/Types'; import type { VNode } from '../renderer/Types';
import { import {
@ -7,24 +7,19 @@ import {
EVENT_TYPE_BUBBLE, EVENT_TYPE_BUBBLE,
EVENT_TYPE_CAPTURE, EVENT_TYPE_CAPTURE,
} from './const'; } from './const';
import {
throwCaughtEventError,
runListenerAndCatchFirstError,
} from './EventError';
import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler'; import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler';
import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler'; import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler';
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler'; import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler'; import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
import { import {
getCustomEventNameWithOn, addOnPrefix,
uniqueCharCode,
getEventTarget
} from './utils'; } from './utils';
import { createCommonCustomEvent } from './customEvents/EventFactory'; import { createCustomEvent } from './customEvents/EventFactory';
import { getListenersFromTree } from './ListenerGetter'; import { getListenersFromTree } from './ListenerGetter';
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater'; import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
import { getExactNode } from '../renderer/vnode/VNodeUtils'; import { getExactNode } from '../renderer/vnode/VNodeUtils';
import {ListenerUnitList} from './Types';
// 获取事件触发的普通事件监听方法队列 // 获取事件触发的普通事件监听方法队列
function getCommonListeners( function getCommonListeners(
@ -33,14 +28,9 @@ function getCommonListeners(
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
target: null | EventTarget, target: null | EventTarget,
isCapture: boolean, isCapture: boolean,
): ProcessingListenerList { ): ListenerUnitList {
const customEventName = getCustomEventNameWithOn(CommonEventToHorizonMap[nativeEvtName]); const horizonEvtName = addOnPrefix(CommonEventToHorizonMap[nativeEvtName]);
if (!customEventName) { if (!horizonEvtName) {
return [];
}
// 火狐浏览器兼容。火狐浏览器下功能键将触发keypress事件 火狐下keypress的charcode有值keycode为0
if (nativeEvtName === 'keypress' && uniqueCharCode(nativeEvent) === 0) {
return []; return [];
} }
@ -52,49 +42,42 @@ function getCommonListeners(
if (nativeEvtName === 'focusin') { if (nativeEvtName === 'focusin') {
nativeEvtName = 'focus'; nativeEvtName = 'focus';
} }
if (nativeEvtName === 'focusout') { if (nativeEvtName === 'focusout') {
nativeEvtName = 'blur'; nativeEvtName = 'blur';
} }
const customEvent = createCommonCustomEvent(customEventName, nativeEvtName, nativeEvent, null, target); const horizonEvent = createCustomEvent(horizonEvtName, nativeEvtName, nativeEvent, null, target);
return getListenersFromTree( return getListenersFromTree(
vNode, vNode,
customEventName, horizonEvtName,
customEvent, horizonEvent,
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE, isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
); );
} }
// 按顺序执行事件队列 // 按顺序执行事件队列
export function processListeners( function processListeners(listenerList: ListenerUnitList): void {
processingEventsList: ProcessingListenerList listenerList.forEach(eventUnit => {
): void { const { currentTarget, listener, event } = eventUnit;
processingEventsList.forEach(eventUnitList => { if (event.isPropagationStopped()) {
let lastVNode; return;
eventUnitList.forEach(eventUnit => { }
const { vNode, currentTarget, listener, event } = eventUnit; event.currentTarget = currentTarget;
if (vNode !== lastVNode && event.isPropagationStopped()) { listener(event);
return; event.currentTarget = null;
}
event.currentTarget = currentTarget;
runListenerAndCatchFirstError(listener, event);
event.currentTarget = null;
lastVNode = vNode;
});
}); });
// 执行所有事件后重新throw遇到的第一个错误
throwCaughtEventError();
} }
function getProcessListenersFacade( function getProcessListenersFacade(
nativeEvtName: string, nativeEvtName: string,
vNode: VNode, vNode: VNode | null,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
target, target,
isCapture: boolean isCapture: boolean
): ProcessingListenerList { ): ListenerUnitList {
// 触发普通委托事件 // 触发普通委托事件
let processingListenerList: ProcessingListenerList = getCommonListeners( let listenerList: ListenerUnitList = getCommonListeners(
nativeEvtName, nativeEvtName,
vNode, vNode,
nativeEvent, nativeEvent,
@ -105,7 +88,7 @@ function getProcessListenersFacade(
// 触发特殊handler委托事件 // 触发特殊handler委托事件
if (!isCapture) { if (!isCapture) {
if (horizonEventToNativeMap.get('onChange').includes(nativeEvtName)) { if (horizonEventToNativeMap.get('onChange').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getChangeListeners( listenerList = listenerList.concat(getChangeListeners(
nativeEvtName, nativeEvtName,
nativeEvent, nativeEvent,
vNode, vNode,
@ -114,7 +97,7 @@ function getProcessListenersFacade(
} }
if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) { if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getSelectionListeners( listenerList = listenerList.concat(getSelectionListeners(
nativeEvtName, nativeEvtName,
nativeEvent, nativeEvent,
vNode, vNode,
@ -125,7 +108,7 @@ function getProcessListenersFacade(
if (nativeEvtName === 'compositionend' || if (nativeEvtName === 'compositionend' ||
nativeEvtName === 'compositionstart' || nativeEvtName === 'compositionstart' ||
nativeEvtName === 'compositionupdate') { nativeEvtName === 'compositionupdate') {
processingListenerList = processingListenerList.concat(getCompositionListeners( listenerList = listenerList.concat(getCompositionListeners(
nativeEvtName, nativeEvtName,
nativeEvent, nativeEvent,
vNode, vNode,
@ -134,7 +117,7 @@ function getProcessListenersFacade(
} }
if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) { if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getBeforeInputListeners( listenerList = listenerList.concat(getBeforeInputListeners(
nativeEvtName, nativeEvtName,
nativeEvent, nativeEvent,
vNode, vNode,
@ -142,7 +125,7 @@ function getProcessListenersFacade(
)); ));
} }
} }
return processingListenerList; return listenerList;
} }
// 触发可以被执行的horizon事件监听 // 触发可以被执行的horizon事件监听
@ -150,55 +133,47 @@ function triggerHorizonEvents(
nativeEvtName: string, nativeEvtName: string,
isCapture: boolean, isCapture: boolean,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
vNode: null | VNode, vNode: VNode | null,
): void { ): void {
const nativeEventTarget = getEventTarget(nativeEvent); const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
const processingListenerList = getProcessListenersFacade(
nativeEvtName, // 获取委托事件队列
vNode, const listenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
nativeEvent,
nativeEventTarget,
isCapture);
// 处理触发的事件队列 // 处理触发的事件队列
processListeners(processingListenerList); processListeners(listenerList);
} }
// 其他事件正在执行中标记 // 其他事件正在执行中标记
let isInEventsExecution = false; let isInEventsExecution = false;
// 处理委托事件入口
export function handleEventMain( export function handleEventMain(
nativeEvtName: string, nativeEvtName: string,
isCapture: boolean, isCapture: boolean,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
target: EventTarget, targetContainer: EventTarget,
): void { ): void {
let rootVNode = vNode; let startVNode = vNode;
if (vNode !== null) { if (startVNode !== null) {
rootVNode = getExactNode(vNode, target); startVNode = getExactNode(startVNode, targetContainer);
if (!rootVNode) { if (!startVNode) {
return; return;
} }
} }
// 有事件正在执行,同步执行事件 // 有事件正在执行,同步执行事件
if (isInEventsExecution) { if (isInEventsExecution) {
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode); triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode);
return; return;
} }
// 没有事件在执行,经过调度再执行事件 // 没有事件在执行,经过调度再执行事件
isInEventsExecution = true; isInEventsExecution = true;
try { try {
asyncUpdates(() => asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode));
triggerHorizonEvents(
nativeEvtName,
isCapture,
nativeEvent,
rootVNode,
));
} finally { } finally {
isInEventsExecution = false; isInEventsExecution = false;
if (shouldUpdateValue()) { if (shouldUpdateValue()) {

View File

@ -1,73 +1,20 @@
import {VNode} from '../renderer/Types'; import {VNode} from '../renderer/Types';
import {DomComponent} from '../renderer/vnode/VNodeTags'; import {DomComponent} from '../renderer/vnode/VNodeTags';
import {throwIfTrue} from '../renderer/utils/throwIfTrue';
import type {Props} from '../dom/DOMOperator';
import {EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE} from './const'; import {EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE} from './const';
import {ProcessingListenerList, ListenerUnitList} from './Types'; import {ListenerUnitList} from './Types';
import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
// 返回是否应该阻止事件响应标记disabled组件不响应鼠标事件
function shouldPrevent(
name: string,
type: string,
props: Props,
): boolean {
const canPreventMouseEvents = [
'onClick',
'onClickCapture',
'onDoubleClick',
'onDoubleClickCapture',
'onMouseDown',
'onMouseDownCapture',
'onMouseMove',
'onMouseMoveCapture',
'onMouseUp',
'onMouseUpCapture',
'onMouseEnter',
];
const interActiveElements = ['button', 'input', 'select', 'textarea'];
if (canPreventMouseEvents.includes(name)) {
return !!(props.disabled && interActiveElements.includes(type));
}
return false;
}
// 从vnode属性中获取事件listener
function getListener(
vNode: VNode,
eventName: string,
): Function | null {
const realNode = vNode.realNode;
if (realNode === null) {
return null;
}
const props = vNode.props;
if (props === null) {
return null;
}
const listener = props[eventName];
if (shouldPrevent(eventName, vNode.type, props)) {
return null;
}
throwIfTrue(
listener && typeof listener !== 'function',
'`%s` listener should be a function.',
eventName
);
return listener;
}
// 获取监听事件 // 获取监听事件
export function getListenersFromTree( export function getListenersFromTree(
targetVNode: VNode | null, targetVNode: VNode | null,
name: string | null, horizonEvtName: string | null,
horizonEvent: CustomBaseEvent, horizonEvent: CustomBaseEvent,
eventType: string, eventType: string,
): ProcessingListenerList { ): ListenerUnitList {
if (!name) { if (!horizonEvtName) {
return []; return [];
} }
const captureName = name + EVENT_TYPE_CAPTURE;
const listeners: ListenerUnitList = []; const listeners: ListenerUnitList = [];
let vNode = targetVNode; let vNode = targetVNode;
@ -77,7 +24,8 @@ export function getListenersFromTree(
const {realNode, tag} = vNode; const {realNode, tag} = vNode;
if (tag === DomComponent && realNode !== null) { if (tag === DomComponent && realNode !== null) {
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) { if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) {
const captureListener = getListener(vNode, captureName); const captureName = horizonEvtName + EVENT_TYPE_CAPTURE;
const captureListener = vNode.props[captureName];
if (captureListener) { if (captureListener) {
listeners.unshift({ listeners.unshift({
vNode, vNode,
@ -87,8 +35,9 @@ export function getListenersFromTree(
}); });
} }
} }
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) { if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) {
const bubbleListener = getListener(vNode, name); const bubbleListener = vNode.props[horizonEvtName];
if (bubbleListener) { if (bubbleListener) {
listeners.push({ listeners.push({
vNode, vNode,
@ -101,7 +50,8 @@ export function getListenersFromTree(
} }
vNode = vNode.parent; vNode = vNode.parent;
} }
return listeners.length > 0 ? [listeners]: [];
return listeners;
} }

View File

@ -12,5 +12,3 @@ export type ListenerUnit = {
}; };
export type ListenerUnitList = Array<ListenerUnit>; export type ListenerUnitList = Array<ListenerUnit>;
export type ProcessingListenerList = Array<ListenerUnitList>;

View File

@ -1,41 +0,0 @@
import {isMounted} from '../renderer/vnode/VNodeUtils';
import {SuspenseComponent} from '../renderer/vnode/VNodeTags';
import {getNearestVNode} from '../dom/DOMInternalKeys';
import {handleEventMain} from './HorizonEventMain';
import {runDiscreteUpdates} from '../renderer/Renderer';
import {getEventTarget} from './utils';
// 触发委托事件
function triggerDelegatedEvent(
nativeEvtName: string,
isCapture: boolean,
targetDom: EventTarget,
nativeEvent,
) {
// 执行之前的调度事件
runDiscreteUpdates();
const nativeEventTarget = getEventTarget(nativeEvent);
let targetVNode = getNearestVNode(nativeEventTarget);
if (targetVNode !== null) {
if (isMounted(targetVNode)) {
if (targetVNode.tag === SuspenseComponent) {
targetVNode = null;
}
} else {
// vnode已销毁
targetVNode = null;
}
}
handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom);
}
// 生成委托事件的监听方法
export function createCustomEventListener(
target: EventTarget,
nativeEvtName: string,
isCapture: boolean,
): EventListener {
return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target);
}

View File

@ -34,7 +34,7 @@ export const horizonEventToNativeMap = new Map([
['onCompositionStart', ['compositionstart']], ['onCompositionStart', ['compositionstart']],
['onCompositionUpdate', ['compositionupdate']], ['onCompositionUpdate', ['compositionupdate']],
['onBeforeInput', ['compositionend', 'keypress', 'textInput']], ['onBeforeInput', ['compositionend', 'keypress', 'textInput']],
['onChange', ['change', 'click', 'focusout', 'input',]], ['onChange', ['change', 'click', 'focusout', 'input']],
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', ['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']], 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],

View File

@ -6,37 +6,17 @@ import {VNode} from '../../renderer/Types';
// 从原生事件中复制属性到自定义事件中 // 从原生事件中复制属性到自定义事件中
function extendAttribute(target, source) { function extendAttribute(target, source) {
const attributes = [ let val;
// AnimationEvent let attr;
'animationName', 'elapsedTime', 'pseudoElement', for (attr in source) {
// CompositionEvent、InputEvent // 这两个方法需要override
'data', if (attr === 'preventDefault' || attr === 'stopPropagation') {
// DragEvent continue;
'dataTransfer', }
// FocusEvent
'relatedTarget',
// KeyboardEvent
'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey',
'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData',
// MouseEvent
'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY',
'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget',
// PointerEvent
'pointerId', 'width', 'height', 'pressure', 'tangentialPressure',
'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary',
// TouchEvent
'touches', 'targetTouches', 'changedTouches',
// TransitionEvent
'propertyName',
// UIEvent
'view', 'detail',
// WheelEvent
'deltaX', 'deltaY', 'deltaZ', 'deltaMode',
];
attributes.forEach(attr => { val = source[attr];
if (typeof source[attr] !== 'undefined') { if (val !== undefined) {
if (typeof source[attr] === 'function') { if (typeof val === 'function') {
target[attr] = function() { target[attr] = function() {
return source[attr].apply(source, arguments); return source[attr].apply(source, arguments);
}; };
@ -44,54 +24,71 @@ function extendAttribute(target, source) {
target[attr] = source[attr]; target[attr] = source[attr];
} }
} }
}) }
} }
// 兼容IE的event key
const uniqueKeyMap = new Map([
['Esc', 'Escape'],
['Spacebar', ' '],
['Left', 'ArrowLeft'],
['Up', 'ArrowUp'],
['Right', 'ArrowRight'],
['Down', 'ArrowDown'],
['Del', 'Delete'],
]);
export class CustomBaseEvent { export class CustomBaseEvent {
data: string;
defaultPrevented: boolean; defaultPrevented: boolean;
customEventName: string;
targetVNode: VNode;
type: string;
nativeEvent: any;
target: EventTarget; target: EventTarget;
timeStamp: number;
isDefaultPrevented: () => boolean; isDefaultPrevented: () => boolean;
isPropagationStopped: () => boolean; isPropagationStopped: () => boolean;
currentTarget: EventTarget; currentTarget: EventTarget;
relatedTarget: EventTarget;
// custom事件自定义属性
customEventName: string;
targetVNode: VNode;
type: string;
timeStamp: number;
nativeEvent: any;
// 键盘事件属性
key: string;
charCode: number;
keyCode: number;
which: number;
constructor( constructor(
customEvtName: string | null, customEvtName: string | null,
nativeEvtName: string, nativeEvtName: string,
nativeEvt: { [propName: string]: any }, nativeEvt: { [propName: string]: any },
vNode: VNode, vNode: VNode | null,
target: null | EventTarget target: null | EventTarget
) { ) {
// 复制原生属性到自定义事件 // 复制原生属性到自定义事件
extendAttribute(this, nativeEvt); extendAttribute(this, nativeEvt);
const defaultPrevented = nativeEvt.defaultPrevented != null ?
nativeEvt.defaultPrevented :
nativeEvt.returnValue === false;
this.defaultPrevented = defaultPrevented;
this.preventDefault = this.preventDefault.bind(this);
this.stopPropagation = this.stopPropagation.bind(this);
this.isDefaultPrevented = () => defaultPrevented;
this.isPropagationStopped = () => false;
this.relatedTarget = nativeEvt.relatedTarget;
this.target = target;
// 键盘事件属性
this.key = uniqueKeyMap.get(nativeEvt.key) || nativeEvt.key;
// custom事件自定义属性 // custom事件自定义属性
this.customEventName = customEvtName; this.customEventName = customEvtName;
this.targetVNode = vNode; this.targetVNode = vNode;
this.type = nativeEvtName; this.type = nativeEvtName;
this.nativeEvent = nativeEvt; this.nativeEvent = nativeEvt;
this.target = target;
this.timeStamp = nativeEvt.timeStamp || Date.now();
const defaultPrevented = nativeEvt.defaultPrevented != null ?
nativeEvt.defaultPrevented :
nativeEvt.returnValue === false;
this.defaultPrevented = defaultPrevented;
this.preventDefault = this.preventDefault.bind(this);
this.stopPropagation = this.stopPropagation.bind(this);
this.isDefaultPrevented = () => defaultPrevented;
this.isPropagationStopped = () => false;
}
// 兼容性方法
persist() {
} }
// 阻止默认行为 // 阻止默认行为

View File

@ -1,78 +0,0 @@
/**
*
*/
import type {VNode} from '../../renderer/Types';
import {uniqueCharCode} from '../utils';
import {CustomBaseEvent} from './CustomBaseEvent';
import {CHAR_CODE_ENTER} from '../const';
const uniqueKeyMap = new Map([
['Esc', 'Escape'],
['Spacebar', ' '],
['Left', 'ArrowLeft'],
['Up', 'ArrowUp'],
['Right', 'ArrowRight'],
['Down', 'ArrowDown'],
['Del', 'Delete'],
]);
const charCodeToKeyMap = new Map([
[8, 'Backspace'],
[9, 'Tab'],
[13, 'Enter'],
[16, 'Shift'],
[17, 'Control'],
[18, 'Alt'],
[19, 'Pause'],
[27, 'Escape'],
[32, ' '],
[33, 'PageUp'],
[34, 'PageDown'],
[35, 'End'],
[36, 'Home'],
[37, 'ArrowLeft'],
[38, 'ArrowUp'],
[39, 'ArrowRight'],
[40, 'ArrowDown'],
[46, 'Delete']
]);
function getKey(event) {
if (event.key) {
return uniqueKeyMap.get(event.key) || event.key;
}
if (event.type === 'keypress') {
const charCode = uniqueCharCode(event);
return charCode === CHAR_CODE_ENTER ? 'Enter' : String.fromCharCode(charCode);
}
if (event.type === 'keydown' || event.type === 'keyup') {
return charCodeToKeyMap.get(event.keyCode);
}
return '';
}
export class CustomKeyboardEvent extends CustomBaseEvent {
key: string;
charCode: number;
keyCode: number;
which: number;
constructor(
customEvtName: string | null,
nativeEvtName: string,
nativeEvt: { [propName: string]: any },
vNode: VNode,
target: null | EventTarget
) {
super(customEvtName, nativeEvtName, nativeEvt, vNode, target);
this.key = getKey(nativeEvt);
this.charCode = nativeEvtName === 'keypress' ? uniqueCharCode(nativeEvt) : 0;
this.keyCode = (nativeEvtName === 'keydown' || nativeEvtName === 'keyup') ? nativeEvt.keyCode : 0;
this.which = this.charCode || this.keyCode;
}
}

View File

@ -1,26 +0,0 @@
import type {VNode} from '../../renderer/Types';
import {CustomBaseEvent} from './CustomBaseEvent';
export class CustomMouseEvent extends CustomBaseEvent {
relatedTarget: EventTarget;
constructor(
customEvtName: string | null,
nativeEvtName: string,
nativeEvt: { [propName: string]: any },
vNode: VNode,
target: null | EventTarget
) {
super(customEvtName, nativeEvtName, nativeEvt, vNode, target);
let relatedTarget = nativeEvt.relatedTarget;
if (relatedTarget === undefined) {
if (nativeEvt.fromElement === nativeEvt.srcElement) {
relatedTarget = nativeEvt.toElement;
} else {
relatedTarget = nativeEvt.fromElement;
}
}
this.relatedTarget = relatedTarget;
}
}

View File

@ -1,42 +1,8 @@
import {CustomKeyboardEvent} from './CustomKeyboardEvent';
import {CustomMouseEvent} from './CustomMouseEvent';
import {CustomBaseEvent} from './CustomBaseEvent'; import {CustomBaseEvent} from './CustomBaseEvent';
const CommonEventToCustom = {
keypress: CustomKeyboardEvent,
keydown: CustomKeyboardEvent,
keyup: CustomKeyboardEvent,
click: CustomMouseEvent,
dblclick: CustomMouseEvent,
mousedown: CustomMouseEvent,
mousemove: CustomMouseEvent,
mouseup: CustomMouseEvent,
mouseout: CustomMouseEvent,
mouseover: CustomMouseEvent,
contextmenu: CustomMouseEvent,
pointercancel: CustomMouseEvent,
pointerdown: CustomMouseEvent,
pointermove: CustomMouseEvent,
pointerout: CustomMouseEvent,
pointerover: CustomMouseEvent,
pointerup: CustomMouseEvent,
}
// 创建普通自定义事件对象实例,和原生事件对应 // 创建普通自定义事件对象实例,和原生事件对应
export function createCommonCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) { export function createCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) {
const EventConstructor = CommonEventToCustom[nativeEvtName] || CustomBaseEvent; return new CustomBaseEvent(
return new EventConstructor(
customEventName,
nativeEvtName,
nativeEvent,
vNode,
currentTarget,
);
}
// 创建模拟事件实例对象,需要handler特殊处理
export function createHandlerCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) {
return new CustomMouseEvent(
customEventName, customEventName,
nativeEvtName, nativeEvtName,
nativeEvent, nativeEvent,

View File

@ -1,9 +1,10 @@
import type {VNode} from '../../renderer/Types'; import type {VNode} from '../../renderer/Types';
import type {AnyNativeEvent, ProcessingListenerList} from '../Types'; import type {AnyNativeEvent} from '../Types';
import {getListenersFromTree} from '../ListenerGetter'; import {getListenersFromTree} from '../ListenerGetter';
import {createHandlerCustomEvent} from '../customEvents/EventFactory'; import {createCustomEvent} from '../customEvents/EventFactory';
import {CHAR_CODE_SPACE, EVENT_TYPE_ALL} from '../const'; import {CHAR_CODE_SPACE, EVENT_TYPE_ALL} from '../const';
import {CustomBaseEvent} from '../customEvents/CustomBaseEvent'; import {CustomBaseEvent} from '../customEvents/CustomBaseEvent';
import {ListenerUnitList} from '../Types';
const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE); const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE);
function getInputCharsByNative( function getInputCharsByNative(
@ -28,14 +29,14 @@ export function getListeners(
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
target: null | EventTarget, target: null | EventTarget,
): ProcessingListenerList { ): ListenerUnitList {
const chars = getInputCharsByNative(nativeEvtName, nativeEvent); const chars = getInputCharsByNative(nativeEvtName, nativeEvent);
// 无字符将要输入,无需处理 // 无字符将要输入,无需处理
if (!chars) { if (!chars) {
return []; return [];
} }
const event: CustomBaseEvent = createHandlerCustomEvent( const event: CustomBaseEvent = createCustomEvent(
'onBeforeInput', 'onBeforeInput',
'beforeinput', 'beforeinput',
nativeEvent, nativeEvent,

View File

@ -1,10 +1,10 @@
import {createHandlerCustomEvent} from '../customEvents/EventFactory'; import {createCustomEvent} from '../customEvents/EventFactory';
import {getDom} from '../../dom/DOMInternalKeys'; import {getDom} from '../../dom/DOMInternalKeys';
import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler'; import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler';
import {addValueUpdateList} from '../ControlledValueUpdater'; import {addValueUpdateList} from '../ControlledValueUpdater';
import {isTextInputElement} from '../utils'; import {isTextInputElement} from '../utils';
import {EVENT_TYPE_ALL} from '../const'; import {EVENT_TYPE_ALL} from '../const';
import {AnyNativeEvent, ProcessingListenerList} from '../Types'; import {AnyNativeEvent, ListenerUnitList} from '../Types';
import { import {
getListenersFromTree, getListenersFromTree,
} from '../ListenerGetter'; } from '../ListenerGetter';
@ -39,7 +39,7 @@ export function getListeners(
nativeEvt: AnyNativeEvent, nativeEvt: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
target: null | EventTarget, target: null | EventTarget,
): ProcessingListenerList { ): ListenerUnitList {
if (!vNode) { if (!vNode) {
return []; return [];
} }
@ -48,7 +48,7 @@ export function getListeners(
// 判断是否需要触发change事件 // 判断是否需要触发change事件
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) { if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
addValueUpdateList(target); addValueUpdateList(target);
const event = createHandlerCustomEvent( const event = createCustomEvent(
'onChange', 'onChange',
'change', 'change',
nativeEvt, nativeEvt,

View File

@ -1,8 +1,9 @@
import type {VNode} from '../../renderer/Types'; import type {VNode} from '../../renderer/Types';
import type {AnyNativeEvent, ProcessingListenerList} from '../Types'; import type {AnyNativeEvent} from '../Types';
import {getListenersFromTree} from '../ListenerGetter'; import {getListenersFromTree} from '../ListenerGetter';
import {createHandlerCustomEvent} from '../customEvents/EventFactory'; import {createCustomEvent} from '../customEvents/EventFactory';
import {EVENT_TYPE_ALL} from '../const'; import {EVENT_TYPE_ALL} from '../const';
import {ListenerUnitList} from '../Types';
const compositionEventObj = { const compositionEventObj = {
compositionstart: 'onCompositionStart', compositionstart: 'onCompositionStart',
@ -16,10 +17,10 @@ export function getListeners(
nativeEvt: AnyNativeEvent, nativeEvt: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
target: null | EventTarget, target: null | EventTarget,
): ProcessingListenerList { ): ListenerUnitList {
const evtType = compositionEventObj[evtName]; const evtType = compositionEventObj[evtName];
const event = createHandlerCustomEvent( const event = createCustomEvent(
evtType, evtType,
evtName, evtName,
nativeEvt, nativeEvt,

View File

@ -1,13 +1,14 @@
import {createHandlerCustomEvent} from '../customEvents/EventFactory'; import {createCustomEvent} from '../customEvents/EventFactory';
import {shallowCompare} from '../../renderer/utils/compare'; import {shallowCompare} from '../../renderer/utils/compare';
import {getFocusedDom} from '../../dom/utils/Common'; import {getFocusedDom} from '../../dom/utils/Common';
import {getDom} from '../../dom/DOMInternalKeys'; import {getDom} from '../../dom/DOMInternalKeys';
import {isDocument} from '../../dom/utils/Common'; import {isDocument} from '../../dom/utils/Common';
import {isTextInputElement} from '../utils'; import {isTextInputElement} from '../utils';
import type {AnyNativeEvent, ProcessingListenerList} from '../Types'; import type {AnyNativeEvent} from '../Types';
import {getListenersFromTree} from '../ListenerGetter'; import {getListenersFromTree} from '../ListenerGetter';
import type {VNode} from '../../renderer/Types'; import type {VNode} from '../../renderer/Types';
import {EVENT_TYPE_ALL} from '../const'; import {EVENT_TYPE_ALL} from '../const';
import {ListenerUnitList} from '../Types';
const horizonEventName = 'onSelect' const horizonEventName = 'onSelect'
@ -53,7 +54,7 @@ function getSelectEvent(nativeEvent, target) {
if (!shallowCompare(lastSelection, currentSelection)) { if (!shallowCompare(lastSelection, currentSelection)) {
lastSelection = currentSelection; lastSelection = currentSelection;
const event = createHandlerCustomEvent( const event = createCustomEvent(
horizonEventName, horizonEventName,
'select', 'select',
nativeEvent, nativeEvent,
@ -83,9 +84,9 @@ export function getListeners(
nativeEvt: AnyNativeEvent, nativeEvt: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
target: null | EventTarget, target: null | EventTarget,
): ProcessingListenerList { ): ListenerUnitList {
const targetNode = vNode ? getDom(vNode) : window; const targetNode = vNode ? getDom(vNode) : window;
let eventUnitList: ProcessingListenerList = []; let eventUnitList: ListenerUnitList = [];
switch (name) { switch (name) {
case 'focusin': case 'focusin':
initTargetCache(targetNode, vNode); initTargetCache(targetNode, vNode);

View File

@ -1,35 +1,3 @@
import {isText} from '../dom/utils/Common';
import { CHAR_CODE_ENTER, CHAR_CODE_SPACE } from './const';
export function uniqueCharCode(nativeEvent): number {
let charCode = nativeEvent.charCode;
// 火狐浏览器没有设置enter键的charCode用keyCode
if (charCode === 0 && nativeEvent.keyCode === CHAR_CODE_ENTER) {
charCode = CHAR_CODE_ENTER;
}
// 当ctrl按下时10表示enter键按下
if (charCode === 10) {
charCode = CHAR_CODE_ENTER;
}
// 忽略非打印的Enter键
if (charCode >= CHAR_CODE_SPACE || charCode === CHAR_CODE_ENTER) {
return charCode;
}
return 0;
}
// 获取事件的target对象
export function getEventTarget(nativeEvent) {
const target = nativeEvent.target || nativeEvent.srcElement || window;
if (isText(target)) {
return target.parentNode;
}
return target;
}
// 支持的输入框类型 // 支持的输入框类型
const supportedInputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month', const supportedInputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month',
@ -46,11 +14,9 @@ export function isTextInputElement(dom?: HTMLElement): boolean {
// 例dragEnd -> onDragEnd // 例dragEnd -> onDragEnd
export function getCustomEventNameWithOn(name) { export function addOnPrefix(name) {
if (!name) { if (!name) {
return ''; return '';
} }
const capitalizedEvent = name[0].toUpperCase() + name.slice(1); return 'on' + name[0].toUpperCase() + name.slice(1);
const horizonEventName = 'on' + capitalizedEvent;
return horizonEventName;
} }

View File

@ -37,7 +37,7 @@ function mergeDefault(sourceObj, defaultObj) {
}); });
} }
function buildElement(isClone, type, setting, ...children) { function buildElement(isClone, type, setting, children) {
// setting中的值优先级最高clone情况下从 type 中取值,创建情况下直接赋值为 null // setting中的值优先级最高clone情况下从 type 中取值,创建情况下直接赋值为 null
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null); const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
@ -45,11 +45,14 @@ function buildElement(isClone, type, setting, ...children) {
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode(); let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
if (setting != null) { if (setting != null) {
Object.keys(setting).forEach(k => { const keys = Object.keys(setting);
const keyLength = keys.length;
for(let i = 0; i < keyLength; i++) {
const k = keys[i];
if (isValidKey(k)) { if (isValidKey(k)) {
props[k] = setting[k]; props[k] = setting[k];
} }
}); }
if (setting.ref !== undefined && isClone) { if (setting.ref !== undefined && isClone) {
vNode = getProcessingClassVNode(); vNode = getProcessingClassVNode();
} }
@ -69,11 +72,11 @@ function buildElement(isClone, type, setting, ...children) {
// 创建Element结构体供JSX编译时调用 // 创建Element结构体供JSX编译时调用
export function createElement(type, setting, ...children) { export function createElement(type, setting, ...children) {
return buildElement(false, type, setting, ...children); return buildElement(false, type, setting, children);
} }
export function cloneElement(element, setting, ...children) { export function cloneElement(element, setting, ...children) {
return buildElement(true, element, setting, ...children); return buildElement(true, element, setting, children);
} }
// 检测结构体是否为合法的Element // 检测结构体是否为合法的Element

View File

@ -8,10 +8,11 @@ import type {Update} from './UpdateHandler';
import {ClassComponent, TreeRoot} from './vnode/VNodeTags'; import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
import {FlagUtils, Interrupted} from './vnode/VNodeFlags'; import {FlagUtils, Interrupted} from './vnode/VNodeFlags';
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler'; import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
import {launchUpdateFromVNode, setBuildResultError, tryRenderRoot} from './TreeBuilder'; import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder';
import {setRootThrowError} from './submit/Submit'; import {setRootThrowError} from './submit/Submit';
import {handleSuspenseChildThrowError} from './render/SuspenseComponent'; import {handleSuspenseChildThrowError} from './render/SuspenseComponent';
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate'; import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
import {BuildErrored, setBuildResult} from './GlobalVar';
function consoleError(error: any): void { function consoleError(error: any): void {
if (isDev) { if (isDev) {
@ -82,7 +83,7 @@ export function handleRenderThrowError(
} }
// 抛出错误无法作为suspense内容处理或无suspense来处理这次当成真的错误来处理 // 抛出错误无法作为suspense内容处理或无suspense来处理这次当成真的错误来处理
setBuildResultError(); setBuildResult(BuildErrored);
// 向上遍历寻找ClassComponent组件同时也是Error Boundaries组件 或者 TreeRoot // 向上遍历寻找ClassComponent组件同时也是Error Boundaries组件 或者 TreeRoot
let vNode = sourceVNode.parent; let vNode = sourceVNode.parent;
@ -133,7 +134,7 @@ function triggerUpdate(vNode, state) {
const root = updateShouldUpdateOfTree(vNode); const root = updateShouldUpdateOfTree(vNode);
if (root !== null) { if (root !== null) {
tryRenderRoot(root); tryRenderFromRoot(root);
} }
} }

View File

@ -2,14 +2,16 @@
export const ByAsync = 'BY_ASYNC'; export const ByAsync = 'BY_ASYNC';
export const BySync = 'BY_SYNC'; export const BySync = 'BY_SYNC';
export const InRender = 'IN_RENDER'; export const InRender = 'IN_RENDER';
export const InEvent = 'IN_EVENT';
type RenderMode = typeof ByAsync | typeof BySync | typeof InRender; type RenderMode = typeof ByAsync | typeof BySync | typeof InRender | typeof InEvent;
// 当前执行模式标记 // 当前执行模式标记
let executeMode = { let executeMode = {
[ByAsync]: false, [ByAsync]: false,
[BySync]: false, [BySync]: false,
[InRender]: false, [InRender]: false,
[InEvent]: false,
}; };
export function changeMode(mode: RenderMode, state = true) { export function changeMode(mode: RenderMode, state = true) {
@ -21,7 +23,7 @@ export function checkMode(mode: RenderMode) {
} }
export function isExecuting() { export function isExecuting() {
return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender]; return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender] || executeMode[InEvent];
} }
export function copyExecuteMode() { export function copyExecuteMode() {

View File

@ -1,6 +1,6 @@
import type {VNode} from './Types'; import type {VNode} from './Types';
// 当前处理的classVNode用于inst.refs用法中的 // 当前处理的classVNode用于设置inst.refs
let processingClassVNode: VNode | null = null; let processingClassVNode: VNode | null = null;
export function getProcessingClassVNode(): VNode | null { export function getProcessingClassVNode(): VNode | null {
return processingClassVNode; return processingClassVNode;

View File

@ -9,9 +9,9 @@ import {
} from './TreeBuilder'; } from './TreeBuilder';
import { runAsyncEffects } from './submit/HookEffectHandler'; import { runAsyncEffects } from './submit/HookEffectHandler';
import { Callback, newUpdate, pushUpdate } from './UpdateHandler'; import { Callback, newUpdate, pushUpdate } from './UpdateHandler';
import { getFirstChild } from './vnode/VNodeUtils';
export { createVNode } from './vnode/VNodeCreator';
export { createVNode, createTreeRootVNode } from './vnode/VNodeCreator';
export { createPortal } from './components/CreatePortal'; export { createPortal } from './components/CreatePortal';
export { export {
asyncUpdates, asyncUpdates,

View File

@ -10,7 +10,7 @@ import { runAsyncEffects } from './submit/HookEffectHandler';
import { handleRenderThrowError } from './ErrorHandler'; import { handleRenderThrowError } from './ErrorHandler';
import componentRenders from './render'; import componentRenders from './render';
import { import {
BuildCompleted, BuildErrored, BuildCompleted,
BuildFatalErrored, BuildFatalErrored,
BuildInComplete, getBuildResult, BuildInComplete, getBuildResult,
getStartVNode, getStartVNode,
@ -22,10 +22,11 @@ import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils';
import { import {
ByAsync, ByAsync,
BySync, BySync,
InRender,
InEvent,
changeMode, changeMode,
checkMode, checkMode,
copyExecuteMode, copyExecuteMode,
InRender,
isExecuting, isExecuting,
setExecuteMode setExecuteMode
} from './ExecuteMode'; } from './ExecuteMode';
@ -36,12 +37,11 @@ import {
updateShouldUpdateOfTree updateShouldUpdateOfTree
} from './vnode/VNodeShouldUpdate'; } from './vnode/VNodeShouldUpdate';
// 当前运行的vNode节点
let processing: VNode | null = null;
// 不可恢复错误 // 不可恢复错误
let unrecoverableErrorDuringBuild: any = null; let unrecoverableErrorDuringBuild: any = null;
// 当前运行的vNode节点
let processing: VNode | null = null;
export function setProcessing(vNode: VNode | null) { export function setProcessing(vNode: VNode | null) {
processing = vNode; processing = vNode;
} }
@ -64,7 +64,7 @@ function collectDirtyNodes(vNode: VNode, parent: VNode): void {
} }
} }
// ============================== 向上递归 ============================== // ============================== 向上冒泡 ==============================
// 尝试完成当前工作单元然后移动到下一个兄弟工作单元。如果没有更多的同级请返回父vNode。 // 尝试完成当前工作单元然后移动到下一个兄弟工作单元。如果没有更多的同级请返回父vNode。
function bubbleVNode(vNode: VNode): void { function bubbleVNode(vNode: VNode): void {
@ -201,18 +201,18 @@ function buildVNodeTree(treeRoot: VNode) {
changeMode(InRender, true); changeMode(InRender, true);
// 计算出开始节点 // 计算出开始节点
const startUpdateVNode = calcStartUpdateVNode(treeRoot); const startVNode = calcStartUpdateVNode(treeRoot);
// 缓存起来 // 缓存起来
setStartVNode(startUpdateVNode); setStartVNode(startVNode);
// 清空toUpdateNodes // 清空toUpdateNodes
treeRoot.toUpdateNodes.clear(); treeRoot.toUpdateNodes.clear();
if (startUpdateVNode.tag !== TreeRoot) { // 不是根节点 if (startVNode.tag !== TreeRoot) { // 不是根节点
// 设置namespace用于createElement // 设置namespace用于createElement
const parentObj = findDomParent(startUpdateVNode); const parentObj = findDomParent(startVNode);
// 当在componentWillUnmount中调用setStateparent可能是null因为startUpdateVNode会被clear // 当在componentWillUnmount中调用setStateparent可能是null因为startVNode会被clear
if (parentObj !== null) { if (parentObj !== null) {
const domParent = parentObj.parent; const domParent = parentObj.parent;
resetNamespaceCtx(domParent); resetNamespaceCtx(domParent);
@ -220,20 +220,20 @@ function buildVNodeTree(treeRoot: VNode) {
} }
// 恢复父节点的context // 恢复父节点的context
recoverParentsContextCtx(startUpdateVNode); recoverParentsContextCtx(startVNode);
} }
// 重置环境变量,为重新进行深度遍历做准备 // 重置环境变量,为重新进行深度遍历做准备
resetProcessingVariables(startUpdateVNode); resetProcessingVariables(startVNode);
do { while (processing !== null) {
try { try {
while (processing !== null) { while (processing !== null) {
// 捕获创建 vNodes // 捕获创建 vNodes
const next = captureVNode(processing); const next = captureVNode(processing);
if (next === null) { if (next === null) {
// 如果没有产生新的,那么就完成当前节点,向上遍历 // 如果没有子节点,那么就完成当前节点,开始冒泡
bubbleVNode(processing); bubbleVNode(processing);
} else { } else {
processing = next; processing = next;
@ -241,14 +241,10 @@ function buildVNodeTree(treeRoot: VNode) {
} }
setProcessingClassVNode(null); setProcessingClassVNode(null);
break;
} catch (thrownValue) { } catch (thrownValue) {
handleError(treeRoot, thrownValue); handleError(treeRoot, thrownValue);
} }
} while (true); }
processing = null;
setExecuteMode(preMode); setExecuteMode(preMode);
} }
@ -272,7 +268,7 @@ function renderFromRoot(treeRoot) {
} }
// 尝试去渲染,已有任务就跳出 // 尝试去渲染,已有任务就跳出
export function tryRenderRoot(treeRoot: VNode) { export function tryRenderFromRoot(treeRoot: VNode) {
if (treeRoot.shouldUpdate && treeRoot.task === null) { if (treeRoot.shouldUpdate && treeRoot.task === null) {
// 任务放进queue但是调度开始还是异步的 // 任务放进queue但是调度开始还是异步的
treeRoot.task = pushRenderCallback( treeRoot.task = pushRenderCallback(
@ -304,7 +300,7 @@ export function launchUpdateFromVNode(vNode: VNode) {
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。 // 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
renderFromRoot(treeRoot); renderFromRoot(treeRoot);
} else { } else {
tryRenderRoot(treeRoot); tryRenderFromRoot(treeRoot);
if (!isExecuting()) { if (!isExecuting()) {
// 同步执行 // 同步执行
@ -313,12 +309,6 @@ export function launchUpdateFromVNode(vNode: VNode) {
} }
} }
export function setBuildResultError() {
if (getBuildResult() !== BuildCompleted) {
setBuildResult(BuildErrored);
}
}
// ============================== HorizonDOM使用 ============================== // ============================== HorizonDOM使用 ==============================
export function runDiscreteUpdates() { export function runDiscreteUpdates() {
if (checkMode(ByAsync) || checkMode(InRender)) { if (checkMode(ByAsync) || checkMode(InRender)) {
@ -331,7 +321,7 @@ export function runDiscreteUpdates() {
export function asyncUpdates(fn, ...param) { export function asyncUpdates(fn, ...param) {
const preMode = copyExecuteMode(); const preMode = copyExecuteMode();
changeMode(ByAsync, true); changeMode(InEvent, true);
try { try {
return fn(...param); return fn(...param);
} finally { } finally {

View File

@ -2,7 +2,7 @@ import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType'; import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags'; import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags';
import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator'; import {updateVNode, createVNodeFromElement, updateVNodePath, createFragmentVNode, createPortalVNode, createDomTextVNode} from '../vnode/VNodeCreator';
import { import {
isSameType, isSameType,
getIteratorFn, getIteratorFn,
@ -113,7 +113,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
switch (newNodeType) { switch (newNodeType) {
case DiffCategory.TEXT_NODE: { case DiffCategory.TEXT_NODE: {
if (oldNode === null || oldNode.tag !== DomText) { if (oldNode === null || oldNode.tag !== DomText) {
resultNode = createVNode(DomText, String(newChild)); resultNode = createDomTextVNode(String(newChild));
} else { } else {
resultNode = updateVNode(oldNode, String(newChild)); resultNode = updateVNode(oldNode, String(newChild));
} }
@ -121,7 +121,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
} }
case DiffCategory.ARR_NODE: { case DiffCategory.ARR_NODE: {
if (oldNode === null || oldNode.tag !== Fragment) { if (oldNode === null || oldNode.tag !== Fragment) {
resultNode = createVNode(Fragment, null, newChild); resultNode = createFragmentVNode(null, newChild);
} else { } else {
resultNode = updateVNode(oldNode, newChild); resultNode = updateVNode(oldNode, newChild);
} }
@ -132,7 +132,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
if (newChild.type === TYPE_FRAGMENT) { if (newChild.type === TYPE_FRAGMENT) {
if (oldNode === null || oldNode.tag !== Fragment) { if (oldNode === null || oldNode.tag !== Fragment) {
const key = oldNode !== null ? oldNode.key : newChild.key; const key = oldNode !== null ? oldNode.key : newChild.key;
resultNode = createVNode(Fragment, key, newChild.props.children); resultNode = createFragmentVNode(key, newChild.props.children);
} else { } else {
resultNode = updateVNode(oldNode, newChild); resultNode = updateVNode(oldNode, newChild);
} }
@ -151,7 +151,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
break; break;
} else if (newChild.vtype === TYPE_PORTAL) { } else if (newChild.vtype === TYPE_PORTAL) {
if (oldNode === null || oldNode.tag !== DomPortal || oldNode.outerDom !== newChild.outerDom) { if (oldNode === null || oldNode.tag !== DomPortal || oldNode.outerDom !== newChild.outerDom) {
resultNode = createVNode(DomPortal, newChild); resultNode = createPortalVNode(newChild);
} else { } else {
resultNode = updateVNode(oldNode, newChild.children || []); resultNode = updateVNode(oldNode, newChild.children || []);
} }
@ -504,7 +504,7 @@ function diffStringNodeHandler(
deleteVNodes(parentNode, firstChildVNode.next); deleteVNodes(parentNode, firstChildVNode.next);
newTextNode.next = null; newTextNode.next = null;
} else { } else {
newTextNode = createVNode(DomText, String(newChild)); newTextNode = createDomTextVNode(String(newChild));
deleteVNodes(parentNode, firstChildVNode); deleteVNodes(parentNode, firstChildVNode);
} }
@ -562,7 +562,7 @@ function diffObjectNodeHandler(
if (resultNode === null) { if (resultNode === null) {
// 新建 // 新建
if (newChild.type === TYPE_FRAGMENT) { if (newChild.type === TYPE_FRAGMENT) {
resultNode = createVNode(Fragment, newChild.key, newChild.props.children); resultNode = createFragmentVNode(newChild.key, newChild.props.children);
} else { } else {
resultNode = createVNodeFromElement(newChild); resultNode = createVNodeFromElement(newChild);
resultNode.ref = newChild.ref; resultNode.ref = newChild.ref;
@ -580,7 +580,7 @@ function diffObjectNodeHandler(
} }
if (resultNode === null) { if (resultNode === null) {
// 新建 // 新建
resultNode = createVNode(DomPortal, newChild); resultNode = createPortalVNode(newChild);
} }
} }

View File

@ -1,14 +1,13 @@
import type {VNode} from '../Types'; import type {VNode} from '../Types';
import {mergeDefaultProps} from './LazyComponent'; import {mergeDefaultProps} from './LazyComponent';
import {updateVNode, createVNode, onlyUpdateChildVNodes, updateVNodePath} from '../vnode/VNodeCreator'; import {updateVNode, onlyUpdateChildVNodes, updateVNodePath, createFragmentVNode, createUndeterminedVNode} from '../vnode/VNodeCreator';
import {shallowCompare} from '../utils/compare'; import {shallowCompare} from '../utils/compare';
import { import {
TYPE_FRAGMENT, TYPE_FRAGMENT,
TYPE_PROFILER, TYPE_PROFILER,
TYPE_STRICT_MODE, TYPE_STRICT_MODE,
} from '../../external/JSXElementType'; } from '../../external/JSXElementType';
import {Fragment} from '../vnode/VNodeTags';
export function bubbleRender() {} export function bubbleRender() {}
@ -24,9 +23,9 @@ export function captureMemoComponent(
let newChild = null; let newChild = null;
const type = Component.type; const type = Component.type;
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
newChild = createVNode(Fragment, null, newProps.children); newChild = createFragmentVNode(null, newProps.children);
} else { } else {
newChild = createVNode('props', type, null, newProps, processing); newChild = createUndeterminedVNode(type, null, newProps);
} }
newChild.parent = processing; newChild.parent = processing;
newChild.ref = processing.ref; newChild.ref = processing.ref;

View File

@ -1,15 +1,14 @@
import type {VNode, PromiseType} from '../Types'; import type {VNode, PromiseType} from '../Types';
import {FlagUtils, Interrupted} from '../vnode/VNodeFlags'; import {FlagUtils, Interrupted} from '../vnode/VNodeFlags';
import {createVNode, onlyUpdateChildVNodes, updateVNode, updateVNodePath} from '../vnode/VNodeCreator'; import {onlyUpdateChildVNodes, updateVNode, updateVNodePath, createFragmentVNode} from '../vnode/VNodeCreator';
import { import {
ClassComponent, ClassComponent,
IncompleteClassComponent, IncompleteClassComponent,
SuspenseComponent, SuspenseComponent,
Fragment,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
import {pushForceUpdate} from '../UpdateHandler'; import {pushForceUpdate} from '../UpdateHandler';
import {launchUpdateFromVNode, tryRenderRoot} from '../TreeBuilder'; import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder';
import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate'; import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate';
import {getContextChangeCtx} from '../ContextSaver'; import {getContextChangeCtx} from '../ContextSaver';
@ -31,12 +30,12 @@ function createFallback(processing: VNode, fallbackChildren) {
if (oldFallbackFragment !== null) { if (oldFallbackFragment !== null) {
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren); fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
} else { } else {
fallbackFragment = createVNode(Fragment, null, fallbackChildren); fallbackFragment = createFragmentVNode(null, fallbackChildren);
FlagUtils.markAddition(fallbackFragment); FlagUtils.markAddition(fallbackFragment);
} }
} else { } else {
// 创建 // 创建
fallbackFragment = createVNode(Fragment, null, fallbackChildren); fallbackFragment = createFragmentVNode(null, fallbackChildren);
} }
processing.child = childFragment; processing.child = childFragment;
@ -72,7 +71,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
// SuspenseComponent 中使用 // SuspenseComponent 中使用
processing.suspenseChildStatus = SuspenseChildStatus.ShowChild; processing.suspenseChildStatus = SuspenseChildStatus.ShowChild;
} else { } else {
childFragment = createVNode(Fragment, null, newChildren); childFragment = createFragmentVNode(null, newChildren);
} }
childFragment.parent = processing; childFragment.parent = processing;
@ -210,7 +209,7 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
suspenseVNode.promiseResolve = true; suspenseVNode.promiseResolve = true;
const root = updateShouldUpdateOfTree(suspenseVNode); const root = updateShouldUpdateOfTree(suspenseVNode);
if (root !== null) { if (root !== null) {
tryRenderRoot(root); tryRenderFromRoot(root);
} }
} }

View File

@ -7,22 +7,16 @@ import type {
Effect as HookEffect, Effect as HookEffect,
EffectList, EffectList,
} from '../hooks/HookType'; } from '../hooks/HookType';
import {
callRenderQueueImmediate,
} from '../taskExecutor/RenderQueue';
import {runAsync} from '../taskExecutor/TaskExecutor'; import {runAsync} from '../taskExecutor/TaskExecutor';
import { import {
copyExecuteMode, InRender, setExecuteMode,changeMode copyExecuteMode, InRender, setExecuteMode,changeMode
} from '../ExecuteMode'; } from '../ExecuteMode';
import {handleSubmitError} from '../ErrorHandler';
import {clearDirtyNodes} from './Submit';
import {EffectConstant} from '../hooks/EffectConstant'; import {EffectConstant} from '../hooks/EffectConstant';
let hookEffects: Array<HookEffect | VNode> = []; let hookEffects: Array<HookEffect | VNode> = [];
let hookRemoveEffects: Array<HookEffect | VNode> = []; let hookRemoveEffects: Array<HookEffect | VNode> = [];
// 是否正在异步调度effects // 是否正在异步调度effects
let isScheduling: boolean = false; let isScheduling: boolean = false;
let hookEffectRoot: VNode | null = null;
export function setSchedulingEffects(value) { export function setSchedulingEffects(value) {
isScheduling = value; isScheduling = value;
@ -31,13 +25,6 @@ export function isSchedulingEffects() {
return isScheduling; return isScheduling;
} }
export function setHookEffectRoot(root: VNode | null) {
hookEffectRoot = root;
}
export function getHookEffectRoot() {
return hookEffectRoot;
}
export function callUseEffects(vNode: VNode) { export function callUseEffects(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;
@ -47,8 +34,8 @@ export function callUseEffects(vNode: VNode) {
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect && (effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect (effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
) { ) {
hookEffects.push({effect, vNode}); hookEffects.push(effect);
hookRemoveEffects.push({effect, vNode}); hookRemoveEffects.push(effect);
// 异步调用 // 异步调用
if (!isScheduling) { if (!isScheduling) {
@ -60,20 +47,13 @@ export function callUseEffects(vNode: VNode) {
} }
export function runAsyncEffects() { export function runAsyncEffects() {
if (hookEffectRoot === null) {
return false;
}
const root = hookEffectRoot;
hookEffectRoot = null;
const preMode = copyExecuteMode(); const preMode = copyExecuteMode();
changeMode(InRender, true); changeMode(InRender, true);
// 调用effect destroy // 调用effect destroy
const removeEffects = hookRemoveEffects; const removeEffects = hookRemoveEffects;
hookRemoveEffects = []; hookRemoveEffects = [];
removeEffects.forEach(({effect, vNode}) => { removeEffects.forEach((effect) => {
const destroy = effect.removeEffect; const destroy = effect.removeEffect;
effect.removeEffect = undefined; effect.removeEffect = undefined;
@ -81,7 +61,7 @@ export function runAsyncEffects() {
try { try {
destroy(); destroy();
} catch (error) { } catch (error) {
handleSubmitError(vNode, error); // 不处理副作用阶段抛出的异常
} }
} }
}); });
@ -89,24 +69,17 @@ export function runAsyncEffects() {
// 调用effect create // 调用effect create
const createEffects = hookEffects; const createEffects = hookEffects;
hookEffects = []; hookEffects = [];
createEffects.forEach(({effect, vNode}) => { createEffects.forEach((effect) => {
try { try {
const create = effect.effect; const create = effect.effect;
effect.removeEffect = create(); effect.removeEffect = create();
} catch (error) { } catch (error) {
handleSubmitError(vNode, error); // 不处理副作用阶段抛出的异常
} }
}); });
// 清理dirtyNodes
clearDirtyNodes(root.dirtyNodes);
setExecuteMode(preMode); setExecuteMode(preMode);
callRenderQueueImmediate();
return true;
} }
// 在销毁vNode的时候调用remove // 在销毁vNode的时候调用remove
@ -118,7 +91,7 @@ export function callEffectRemove(vNode: VNode) {
if (removeEffect !== undefined) { if (removeEffect !== undefined) {
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect就异步调用 if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect就异步调用
hookRemoveEffects.push({effect, vNode}); hookRemoveEffects.push(effect);
if (!isScheduling) { if (!isScheduling) {
isScheduling = true; isScheduling = true;
@ -136,10 +109,10 @@ export function callUseLayoutEffectRemove(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange; const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
effectList.forEach(item => { effectList.forEach(effect => {
if ((item.effectConstant & layoutLabel) === layoutLabel) { if ((effect.effectConstant & layoutLabel) === layoutLabel) {
const remove = item.removeEffect; const remove = effect.removeEffect;
item.removeEffect = undefined; effect.removeEffect = undefined;
if (typeof remove === 'function') { if (typeof remove === 'function') {
remove(); remove();
} }
@ -152,10 +125,10 @@ export function callUseLayoutEffectCreate(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange; const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
effectList.forEach(item => { effectList.forEach(effect => {
if ((item.effectConstant & layoutLabel) === layoutLabel) { if ((effect.effectConstant & layoutLabel) === layoutLabel) {
const create = item.effect; const create = effect.effect;
item.removeEffect = create(); effect.removeEffect = create();
} }
}); });
} }

View File

@ -27,7 +27,6 @@ import {
removeChildDom, removeChildDom,
hideDom, hideDom,
unHideDom, unHideDom,
clearContainer,
} from '../../dom/DOMOperator'; } from '../../dom/DOMOperator';
import { import {
callEffectRemove, callEffectRemove,
@ -56,28 +55,17 @@ function callComponentWillUnmount(vNode: VNode, instance: any) {
function callBeforeSubmitLifeCycles( function callBeforeSubmitLifeCycles(
vNode: VNode, vNode: VNode,
): void { ): void {
switch (vNode.tag) { if (vNode.tag === ClassComponent && !vNode.isCreated) { // 调用instance.getSnapshotBeforeUpdate
case ClassComponent: { // 调用instance.getSnapshotBeforeUpdate const prevProps = vNode.isLazyComponent
if (!vNode.isCreated) { ? mergeDefaultProps(vNode.type, vNode.oldProps)
const prevProps = vNode.isLazyComponent : vNode.oldProps;
? mergeDefaultProps(vNode.type, vNode.oldProps) const prevState = vNode.oldState;
: vNode.oldProps; const instance = vNode.realNode;
const prevState = vNode.oldState;
const instance = vNode.realNode;
const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState); const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
// __snapshotResult会在调用componentDidUpdate的时候作为第三个参数 // __snapshotResult会在调用componentDidUpdate的时候作为第三个参数
instance.__snapshotResult = snapshot; instance.__snapshotResult = snapshot;
}
return;
}
case TreeRoot: {
const root = vNode.realNode;
clearContainer(root.outerDom);
}
// No Default
} }
} }
@ -138,7 +126,6 @@ function callAfterSubmitLifeCycles(
if (vNode.isCreated && vNode.flags.Update) { if (vNode.isCreated && vNode.flags.Update) {
// button、input、select、textarea、如果有 autoFocus 属性需要focus // button、input、select、textarea、如果有 autoFocus 属性需要focus
if (shouldAutoFocus(vNode.type, vNode.props)) { if (shouldAutoFocus(vNode.type, vNode.props)) {
// button、input、select、textarea、如果有 autoFocus 属性需要focus
vNode.realNode.focus(); vNode.realNode.focus();
} }
} }
@ -333,11 +320,12 @@ function submitClear(vNode: VNode): void {
clearVNode(clearChild); clearVNode(clearChild);
clearChild = clearChild.next as VNode; clearChild = clearChild.next as VNode;
} }
// 在所有子项都卸载后删除dom树中的节点 // 在所有子项都卸载后删除dom树中的节点
removeChildDom(currentParent, vNode.realNode); removeChildDom(currentParent, vNode.realNode);
currentParent.append(cloneDom); currentParent.append(cloneDom);
vNode.realNode = cloneDom; vNode.realNode = cloneDom;
attachRef(vNode);
FlagUtils.removeFlag(vNode, Clear); FlagUtils.removeFlag(vNode, Clear);
vNode.clearChild = null; vNode.clearChild = null;
} }

View File

@ -11,18 +11,16 @@ import {
callBeforeSubmitLifeCycles, submitDeletion, submitAddition, callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
submitResetTextContent, submitUpdate, detachRef, submitClear, submitResetTextContent, submitUpdate, detachRef, submitClear,
} from './LifeCycleHandler'; } from './LifeCycleHandler';
import {tryRenderRoot, setProcessing} from '../TreeBuilder'; import {tryRenderFromRoot} from '../TreeBuilder';
import { import {
BySync,
InRender, InRender,
copyExecuteMode, copyExecuteMode,
setExecuteMode, setExecuteMode,
checkMode,
changeMode, changeMode,
} from '../ExecuteMode'; } from '../ExecuteMode';
import { import {
isSchedulingEffects, isSchedulingEffects,
setSchedulingEffects, setHookEffectRoot, setSchedulingEffects,
} from './HookEffectHandler'; } from './HookEffectHandler';
import {getStartVNode} from '../GlobalVar'; import {getStartVNode} from '../GlobalVar';
@ -67,18 +65,13 @@ export function submitToRender(treeRoot) {
if (isSchedulingEffects()) { if (isSchedulingEffects()) {
setSchedulingEffects(false); setSchedulingEffects(false);
// 记录root说明这个root有副作用要执行
setHookEffectRoot(treeRoot);
} else {
clearDirtyNodes(dirtyNodes);
} }
// 统计root同步重渲染的次数如果太多可能是无线循环 // 统计root同步重渲染的次数如果太多可能是无线循环
countLoopingUpdate(treeRoot); countLoopingUpdate(treeRoot);
// 在退出`submit` 之前始终调用此函数以确保任何已计划在此根上执行的update被执行。 // 在退出`submit` 之前始终调用此函数以确保任何已计划在此根上执行的update被执行。
tryRenderRoot(treeRoot); tryRenderFromRoot(treeRoot);
if (rootThrowError) { if (rootThrowError) {
const error = rootThrowError; const error = rootThrowError;
@ -86,11 +79,6 @@ export function submitToRender(treeRoot) {
throw error; throw error;
} }
// 非批量即同步执行的没有必要去执行RenderQueueRenderQueue放的是异步的
if (!checkMode(BySync)) { // 非批量
callRenderQueueImmediate();
}
return null; return null;
} }
@ -101,7 +89,6 @@ function beforeSubmit(dirtyNodes: Array<VNode>) {
callBeforeSubmitLifeCycles(node); callBeforeSubmitLifeCycles(node);
} }
} catch (error) { } catch (error) {
throwIfTrue(node === null, 'Should be working on an effect.');
handleSubmitError(node, error); handleSubmitError(node, error);
} }
}); });
@ -143,7 +130,6 @@ function submit(dirtyNodes: Array<VNode>) {
} }
} }
} catch (error) { } catch (error) {
throwIfTrue(node === null, 'Should be working on an effect.');
handleSubmitError(node, error); handleSubmitError(node, error);
} }
}); });
@ -160,7 +146,6 @@ function afterSubmit(dirtyNodes: Array<VNode>) {
attachRef(node); attachRef(node);
} }
} catch (error) { } catch (error) {
throwIfTrue(node === null, 'Should be working on an effect.');
handleSubmitError(node, error); handleSubmitError(node, error);
} }
}); });
@ -197,13 +182,3 @@ export function checkLoopingUpdateLimit() {
); );
} }
} }
// 清理dirtyNodes
export function clearDirtyNodes(dirtyNodes) {
dirtyNodes.forEach(node => {
if (node.flags.Deletion) {
node.realNode = null;
node.next = null;
}
});
}

View File

@ -28,22 +28,14 @@ function callRenderQueue() {
// 防止重入 // 防止重入
isCallingRenderQueue = true; isCallingRenderQueue = true;
let i = 0;
try { try {
for (; i < renderQueue.length; i++) { let callback;
let callback = renderQueue[i]; while (callback = renderQueue.shift()) {
callback(); callback();
} }
renderQueue = null; renderQueue = null;
} catch (error) { } catch (error) {
// 如果有异常抛出,请将剩余的回调留在队列中
if (renderQueue !== null) {
renderQueue = renderQueue.slice(i + 1);
}
// 在下一个异步中再调用
runAsync(callRenderQueueImmediate, ImmediatePriority);
throw error; throw error;
} finally { } finally {
isCallingRenderQueue = false; isCallingRenderQueue = false;

View File

@ -87,59 +87,71 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
function getVNodeTag(type: any) { function getVNodeTag(type: any) {
let vNodeTag = ClsOrFunComponent; let vNodeTag = ClsOrFunComponent;
let isLazy = false; let isLazy = false;
const componentType = typeof type;
if (typeof type === 'function') { if (componentType === 'function') {
if (isClassComponent(type)) { if (isClassComponent(type)) {
vNodeTag = ClassComponent; vNodeTag = ClassComponent;
} }
} else if (typeof type === 'string') { } else if (componentType === 'string') {
vNodeTag = DomComponent; vNodeTag = DomComponent;
} else if (type === TYPE_SUSPENSE) { } else if (type === TYPE_SUSPENSE) {
vNodeTag = SuspenseComponent; vNodeTag = SuspenseComponent;
} else if (typeof type === 'object' && type !== null && typeMap[type.vtype]) { } else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {
vNodeTag = typeMap[type.vtype]; vNodeTag = typeMap[type.vtype];
isLazy = type.vtype === TYPE_LAZY; isLazy = type.vtype === TYPE_LAZY;
} else { } else {
throw Error(`Component type is invalid, got: ${type == null ? type : typeof type}`); throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`);
} }
return { vNodeTag, isLazy }; return { vNodeTag, isLazy };
} }
export function createFragmentVNode(fragmentKey, fragmentProps) {
const vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps);
vNode.shouldUpdate = true;
return vNode;
}
export function createDomTextVNode(content) {
const vNode = newVirtualNode(DomText, null, content);
vNode.shouldUpdate = true;
return vNode;
}
export function createPortalVNode(portal) {
const children = portal.children ?? [];
const vNode = newVirtualNode(DomPortal, portal.key, children);
vNode.shouldUpdate = true;
vNode.outerDom = portal.outerDom;
return vNode;
}
export function createUndeterminedVNode(type, key, props) {
const { vNodeTag, isLazy } = getVNodeTag(type);
const vNode = newVirtualNode(vNodeTag, key, props);
vNode.type = type;
vNode.shouldUpdate = true;
// lazy类型的特殊处理
vNode.isLazyComponent = isLazy;
if (isLazy) {
vNode.lazyType = type;
}
return vNode;
}
export function createTreeRootVNode(container) {
const vNode = newVirtualNode(TreeRoot, null, null, container);
vNode.path.push(0);
createUpdateArray(vNode);
return vNode;
}
// TODO: 暂时保留给测试用例使用,后续修改测试用例
export function createVNode(tag: VNodeTag | string, ...secondArg) { export function createVNode(tag: VNodeTag | string, ...secondArg) {
let vNode = null; let vNode = null;
switch (tag) { switch (tag) {
case Fragment:
const [fragmentKey, fragmentProps] = secondArg;
vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps);
vNode.shouldUpdate = true;
break;
case DomText:
const content = secondArg[0];
vNode = newVirtualNode(DomText, null, content);
vNode.shouldUpdate = true;
break;
case DomPortal:
const portal = secondArg[0];
const children = portal.children ?? [];
vNode = newVirtualNode(DomPortal, portal.key, children);
vNode.shouldUpdate = true;
vNode.outerDom = portal.outerDom;
break;
case 'props':
const [type, key, props] = secondArg;
const { vNodeTag, isLazy } = getVNodeTag(type);
vNode = newVirtualNode(vNodeTag, key, props);
vNode.type = type;
vNode.shouldUpdate = true;
// lazy类型的特殊处理
vNode.isLazyComponent = isLazy;
if (isLazy) {
vNode.lazyType = type;
}
break;
case TreeRoot: case TreeRoot:
// 创建treeRoot // 创建treeRoot
vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]); vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]);
@ -162,9 +174,9 @@ export function createVNodeFromElement(element: JSXElement): VNode {
const props = element.props; const props = element.props;
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
return createVNode(Fragment, key, props.children); return createFragmentVNode(key, props.children);
} else { } else {
return createVNode('props', type, key, props); return createUndeterminedVNode(type, key, props);
} }
} }

View File

@ -39,14 +39,16 @@ export class FlagUtils {
}); });
} }
static hasAnyFlag(node: VNode) { // 有标志位 static hasAnyFlag(node: VNode) { // 有标志位
let keyFlag = false; const flags = node.flags;
FlagArr.forEach(key => { const arrLength = FlagArr.length;
if (node.flags[key]) { let key;
keyFlag = true; for (let i = 0; i < arrLength; i++) {
return; key = FlagArr[i];
if (flags[key]) {
return true;
} }
}); }
return keyFlag; return false;
} }
static setNoFlags(node: VNode) { static setNoFlags(node: VNode) {
@ -91,7 +93,6 @@ export class FlagUtils {
static markForceUpdate(node: VNode) { static markForceUpdate(node: VNode) {
node.flags.ForceUpdate = true; node.flags.ForceUpdate = true;
} }
static markClear(node: VNode) { static markClear(node: VNode) {
node.flags.Clear = true; node.flags.Clear = true;
} }

View File

@ -56,6 +56,7 @@ export function updateParentsChildShouldUpdate(vNode: VNode) {
let isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate; let isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate;
if (isShouldUpdate) { // 开始节点是shouldUpdate或childShouldUpdate if (isShouldUpdate) { // 开始节点是shouldUpdate或childShouldUpdate
// 更新从当前节点到根节点的childShouldUpdate为true
setParentsChildShouldUpdate(node); setParentsChildShouldUpdate(node);
} else { } else {
while (node !== null) { while (node !== null) {

View File

@ -81,7 +81,6 @@ export function clearVNode(vNode: VNode) {
vNode.next = null; vNode.next = null;
vNode.depContexts = []; vNode.depContexts = [];
vNode.dirtyNodes = []; vNode.dirtyNodes = [];
vNode.oldProps = null;
vNode.state = null; vNode.state = null;
vNode.hooks = []; vNode.hooks = [];
vNode.suspenseChildStatus = ''; vNode.suspenseChildStatus = '';
@ -91,7 +90,9 @@ export function clearVNode(vNode: VNode) {
vNode.changeList = null; vNode.changeList = null;
vNode.effectList = []; vNode.effectList = [];
vNode.updates = null; vNode.updates = null;
vNode.realNode = null;
vNode.oldProps = null;
vNode.oldHooks = []; vNode.oldHooks = [];
vNode.oldState = null; vNode.oldState = null;
vNode.oldRef = null; vNode.oldRef = null;
@ -260,14 +261,14 @@ export function getExactNode(targetVNode, targetContainer) {
if (isPortalRoot(vNode, targetContainer)) { if (isPortalRoot(vNode, targetContainer)) {
return null; return null;
} }
while (container !== null) { while (container !== null) {
const parentNode = getNearestVNode(container); const parentNode = getNearestVNode(container);
if (parentNode === null) { if (parentNode === null) {
return null; return null;
} }
if (parentNode.tag === DomComponent || parentNode.tag === DomText) { if (parentNode.tag === DomComponent || parentNode.tag === DomText) {
vNode = parentNode; return getExactNode(parentNode, targetContainer);
return getExactNode(vNode, targetContainer);
} }
container = container.parentNode; container = container.parentNode;
} }