Match-id-fda5629a5eba25fc511063c1cebf7752506caf2d
This commit is contained in:
commit
5b6f0a11d0
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
asyncUpdates, createVNode, getFirstCustomDom,
|
||||
asyncUpdates, getFirstCustomDom,
|
||||
syncUpdates, startUpdate,
|
||||
createTreeRootVNode,
|
||||
} from '../renderer/Renderer';
|
||||
import {createPortal} from '../renderer/components/CreatePortal';
|
||||
import type {Container} from './DOMOperator';
|
||||
import {isElement} from './utils/Common';
|
||||
import {listenDelegatedEvents} from '../event/EventBinding';
|
||||
import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
|
||||
import {TreeRoot} from '../renderer/vnode/VNodeTags';
|
||||
import {Callback} from '../renderer/UpdateHandler';
|
||||
|
||||
function createRoot(children: any, container: Container, callback?: Callback) {
|
||||
|
@ -19,7 +19,7 @@ function createRoot(children: any, container: Container, callback?: Callback) {
|
|||
}
|
||||
|
||||
// 调度器创建根节点,并给容器dom赋vNode结构体
|
||||
const treeRoot = createVNode(TreeRoot, container);
|
||||
const treeRoot = createTreeRootVNode(container);
|
||||
container._treeRoot = treeRoot;
|
||||
|
||||
// 根节点挂接全量事件
|
||||
|
|
|
@ -19,7 +19,6 @@ const prefix = '_horizon';
|
|||
const internalKeys = {
|
||||
VNode: `${prefix}VNode`,
|
||||
props: `${prefix}Props`,
|
||||
events: `${prefix}Events`,
|
||||
nonDelegatedEvents: `${prefix}NonDelegatedEvents`,
|
||||
};
|
||||
|
||||
|
@ -58,6 +57,7 @@ export function getNearestVNode(dom: Node): null | VNode {
|
|||
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
|
||||
return vNode;
|
||||
}
|
||||
|
||||
// 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过
|
||||
let parentDom = dom.parentNode;
|
||||
let nearVNode = null;
|
||||
|
@ -82,20 +82,11 @@ export function updateVNodeProps(dom: Element | Text, props: Props): void {
|
|||
dom[internalKeys.props] = props;
|
||||
}
|
||||
|
||||
export function getEventListeners(dom: EventTarget): Set<string> {
|
||||
let elementListeners = dom[internalKeys.events];
|
||||
if (!elementListeners) {
|
||||
elementListeners = new Set();
|
||||
dom[internalKeys.events] = elementListeners;
|
||||
}
|
||||
return elementListeners;
|
||||
}
|
||||
|
||||
export function getEventToListenerMap(target: EventTarget): Map<string, EventListener> {
|
||||
let eventsMap = target[internalKeys.nonDelegatedEvents];
|
||||
export function getNonDelegatedListenerMap(dom: Element | Text): Map<string, EventListener> {
|
||||
let eventsMap = dom[internalKeys.nonDelegatedEvents];
|
||||
if (!eventsMap) {
|
||||
eventsMap = new Map();
|
||||
target[internalKeys.nonDelegatedEvents] = eventsMap;
|
||||
dom[internalKeys.nonDelegatedEvents] = eventsMap;
|
||||
}
|
||||
return eventsMap;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
listenDelegatedEvents(portal);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,11 @@ function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
|
|||
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||
dom.innerHTML = propVal.__html;
|
||||
} else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理
|
||||
if (typeof propVal === 'string' || typeof propVal === 'number') {
|
||||
dom.textContent = String(propVal);
|
||||
const type = typeof propVal;
|
||||
if (type === 'string') {
|
||||
dom.textContent = propVal;
|
||||
} else if (type === 'number') {
|
||||
dom.textContent = propVal + ''; // 这种数字转字符串的方式效率最高
|
||||
}
|
||||
} else if (isEventProp(propName)) {
|
||||
// 事件监听属性处理
|
||||
|
|
|
@ -5,7 +5,7 @@ import {Props} from '../DOMOperator';
|
|||
* 获取当前聚焦的 input 或者 textarea 元素
|
||||
* @param doc 指定 document
|
||||
*/
|
||||
export function getFocusedDom(doc?: Document): HorizonDom | void {
|
||||
export function getFocusedDom(doc?: Document): HorizonDom | null {
|
||||
let currentDocument;
|
||||
if (doc) {
|
||||
currentDocument = doc;
|
||||
|
|
|
@ -5,6 +5,13 @@ export const NSS = {
|
|||
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元素
|
||||
export function createDom(
|
||||
tagName: string,
|
||||
|
@ -16,6 +23,18 @@ export function createDom(
|
|||
|
||||
if (ns !== NSS.html) {
|
||||
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 {
|
||||
dom = document.createElement(tagName);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
getTextareaPropsWithoutValue,
|
||||
updateTextareaValue,
|
||||
} from './TextareaValueHandler';
|
||||
import {getDomTag} from "../utils/Common";
|
||||
|
||||
// 获取元素除了被代理的值以外的属性
|
||||
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) {
|
||||
case 'input':
|
||||
resetInputValue(<HTMLInputElement>dom, properties);
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import {getVNodeProps} from '../dom/DOMInternalKeys';
|
||||
import {resetValue} from '../dom/valueHandler';
|
||||
import {getDomTag} from '../dom/utils/Common';
|
||||
|
||||
let updateList = null;
|
||||
let updateList: Array<any> | null = null;
|
||||
|
||||
// 受控组件值重新赋值
|
||||
function updateValue(target: Element) {
|
||||
const props = getVNodeProps(target);
|
||||
if (props) {
|
||||
const type = getDomTag(target);
|
||||
resetValue(target, type, props);
|
||||
resetValue(target, props);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +1,60 @@
|
|||
/**
|
||||
* 事件绑定实现
|
||||
* 事件绑定实现,分为绑定委托事件和非委托事件
|
||||
*/
|
||||
import {allDelegatedNativeEvents} from './EventCollection';
|
||||
import {isDocument} from '../dom/utils/Common';
|
||||
import {
|
||||
getEventListeners,
|
||||
getEventToListenerMap,
|
||||
getNearestVNode,
|
||||
getNonDelegatedListenerMap,
|
||||
} from '../dom/DOMInternalKeys';
|
||||
import {createCustomEventListener} from './WrapperListener';
|
||||
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 =
|
||||
'_horizonListening' +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.slice(4);
|
||||
const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
|
||||
|
||||
// 获取节点上已经委托事件名称
|
||||
function getListenerSetKey(nativeEvtName: string, isCapture: boolean): string {
|
||||
const sufix = isCapture ? 'capture' : 'bubble';
|
||||
return `${nativeEvtName}__${sufix}`;
|
||||
// 触发委托事件
|
||||
function triggerDelegatedEvent(
|
||||
nativeEvtName: string,
|
||||
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(
|
||||
nativeEvtName: string,
|
||||
delegatedElement: Element,
|
||||
isCapture: boolean,
|
||||
): void {
|
||||
let target: Element | Document = delegatedElement;
|
||||
let dom: Element | Document = delegatedElement;
|
||||
// document层次可能触发selectionchange事件,为了捕获这类事件,selectionchange事件绑定在document节点上
|
||||
if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) {
|
||||
target = delegatedElement.ownerDocument;
|
||||
dom = delegatedElement.ownerDocument;
|
||||
}
|
||||
|
||||
const listenerSet = getEventListeners(target);
|
||||
const listenerSetKey = getListenerSetKey(nativeEvtName, isCapture);
|
||||
|
||||
if (!listenerSet.has(listenerSetKey)) {
|
||||
const listener = createCustomEventListener(
|
||||
target,
|
||||
nativeEvtName,
|
||||
isCapture,
|
||||
);
|
||||
target.addEventListener(nativeEvtName, listener, !!isCapture);
|
||||
listenerSet.add(listenerSetKey);
|
||||
}
|
||||
const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, dom);
|
||||
dom.addEventListener(nativeEvtName, listener, isCapture);
|
||||
}
|
||||
|
||||
// 监听所有委托事件
|
||||
|
@ -54,11 +64,12 @@ export function listenDelegatedEvents(dom: Element) {
|
|||
return;
|
||||
}
|
||||
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') {
|
||||
return false;
|
||||
}
|
||||
|
@ -86,7 +97,7 @@ function getIsCapture(horizonEventName) {
|
|||
|
||||
// 封装监听函数
|
||||
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
|
||||
return (event) => {
|
||||
return event => {
|
||||
const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement);
|
||||
listener(customEvent);
|
||||
};
|
||||
|
@ -98,23 +109,24 @@ export function listenNonDelegatedEvent(
|
|||
domElement: Element,
|
||||
listener,
|
||||
): void {
|
||||
const isCapture = getIsCapture(horizonEventName);
|
||||
const isCapture = isCaptureEvent(horizonEventName);
|
||||
const nativeEvtName = getNativeEvtName(horizonEventName, isCapture);
|
||||
|
||||
// 先判断是否存在老的监听事件,若存在则移除
|
||||
const eventToListenerMap = getEventToListenerMap(domElement);
|
||||
if (eventToListenerMap.get(horizonEventName)) {
|
||||
domElement.removeEventListener(nativeEvtName, eventToListenerMap.get(horizonEventName));
|
||||
const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement);
|
||||
const currentListener = nonDelegatedListenerMap.get(horizonEventName);
|
||||
if (currentListener) {
|
||||
domElement.removeEventListener(nativeEvtName, currentListener);
|
||||
nonDelegatedListenerMap.delete(horizonEventName);
|
||||
}
|
||||
|
||||
if (typeof listener !== 'function') {
|
||||
eventToListenerMap.delete(nativeEvtName);
|
||||
return;
|
||||
}
|
||||
|
||||
// 为了和委托事件对外行为一致,将事件对象封装成CustomBaseEvent
|
||||
const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener);
|
||||
// 添加新的监听
|
||||
eventToListenerMap.set(horizonEventName, wrapperListener);
|
||||
nonDelegatedListenerMap.set(horizonEventName, wrapperListener);
|
||||
domElement.addEventListener(nativeEvtName, wrapperListener, isCapture);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ export const allDelegatedNativeEvents = new Set();
|
|||
horizonEventToNativeMap.forEach((dependencies, horizonEvent) => {
|
||||
allDelegatedHorizonEvents.set(horizonEvent, dependencies);
|
||||
allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies);
|
||||
|
||||
dependencies.forEach(d => {
|
||||
allDelegatedNativeEvents.add(d);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import type { AnyNativeEvent, ProcessingListenerList } from './types';
|
||||
import type { AnyNativeEvent } from './Types';
|
||||
import type { VNode } from '../renderer/Types';
|
||||
|
||||
import {
|
||||
|
@ -7,24 +7,19 @@ import {
|
|||
EVENT_TYPE_BUBBLE,
|
||||
EVENT_TYPE_CAPTURE,
|
||||
} from './const';
|
||||
import {
|
||||
throwCaughtEventError,
|
||||
runListenerAndCatchFirstError,
|
||||
} from './EventError';
|
||||
import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler';
|
||||
import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler';
|
||||
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
|
||||
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
|
||||
import {
|
||||
getCustomEventNameWithOn,
|
||||
uniqueCharCode,
|
||||
getEventTarget
|
||||
addOnPrefix,
|
||||
} from './utils';
|
||||
import { createCommonCustomEvent } from './customEvents/EventFactory';
|
||||
import { createCustomEvent } from './customEvents/EventFactory';
|
||||
import { getListenersFromTree } from './ListenerGetter';
|
||||
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
|
||||
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
|
||||
import { getExactNode } from '../renderer/vnode/VNodeUtils';
|
||||
import {ListenerUnitList} from './Types';
|
||||
|
||||
// 获取事件触发的普通事件监听方法队列
|
||||
function getCommonListeners(
|
||||
|
@ -33,14 +28,9 @@ function getCommonListeners(
|
|||
nativeEvent: AnyNativeEvent,
|
||||
target: null | EventTarget,
|
||||
isCapture: boolean,
|
||||
): ProcessingListenerList {
|
||||
const customEventName = getCustomEventNameWithOn(CommonEventToHorizonMap[nativeEvtName]);
|
||||
if (!customEventName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 火狐浏览器兼容。火狐浏览器下功能键将触发keypress事件 火狐下keypress的charcode有值,keycode为0
|
||||
if (nativeEvtName === 'keypress' && uniqueCharCode(nativeEvent) === 0) {
|
||||
): ListenerUnitList {
|
||||
const horizonEvtName = addOnPrefix(CommonEventToHorizonMap[nativeEvtName]);
|
||||
if (!horizonEvtName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -52,49 +42,42 @@ function getCommonListeners(
|
|||
if (nativeEvtName === 'focusin') {
|
||||
nativeEvtName = 'focus';
|
||||
}
|
||||
|
||||
if (nativeEvtName === 'focusout') {
|
||||
nativeEvtName = 'blur';
|
||||
}
|
||||
|
||||
const customEvent = createCommonCustomEvent(customEventName, nativeEvtName, nativeEvent, null, target);
|
||||
const horizonEvent = createCustomEvent(horizonEvtName, nativeEvtName, nativeEvent, null, target);
|
||||
return getListenersFromTree(
|
||||
vNode,
|
||||
customEventName,
|
||||
customEvent,
|
||||
horizonEvtName,
|
||||
horizonEvent,
|
||||
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
|
||||
);
|
||||
}
|
||||
|
||||
// 按顺序执行事件队列
|
||||
export function processListeners(
|
||||
processingEventsList: ProcessingListenerList
|
||||
): void {
|
||||
processingEventsList.forEach(eventUnitList => {
|
||||
let lastVNode;
|
||||
eventUnitList.forEach(eventUnit => {
|
||||
const { vNode, currentTarget, listener, event } = eventUnit;
|
||||
if (vNode !== lastVNode && event.isPropagationStopped()) {
|
||||
return;
|
||||
}
|
||||
event.currentTarget = currentTarget;
|
||||
runListenerAndCatchFirstError(listener, event);
|
||||
event.currentTarget = null;
|
||||
lastVNode = vNode;
|
||||
});
|
||||
function processListeners(listenerList: ListenerUnitList): void {
|
||||
listenerList.forEach(eventUnit => {
|
||||
const { currentTarget, listener, event } = eventUnit;
|
||||
if (event.isPropagationStopped()) {
|
||||
return;
|
||||
}
|
||||
event.currentTarget = currentTarget;
|
||||
listener(event);
|
||||
event.currentTarget = null;
|
||||
});
|
||||
// 执行所有事件后,重新throw遇到的第一个错误
|
||||
throwCaughtEventError();
|
||||
}
|
||||
|
||||
function getProcessListenersFacade(
|
||||
nativeEvtName: string,
|
||||
vNode: VNode,
|
||||
vNode: VNode | null,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
target,
|
||||
isCapture: boolean
|
||||
): ProcessingListenerList {
|
||||
): ListenerUnitList {
|
||||
// 触发普通委托事件
|
||||
let processingListenerList: ProcessingListenerList = getCommonListeners(
|
||||
let listenerList: ListenerUnitList = getCommonListeners(
|
||||
nativeEvtName,
|
||||
vNode,
|
||||
nativeEvent,
|
||||
|
@ -105,7 +88,7 @@ function getProcessListenersFacade(
|
|||
// 触发特殊handler委托事件
|
||||
if (!isCapture) {
|
||||
if (horizonEventToNativeMap.get('onChange').includes(nativeEvtName)) {
|
||||
processingListenerList = processingListenerList.concat(getChangeListeners(
|
||||
listenerList = listenerList.concat(getChangeListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
|
@ -114,7 +97,7 @@ function getProcessListenersFacade(
|
|||
}
|
||||
|
||||
if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) {
|
||||
processingListenerList = processingListenerList.concat(getSelectionListeners(
|
||||
listenerList = listenerList.concat(getSelectionListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
|
@ -125,7 +108,7 @@ function getProcessListenersFacade(
|
|||
if (nativeEvtName === 'compositionend' ||
|
||||
nativeEvtName === 'compositionstart' ||
|
||||
nativeEvtName === 'compositionupdate') {
|
||||
processingListenerList = processingListenerList.concat(getCompositionListeners(
|
||||
listenerList = listenerList.concat(getCompositionListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
|
@ -134,7 +117,7 @@ function getProcessListenersFacade(
|
|||
}
|
||||
|
||||
if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) {
|
||||
processingListenerList = processingListenerList.concat(getBeforeInputListeners(
|
||||
listenerList = listenerList.concat(getBeforeInputListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
|
@ -142,7 +125,7 @@ function getProcessListenersFacade(
|
|||
));
|
||||
}
|
||||
}
|
||||
return processingListenerList;
|
||||
return listenerList;
|
||||
}
|
||||
|
||||
// 触发可以被执行的horizon事件监听
|
||||
|
@ -150,55 +133,47 @@ function triggerHorizonEvents(
|
|||
nativeEvtName: string,
|
||||
isCapture: boolean,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
vNode: VNode | null,
|
||||
): void {
|
||||
const nativeEventTarget = getEventTarget(nativeEvent);
|
||||
const processingListenerList = getProcessListenersFacade(
|
||||
nativeEvtName,
|
||||
vNode,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
isCapture);
|
||||
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
|
||||
|
||||
// 获取委托事件队列
|
||||
const listenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
||||
|
||||
// 处理触发的事件队列
|
||||
processListeners(processingListenerList);
|
||||
processListeners(listenerList);
|
||||
}
|
||||
|
||||
|
||||
// 其他事件正在执行中标记
|
||||
let isInEventsExecution = false;
|
||||
|
||||
// 处理委托事件入口
|
||||
export function handleEventMain(
|
||||
nativeEvtName: string,
|
||||
isCapture: boolean,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: EventTarget,
|
||||
targetContainer: EventTarget,
|
||||
): void {
|
||||
let rootVNode = vNode;
|
||||
if (vNode !== null) {
|
||||
rootVNode = getExactNode(vNode, target);
|
||||
if (!rootVNode) {
|
||||
let startVNode = vNode;
|
||||
if (startVNode !== null) {
|
||||
startVNode = getExactNode(startVNode, targetContainer);
|
||||
if (!startVNode) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 有事件正在执行,同步执行事件
|
||||
if (isInEventsExecution) {
|
||||
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
|
||||
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// 没有事件在执行,经过调度再执行事件
|
||||
isInEventsExecution = true;
|
||||
try {
|
||||
asyncUpdates(() =>
|
||||
triggerHorizonEvents(
|
||||
nativeEvtName,
|
||||
isCapture,
|
||||
nativeEvent,
|
||||
rootVNode,
|
||||
));
|
||||
asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode));
|
||||
} finally {
|
||||
isInEventsExecution = false;
|
||||
if (shouldUpdateValue()) {
|
||||
|
|
|
@ -1,73 +1,20 @@
|
|||
import {VNode} from '../renderer/Types';
|
||||
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 {ProcessingListenerList, ListenerUnitList} from './Types';
|
||||
import {ListenerUnitList} from './Types';
|
||||
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(
|
||||
targetVNode: VNode | null,
|
||||
name: string | null,
|
||||
horizonEvtName: string | null,
|
||||
horizonEvent: CustomBaseEvent,
|
||||
eventType: string,
|
||||
): ProcessingListenerList {
|
||||
if (!name) {
|
||||
): ListenerUnitList {
|
||||
if (!horizonEvtName) {
|
||||
return [];
|
||||
}
|
||||
const captureName = name + EVENT_TYPE_CAPTURE;
|
||||
|
||||
const listeners: ListenerUnitList = [];
|
||||
|
||||
let vNode = targetVNode;
|
||||
|
@ -77,7 +24,8 @@ export function getListenersFromTree(
|
|||
const {realNode, tag} = vNode;
|
||||
if (tag === DomComponent && realNode !== null) {
|
||||
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) {
|
||||
listeners.unshift({
|
||||
vNode,
|
||||
|
@ -87,8 +35,9 @@ export function getListenersFromTree(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) {
|
||||
const bubbleListener = getListener(vNode, name);
|
||||
const bubbleListener = vNode.props[horizonEvtName];
|
||||
if (bubbleListener) {
|
||||
listeners.push({
|
||||
vNode,
|
||||
|
@ -101,7 +50,8 @@ export function getListenersFromTree(
|
|||
}
|
||||
vNode = vNode.parent;
|
||||
}
|
||||
return listeners.length > 0 ? [listeners]: [];
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,5 +12,3 @@ export type ListenerUnit = {
|
|||
};
|
||||
|
||||
export type ListenerUnitList = Array<ListenerUnit>;
|
||||
|
||||
export type ProcessingListenerList = Array<ListenerUnitList>;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -34,7 +34,7 @@ export const horizonEventToNativeMap = new Map([
|
|||
['onCompositionStart', ['compositionstart']],
|
||||
['onCompositionUpdate', ['compositionupdate']],
|
||||
['onBeforeInput', ['compositionend', 'keypress', 'textInput']],
|
||||
['onChange', ['change', 'click', 'focusout', 'input',]],
|
||||
['onChange', ['change', 'click', 'focusout', 'input']],
|
||||
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
|
||||
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
|
||||
|
||||
|
|
|
@ -6,37 +6,17 @@ import {VNode} from '../../renderer/Types';
|
|||
|
||||
// 从原生事件中复制属性到自定义事件中
|
||||
function extendAttribute(target, source) {
|
||||
const attributes = [
|
||||
// AnimationEvent
|
||||
'animationName', 'elapsedTime', 'pseudoElement',
|
||||
// CompositionEvent、InputEvent
|
||||
'data',
|
||||
// DragEvent
|
||||
'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',
|
||||
];
|
||||
let val;
|
||||
let attr;
|
||||
for (attr in source) {
|
||||
// 这两个方法需要override
|
||||
if (attr === 'preventDefault' || attr === 'stopPropagation') {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.forEach(attr => {
|
||||
if (typeof source[attr] !== 'undefined') {
|
||||
if (typeof source[attr] === 'function') {
|
||||
val = source[attr];
|
||||
if (val !== undefined) {
|
||||
if (typeof val === 'function') {
|
||||
target[attr] = function() {
|
||||
return source[attr].apply(source, arguments);
|
||||
};
|
||||
|
@ -44,54 +24,71 @@ function extendAttribute(target, source) {
|
|||
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 {
|
||||
|
||||
data: string;
|
||||
defaultPrevented: boolean;
|
||||
customEventName: string;
|
||||
targetVNode: VNode;
|
||||
type: string;
|
||||
nativeEvent: any;
|
||||
target: EventTarget;
|
||||
timeStamp: number;
|
||||
isDefaultPrevented: () => boolean;
|
||||
isPropagationStopped: () => boolean;
|
||||
currentTarget: EventTarget;
|
||||
relatedTarget: EventTarget;
|
||||
|
||||
// custom事件自定义属性
|
||||
customEventName: string;
|
||||
targetVNode: VNode;
|
||||
type: string;
|
||||
timeStamp: number;
|
||||
nativeEvent: any;
|
||||
|
||||
// 键盘事件属性
|
||||
key: string;
|
||||
charCode: number;
|
||||
keyCode: number;
|
||||
which: number;
|
||||
|
||||
constructor(
|
||||
customEvtName: string | null,
|
||||
nativeEvtName: string,
|
||||
nativeEvt: { [propName: string]: any },
|
||||
vNode: VNode,
|
||||
vNode: VNode | null,
|
||||
target: null | EventTarget
|
||||
) {
|
||||
// 复制原生属性到自定义事件
|
||||
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事件自定义属性
|
||||
this.customEventName = customEvtName;
|
||||
this.targetVNode = vNode;
|
||||
this.type = nativeEvtName;
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
// 阻止默认行为
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,42 +1,8 @@
|
|||
import {CustomKeyboardEvent} from './CustomKeyboardEvent';
|
||||
import {CustomMouseEvent} from './CustomMouseEvent';
|
||||
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) {
|
||||
const EventConstructor = CommonEventToCustom[nativeEvtName] || CustomBaseEvent;
|
||||
return new EventConstructor(
|
||||
customEventName,
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
currentTarget,
|
||||
);
|
||||
}
|
||||
|
||||
// 创建模拟事件实例对象,需要handler特殊处理
|
||||
export function createHandlerCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) {
|
||||
return new CustomMouseEvent(
|
||||
export function createCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) {
|
||||
return new CustomBaseEvent(
|
||||
customEventName,
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import type {VNode} from '../../renderer/Types';
|
||||
import type {AnyNativeEvent, ProcessingListenerList} from '../Types';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import {createHandlerCustomEvent} from '../customEvents/EventFactory';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {CHAR_CODE_SPACE, EVENT_TYPE_ALL} from '../const';
|
||||
import {CustomBaseEvent} from '../customEvents/CustomBaseEvent';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE);
|
||||
|
||||
function getInputCharsByNative(
|
||||
|
@ -28,14 +29,14 @@ export function getListeners(
|
|||
nativeEvent: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ProcessingListenerList {
|
||||
): ListenerUnitList {
|
||||
const chars = getInputCharsByNative(nativeEvtName, nativeEvent);
|
||||
// 无字符将要输入,无需处理
|
||||
if (!chars) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const event: CustomBaseEvent = createHandlerCustomEvent(
|
||||
const event: CustomBaseEvent = createCustomEvent(
|
||||
'onBeforeInput',
|
||||
'beforeinput',
|
||||
nativeEvent,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {createHandlerCustomEvent} from '../customEvents/EventFactory';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {getDom} from '../../dom/DOMInternalKeys';
|
||||
import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler';
|
||||
import {addValueUpdateList} from '../ControlledValueUpdater';
|
||||
import {isTextInputElement} from '../utils';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {AnyNativeEvent, ProcessingListenerList} from '../Types';
|
||||
import {AnyNativeEvent, ListenerUnitList} from '../Types';
|
||||
import {
|
||||
getListenersFromTree,
|
||||
} from '../ListenerGetter';
|
||||
|
@ -39,7 +39,7 @@ export function getListeners(
|
|||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ProcessingListenerList {
|
||||
): ListenerUnitList {
|
||||
if (!vNode) {
|
||||
return [];
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ export function getListeners(
|
|||
// 判断是否需要触发change事件
|
||||
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
|
||||
addValueUpdateList(target);
|
||||
const event = createHandlerCustomEvent(
|
||||
const event = createCustomEvent(
|
||||
'onChange',
|
||||
'change',
|
||||
nativeEvt,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type {VNode} from '../../renderer/Types';
|
||||
import type {AnyNativeEvent, ProcessingListenerList} from '../Types';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import {createHandlerCustomEvent} from '../customEvents/EventFactory';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
|
||||
const compositionEventObj = {
|
||||
compositionstart: 'onCompositionStart',
|
||||
|
@ -16,10 +17,10 @@ export function getListeners(
|
|||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ProcessingListenerList {
|
||||
): ListenerUnitList {
|
||||
const evtType = compositionEventObj[evtName];
|
||||
|
||||
const event = createHandlerCustomEvent(
|
||||
const event = createCustomEvent(
|
||||
evtType,
|
||||
evtName,
|
||||
nativeEvt,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {createHandlerCustomEvent} from '../customEvents/EventFactory';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {shallowCompare} from '../../renderer/utils/compare';
|
||||
import {getFocusedDom} from '../../dom/utils/Common';
|
||||
import {getDom} from '../../dom/DOMInternalKeys';
|
||||
import {isDocument} from '../../dom/utils/Common';
|
||||
import {isTextInputElement} from '../utils';
|
||||
import type {AnyNativeEvent, ProcessingListenerList} from '../Types';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import type {VNode} from '../../renderer/Types';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
|
||||
const horizonEventName = 'onSelect'
|
||||
|
||||
|
@ -53,7 +54,7 @@ function getSelectEvent(nativeEvent, target) {
|
|||
if (!shallowCompare(lastSelection, currentSelection)) {
|
||||
lastSelection = currentSelection;
|
||||
|
||||
const event = createHandlerCustomEvent(
|
||||
const event = createCustomEvent(
|
||||
horizonEventName,
|
||||
'select',
|
||||
nativeEvent,
|
||||
|
@ -83,9 +84,9 @@ export function getListeners(
|
|||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ProcessingListenerList {
|
||||
): ListenerUnitList {
|
||||
const targetNode = vNode ? getDom(vNode) : window;
|
||||
let eventUnitList: ProcessingListenerList = [];
|
||||
let eventUnitList: ListenerUnitList = [];
|
||||
switch (name) {
|
||||
case 'focusin':
|
||||
initTargetCache(targetNode, vNode);
|
||||
|
|
|
@ -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',
|
||||
|
@ -46,11 +14,9 @@ export function isTextInputElement(dom?: HTMLElement): boolean {
|
|||
|
||||
|
||||
// 例:dragEnd -> onDragEnd
|
||||
export function getCustomEventNameWithOn(name) {
|
||||
export function addOnPrefix(name) {
|
||||
if (!name) {
|
||||
return '';
|
||||
}
|
||||
const capitalizedEvent = name[0].toUpperCase() + name.slice(1);
|
||||
const horizonEventName = 'on' + capitalizedEvent;
|
||||
return horizonEventName;
|
||||
return 'on' + name[0].toUpperCase() + name.slice(1);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ function mergeDefault(sourceObj, defaultObj) {
|
|||
});
|
||||
}
|
||||
|
||||
function buildElement(isClone, type, setting, ...children) {
|
||||
function buildElement(isClone, type, setting, children) {
|
||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 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);
|
||||
|
@ -45,11 +45,14 @@ function buildElement(isClone, type, setting, ...children) {
|
|||
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
|
||||
|
||||
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)) {
|
||||
props[k] = setting[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (setting.ref !== undefined && isClone) {
|
||||
vNode = getProcessingClassVNode();
|
||||
}
|
||||
|
@ -69,11 +72,11 @@ function buildElement(isClone, type, setting, ...children) {
|
|||
|
||||
// 创建Element结构体,供JSX编译时调用
|
||||
export function createElement(type, setting, ...children) {
|
||||
return buildElement(false, type, setting, ...children);
|
||||
return buildElement(false, type, setting, children);
|
||||
}
|
||||
|
||||
export function cloneElement(element, setting, ...children) {
|
||||
return buildElement(true, element, setting, ...children);
|
||||
return buildElement(true, element, setting, children);
|
||||
}
|
||||
|
||||
// 检测结构体是否为合法的Element
|
||||
|
|
|
@ -8,10 +8,11 @@ import type {Update} from './UpdateHandler';
|
|||
import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
|
||||
import {FlagUtils, Interrupted} from './vnode/VNodeFlags';
|
||||
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
|
||||
import {launchUpdateFromVNode, setBuildResultError, tryRenderRoot} from './TreeBuilder';
|
||||
import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder';
|
||||
import {setRootThrowError} from './submit/Submit';
|
||||
import {handleSuspenseChildThrowError} from './render/SuspenseComponent';
|
||||
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
|
||||
import {BuildErrored, setBuildResult} from './GlobalVar';
|
||||
|
||||
function consoleError(error: any): void {
|
||||
if (isDev) {
|
||||
|
@ -82,7 +83,7 @@ export function handleRenderThrowError(
|
|||
}
|
||||
|
||||
// 抛出错误无法作为suspense内容处理(或无suspense来处理),这次当成真的错误来处理
|
||||
setBuildResultError();
|
||||
setBuildResult(BuildErrored);
|
||||
|
||||
// 向上遍历寻找ClassComponent组件(同时也是Error Boundaries组件) 或者 TreeRoot
|
||||
let vNode = sourceVNode.parent;
|
||||
|
@ -133,7 +134,7 @@ function triggerUpdate(vNode, state) {
|
|||
|
||||
const root = updateShouldUpdateOfTree(vNode);
|
||||
if (root !== null) {
|
||||
tryRenderRoot(root);
|
||||
tryRenderFromRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
export const ByAsync = 'BY_ASYNC';
|
||||
export const BySync = 'BY_SYNC';
|
||||
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 = {
|
||||
[ByAsync]: false,
|
||||
[BySync]: false,
|
||||
[InRender]: false,
|
||||
[InEvent]: false,
|
||||
};
|
||||
|
||||
export function changeMode(mode: RenderMode, state = true) {
|
||||
|
@ -21,7 +23,7 @@ export function checkMode(mode: RenderMode) {
|
|||
}
|
||||
|
||||
export function isExecuting() {
|
||||
return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender];
|
||||
return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender] || executeMode[InEvent];
|
||||
}
|
||||
|
||||
export function copyExecuteMode() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type {VNode} from './Types';
|
||||
|
||||
// 当前处理的classVNode,用于inst.refs用法中的
|
||||
// 当前处理的classVNode,用于设置inst.refs
|
||||
let processingClassVNode: VNode | null = null;
|
||||
export function getProcessingClassVNode(): VNode | null {
|
||||
return processingClassVNode;
|
||||
|
|
|
@ -9,9 +9,9 @@ import {
|
|||
} from './TreeBuilder';
|
||||
import { runAsyncEffects } from './submit/HookEffectHandler';
|
||||
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 {
|
||||
asyncUpdates,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { runAsyncEffects } from './submit/HookEffectHandler';
|
|||
import { handleRenderThrowError } from './ErrorHandler';
|
||||
import componentRenders from './render';
|
||||
import {
|
||||
BuildCompleted, BuildErrored,
|
||||
BuildCompleted,
|
||||
BuildFatalErrored,
|
||||
BuildInComplete, getBuildResult,
|
||||
getStartVNode,
|
||||
|
@ -22,10 +22,11 @@ import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils';
|
|||
import {
|
||||
ByAsync,
|
||||
BySync,
|
||||
InRender,
|
||||
InEvent,
|
||||
changeMode,
|
||||
checkMode,
|
||||
copyExecuteMode,
|
||||
InRender,
|
||||
isExecuting,
|
||||
setExecuteMode
|
||||
} from './ExecuteMode';
|
||||
|
@ -36,12 +37,11 @@ import {
|
|||
updateShouldUpdateOfTree
|
||||
} from './vnode/VNodeShouldUpdate';
|
||||
|
||||
// 当前运行的vNode节点
|
||||
let processing: VNode | null = null;
|
||||
|
||||
// 不可恢复错误
|
||||
let unrecoverableErrorDuringBuild: any = null;
|
||||
|
||||
// 当前运行的vNode节点
|
||||
let processing: VNode | null = null;
|
||||
export function setProcessing(vNode: VNode | null) {
|
||||
processing = vNode;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ function collectDirtyNodes(vNode: VNode, parent: VNode): void {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================== 向上递归 ==============================
|
||||
// ============================== 向上冒泡 ==============================
|
||||
|
||||
// 尝试完成当前工作单元,然后移动到下一个兄弟工作单元。如果没有更多的同级,请返回父vNode。
|
||||
function bubbleVNode(vNode: VNode): void {
|
||||
|
@ -201,18 +201,18 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
changeMode(InRender, true);
|
||||
|
||||
// 计算出开始节点
|
||||
const startUpdateVNode = calcStartUpdateVNode(treeRoot);
|
||||
const startVNode = calcStartUpdateVNode(treeRoot);
|
||||
// 缓存起来
|
||||
setStartVNode(startUpdateVNode);
|
||||
setStartVNode(startVNode);
|
||||
|
||||
// 清空toUpdateNodes
|
||||
treeRoot.toUpdateNodes.clear();
|
||||
|
||||
if (startUpdateVNode.tag !== TreeRoot) { // 不是根节点
|
||||
if (startVNode.tag !== TreeRoot) { // 不是根节点
|
||||
// 设置namespace,用于createElement
|
||||
const parentObj = findDomParent(startUpdateVNode);
|
||||
const parentObj = findDomParent(startVNode);
|
||||
|
||||
// 当在componentWillUnmount中调用setState,parent可能是null,因为startUpdateVNode会被clear
|
||||
// 当在componentWillUnmount中调用setState,parent可能是null,因为startVNode会被clear
|
||||
if (parentObj !== null) {
|
||||
const domParent = parentObj.parent;
|
||||
resetNamespaceCtx(domParent);
|
||||
|
@ -220,20 +220,20 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
}
|
||||
|
||||
// 恢复父节点的context
|
||||
recoverParentsContextCtx(startUpdateVNode);
|
||||
recoverParentsContextCtx(startVNode);
|
||||
}
|
||||
|
||||
// 重置环境变量,为重新进行深度遍历做准备
|
||||
resetProcessingVariables(startUpdateVNode);
|
||||
resetProcessingVariables(startVNode);
|
||||
|
||||
do {
|
||||
while (processing !== null) {
|
||||
try {
|
||||
while (processing !== null) {
|
||||
// 捕获创建 vNodes
|
||||
const next = captureVNode(processing);
|
||||
|
||||
if (next === null) {
|
||||
// 如果没有产生新的,那么就完成当前节点,向上遍历
|
||||
// 如果没有子节点,那么就完成当前节点,开始冒泡
|
||||
bubbleVNode(processing);
|
||||
} else {
|
||||
processing = next;
|
||||
|
@ -241,14 +241,10 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
}
|
||||
|
||||
setProcessingClassVNode(null);
|
||||
|
||||
break;
|
||||
} catch (thrownValue) {
|
||||
handleError(treeRoot, thrownValue);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
processing = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
// 任务放进queue,但是调度开始还是异步的
|
||||
treeRoot.task = pushRenderCallback(
|
||||
|
@ -304,7 +300,7 @@ export function launchUpdateFromVNode(vNode: VNode) {
|
|||
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
|
||||
renderFromRoot(treeRoot);
|
||||
} else {
|
||||
tryRenderRoot(treeRoot);
|
||||
tryRenderFromRoot(treeRoot);
|
||||
|
||||
if (!isExecuting()) {
|
||||
// 同步执行
|
||||
|
@ -313,12 +309,6 @@ export function launchUpdateFromVNode(vNode: VNode) {
|
|||
}
|
||||
}
|
||||
|
||||
export function setBuildResultError() {
|
||||
if (getBuildResult() !== BuildCompleted) {
|
||||
setBuildResult(BuildErrored);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== HorizonDOM使用 ==============================
|
||||
export function runDiscreteUpdates() {
|
||||
if (checkMode(ByAsync) || checkMode(InRender)) {
|
||||
|
@ -331,7 +321,7 @@ export function runDiscreteUpdates() {
|
|||
|
||||
export function asyncUpdates(fn, ...param) {
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(ByAsync, true);
|
||||
changeMode(InEvent, true);
|
||||
try {
|
||||
return fn(...param);
|
||||
} finally {
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { VNode } from '../Types';
|
|||
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
|
||||
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 {
|
||||
isSameType,
|
||||
getIteratorFn,
|
||||
|
@ -113,7 +113,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
switch (newNodeType) {
|
||||
case DiffCategory.TEXT_NODE: {
|
||||
if (oldNode === null || oldNode.tag !== DomText) {
|
||||
resultNode = createVNode(DomText, String(newChild));
|
||||
resultNode = createDomTextVNode(String(newChild));
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, String(newChild));
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
}
|
||||
case DiffCategory.ARR_NODE: {
|
||||
if (oldNode === null || oldNode.tag !== Fragment) {
|
||||
resultNode = createVNode(Fragment, null, newChild);
|
||||
resultNode = createFragmentVNode(null, newChild);
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, newChild);
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
if (newChild.type === TYPE_FRAGMENT) {
|
||||
if (oldNode === null || oldNode.tag !== Fragment) {
|
||||
const key = oldNode !== null ? oldNode.key : newChild.key;
|
||||
resultNode = createVNode(Fragment, key, newChild.props.children);
|
||||
resultNode = createFragmentVNode(key, newChild.props.children);
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, newChild);
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
break;
|
||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||
if (oldNode === null || oldNode.tag !== DomPortal || oldNode.outerDom !== newChild.outerDom) {
|
||||
resultNode = createVNode(DomPortal, newChild);
|
||||
resultNode = createPortalVNode(newChild);
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, newChild.children || []);
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ function diffStringNodeHandler(
|
|||
deleteVNodes(parentNode, firstChildVNode.next);
|
||||
newTextNode.next = null;
|
||||
} else {
|
||||
newTextNode = createVNode(DomText, String(newChild));
|
||||
newTextNode = createDomTextVNode(String(newChild));
|
||||
deleteVNodes(parentNode, firstChildVNode);
|
||||
}
|
||||
|
||||
|
@ -562,7 +562,7 @@ function diffObjectNodeHandler(
|
|||
if (resultNode === null) {
|
||||
// 新建
|
||||
if (newChild.type === TYPE_FRAGMENT) {
|
||||
resultNode = createVNode(Fragment, newChild.key, newChild.props.children);
|
||||
resultNode = createFragmentVNode(newChild.key, newChild.props.children);
|
||||
} else {
|
||||
resultNode = createVNodeFromElement(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
|
@ -580,7 +580,7 @@ function diffObjectNodeHandler(
|
|||
}
|
||||
if (resultNode === null) {
|
||||
// 新建
|
||||
resultNode = createVNode(DomPortal, newChild);
|
||||
resultNode = createPortalVNode(newChild);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
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 {
|
||||
TYPE_FRAGMENT,
|
||||
TYPE_PROFILER,
|
||||
TYPE_STRICT_MODE,
|
||||
} from '../../external/JSXElementType';
|
||||
import {Fragment} from '../vnode/VNodeTags';
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
|
@ -24,9 +23,9 @@ export function captureMemoComponent(
|
|||
let newChild = null;
|
||||
const type = Component.type;
|
||||
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
|
||||
newChild = createVNode(Fragment, null, newProps.children);
|
||||
newChild = createFragmentVNode(null, newProps.children);
|
||||
} else {
|
||||
newChild = createVNode('props', type, null, newProps, processing);
|
||||
newChild = createUndeterminedVNode(type, null, newProps);
|
||||
}
|
||||
newChild.parent = processing;
|
||||
newChild.ref = processing.ref;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import type {VNode, PromiseType} from '../Types';
|
||||
|
||||
import {FlagUtils, Interrupted} from '../vnode/VNodeFlags';
|
||||
import {createVNode, onlyUpdateChildVNodes, updateVNode, updateVNodePath} from '../vnode/VNodeCreator';
|
||||
import {onlyUpdateChildVNodes, updateVNode, updateVNodePath, createFragmentVNode} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
ClassComponent,
|
||||
IncompleteClassComponent,
|
||||
SuspenseComponent,
|
||||
Fragment,
|
||||
} from '../vnode/VNodeTags';
|
||||
import {pushForceUpdate} from '../UpdateHandler';
|
||||
import {launchUpdateFromVNode, tryRenderRoot} from '../TreeBuilder';
|
||||
import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder';
|
||||
import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate';
|
||||
import {getContextChangeCtx} from '../ContextSaver';
|
||||
|
||||
|
@ -31,12 +30,12 @@ function createFallback(processing: VNode, fallbackChildren) {
|
|||
if (oldFallbackFragment !== null) {
|
||||
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
|
||||
} else {
|
||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||
fallbackFragment = createFragmentVNode(null, fallbackChildren);
|
||||
FlagUtils.markAddition(fallbackFragment);
|
||||
}
|
||||
} else {
|
||||
// 创建
|
||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||
fallbackFragment = createFragmentVNode(null, fallbackChildren);
|
||||
}
|
||||
|
||||
processing.child = childFragment;
|
||||
|
@ -72,7 +71,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
|
|||
// SuspenseComponent 中使用
|
||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowChild;
|
||||
} else {
|
||||
childFragment = createVNode(Fragment, null, newChildren);
|
||||
childFragment = createFragmentVNode(null, newChildren);
|
||||
}
|
||||
|
||||
childFragment.parent = processing;
|
||||
|
@ -210,7 +209,7 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
|||
suspenseVNode.promiseResolve = true;
|
||||
const root = updateShouldUpdateOfTree(suspenseVNode);
|
||||
if (root !== null) {
|
||||
tryRenderRoot(root);
|
||||
tryRenderFromRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,22 +7,16 @@ import type {
|
|||
Effect as HookEffect,
|
||||
EffectList,
|
||||
} from '../hooks/HookType';
|
||||
import {
|
||||
callRenderQueueImmediate,
|
||||
} from '../taskExecutor/RenderQueue';
|
||||
import {runAsync} from '../taskExecutor/TaskExecutor';
|
||||
import {
|
||||
copyExecuteMode, InRender, setExecuteMode,changeMode
|
||||
} from '../ExecuteMode';
|
||||
import {handleSubmitError} from '../ErrorHandler';
|
||||
import {clearDirtyNodes} from './Submit';
|
||||
import {EffectConstant} from '../hooks/EffectConstant';
|
||||
|
||||
let hookEffects: Array<HookEffect | VNode> = [];
|
||||
let hookRemoveEffects: Array<HookEffect | VNode> = [];
|
||||
// 是否正在异步调度effects
|
||||
let isScheduling: boolean = false;
|
||||
let hookEffectRoot: VNode | null = null;
|
||||
|
||||
export function setSchedulingEffects(value) {
|
||||
isScheduling = value;
|
||||
|
@ -31,13 +25,6 @@ export function isSchedulingEffects() {
|
|||
return isScheduling;
|
||||
}
|
||||
|
||||
export function setHookEffectRoot(root: VNode | null) {
|
||||
hookEffectRoot = root;
|
||||
}
|
||||
export function getHookEffectRoot() {
|
||||
return hookEffectRoot;
|
||||
}
|
||||
|
||||
export function callUseEffects(vNode: VNode) {
|
||||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
|
@ -47,8 +34,8 @@ export function callUseEffects(vNode: VNode) {
|
|||
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
|
||||
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
|
||||
) {
|
||||
hookEffects.push({effect, vNode});
|
||||
hookRemoveEffects.push({effect, vNode});
|
||||
hookEffects.push(effect);
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
// 异步调用
|
||||
if (!isScheduling) {
|
||||
|
@ -60,20 +47,13 @@ export function callUseEffects(vNode: VNode) {
|
|||
}
|
||||
|
||||
export function runAsyncEffects() {
|
||||
if (hookEffectRoot === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const root = hookEffectRoot;
|
||||
hookEffectRoot = null;
|
||||
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(InRender, true);
|
||||
|
||||
// 调用effect destroy
|
||||
const removeEffects = hookRemoveEffects;
|
||||
hookRemoveEffects = [];
|
||||
removeEffects.forEach(({effect, vNode}) => {
|
||||
removeEffects.forEach((effect) => {
|
||||
const destroy = effect.removeEffect;
|
||||
effect.removeEffect = undefined;
|
||||
|
||||
|
@ -81,7 +61,7 @@ export function runAsyncEffects() {
|
|||
try {
|
||||
destroy();
|
||||
} catch (error) {
|
||||
handleSubmitError(vNode, error);
|
||||
// 不处理副作用阶段抛出的异常
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -89,24 +69,17 @@ export function runAsyncEffects() {
|
|||
// 调用effect create
|
||||
const createEffects = hookEffects;
|
||||
hookEffects = [];
|
||||
createEffects.forEach(({effect, vNode}) => {
|
||||
createEffects.forEach((effect) => {
|
||||
try {
|
||||
const create = effect.effect;
|
||||
|
||||
effect.removeEffect = create();
|
||||
} catch (error) {
|
||||
handleSubmitError(vNode, error);
|
||||
// 不处理副作用阶段抛出的异常
|
||||
}
|
||||
});
|
||||
|
||||
// 清理dirtyNodes
|
||||
clearDirtyNodes(root.dirtyNodes);
|
||||
|
||||
setExecuteMode(preMode);
|
||||
|
||||
callRenderQueueImmediate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 在销毁vNode的时候调用remove
|
||||
|
@ -118,7 +91,7 @@ export function callEffectRemove(vNode: VNode) {
|
|||
|
||||
if (removeEffect !== undefined) {
|
||||
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect,就异步调用
|
||||
hookRemoveEffects.push({effect, vNode});
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
if (!isScheduling) {
|
||||
isScheduling = true;
|
||||
|
@ -136,10 +109,10 @@ export function callUseLayoutEffectRemove(vNode: VNode) {
|
|||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||
effectList.forEach(item => {
|
||||
if ((item.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const remove = item.removeEffect;
|
||||
item.removeEffect = undefined;
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const remove = effect.removeEffect;
|
||||
effect.removeEffect = undefined;
|
||||
if (typeof remove === 'function') {
|
||||
remove();
|
||||
}
|
||||
|
@ -152,10 +125,10 @@ export function callUseLayoutEffectCreate(vNode: VNode) {
|
|||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||
effectList.forEach(item => {
|
||||
if ((item.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const create = item.effect;
|
||||
item.removeEffect = create();
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const create = effect.effect;
|
||||
effect.removeEffect = create();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import {
|
|||
removeChildDom,
|
||||
hideDom,
|
||||
unHideDom,
|
||||
clearContainer,
|
||||
} from '../../dom/DOMOperator';
|
||||
import {
|
||||
callEffectRemove,
|
||||
|
@ -56,28 +55,17 @@ function callComponentWillUnmount(vNode: VNode, instance: any) {
|
|||
function callBeforeSubmitLifeCycles(
|
||||
vNode: VNode,
|
||||
): void {
|
||||
switch (vNode.tag) {
|
||||
case ClassComponent: { // 调用instance.getSnapshotBeforeUpdate
|
||||
if (!vNode.isCreated) {
|
||||
const prevProps = vNode.isLazyComponent
|
||||
? mergeDefaultProps(vNode.type, vNode.oldProps)
|
||||
: vNode.oldProps;
|
||||
const prevState = vNode.oldState;
|
||||
const instance = vNode.realNode;
|
||||
if (vNode.tag === ClassComponent && !vNode.isCreated) { // 调用instance.getSnapshotBeforeUpdate
|
||||
const prevProps = vNode.isLazyComponent
|
||||
? mergeDefaultProps(vNode.type, vNode.oldProps)
|
||||
: vNode.oldProps;
|
||||
const prevState = vNode.oldState;
|
||||
const instance = vNode.realNode;
|
||||
|
||||
const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
|
||||
const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
|
||||
|
||||
// __snapshotResult会在调用componentDidUpdate的时候作为第三个参数
|
||||
instance.__snapshotResult = snapshot;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case TreeRoot: {
|
||||
const root = vNode.realNode;
|
||||
clearContainer(root.outerDom);
|
||||
}
|
||||
|
||||
// No Default
|
||||
// __snapshotResult会在调用componentDidUpdate的时候作为第三个参数
|
||||
instance.__snapshotResult = snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +126,6 @@ function callAfterSubmitLifeCycles(
|
|||
if (vNode.isCreated && vNode.flags.Update) {
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
if (shouldAutoFocus(vNode.type, vNode.props)) {
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
vNode.realNode.focus();
|
||||
}
|
||||
}
|
||||
|
@ -333,11 +320,12 @@ function submitClear(vNode: VNode): void {
|
|||
clearVNode(clearChild);
|
||||
clearChild = clearChild.next as VNode;
|
||||
}
|
||||
|
||||
|
||||
// 在所有子项都卸载后,删除dom树中的节点
|
||||
removeChildDom(currentParent, vNode.realNode);
|
||||
currentParent.append(cloneDom);
|
||||
vNode.realNode = cloneDom;
|
||||
attachRef(vNode);
|
||||
FlagUtils.removeFlag(vNode, Clear);
|
||||
vNode.clearChild = null;
|
||||
}
|
||||
|
|
|
@ -11,18 +11,16 @@ import {
|
|||
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
|
||||
submitResetTextContent, submitUpdate, detachRef, submitClear,
|
||||
} from './LifeCycleHandler';
|
||||
import {tryRenderRoot, setProcessing} from '../TreeBuilder';
|
||||
import {tryRenderFromRoot} from '../TreeBuilder';
|
||||
import {
|
||||
BySync,
|
||||
InRender,
|
||||
copyExecuteMode,
|
||||
setExecuteMode,
|
||||
checkMode,
|
||||
changeMode,
|
||||
} from '../ExecuteMode';
|
||||
import {
|
||||
isSchedulingEffects,
|
||||
setSchedulingEffects, setHookEffectRoot,
|
||||
setSchedulingEffects,
|
||||
} from './HookEffectHandler';
|
||||
import {getStartVNode} from '../GlobalVar';
|
||||
|
||||
|
@ -67,18 +65,13 @@ export function submitToRender(treeRoot) {
|
|||
|
||||
if (isSchedulingEffects()) {
|
||||
setSchedulingEffects(false);
|
||||
|
||||
// 记录root,说明这个root有副作用要执行
|
||||
setHookEffectRoot(treeRoot);
|
||||
} else {
|
||||
clearDirtyNodes(dirtyNodes);
|
||||
}
|
||||
|
||||
// 统计root同步重渲染的次数,如果太多可能是无线循环
|
||||
countLoopingUpdate(treeRoot);
|
||||
|
||||
// 在退出`submit` 之前始终调用此函数,以确保任何已计划在此根上执行的update被执行。
|
||||
tryRenderRoot(treeRoot);
|
||||
tryRenderFromRoot(treeRoot);
|
||||
|
||||
if (rootThrowError) {
|
||||
const error = rootThrowError;
|
||||
|
@ -86,11 +79,6 @@ export function submitToRender(treeRoot) {
|
|||
throw error;
|
||||
}
|
||||
|
||||
// 非批量:即同步执行的,没有必要去执行RenderQueue,RenderQueue放的是异步的
|
||||
if (!checkMode(BySync)) { // 非批量
|
||||
callRenderQueueImmediate();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -101,7 +89,6 @@ function beforeSubmit(dirtyNodes: Array<VNode>) {
|
|||
callBeforeSubmitLifeCycles(node);
|
||||
}
|
||||
} catch (error) {
|
||||
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||
handleSubmitError(node, error);
|
||||
}
|
||||
});
|
||||
|
@ -143,7 +130,6 @@ function submit(dirtyNodes: Array<VNode>) {
|
|||
}
|
||||
}
|
||||
} catch (error) {
|
||||
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||
handleSubmitError(node, error);
|
||||
}
|
||||
});
|
||||
|
@ -160,7 +146,6 @@ function afterSubmit(dirtyNodes: Array<VNode>) {
|
|||
attachRef(node);
|
||||
}
|
||||
} catch (error) {
|
||||
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,22 +28,14 @@ function callRenderQueue() {
|
|||
// 防止重入
|
||||
isCallingRenderQueue = true;
|
||||
|
||||
let i = 0;
|
||||
try {
|
||||
for (; i < renderQueue.length; i++) {
|
||||
let callback = renderQueue[i];
|
||||
let callback;
|
||||
while (callback = renderQueue.shift()) {
|
||||
callback();
|
||||
}
|
||||
|
||||
renderQueue = null;
|
||||
} catch (error) {
|
||||
// 如果有异常抛出,请将剩余的回调留在队列中
|
||||
if (renderQueue !== null) {
|
||||
renderQueue = renderQueue.slice(i + 1);
|
||||
}
|
||||
|
||||
// 在下一个异步中再调用
|
||||
runAsync(callRenderQueueImmediate, ImmediatePriority);
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
isCallingRenderQueue = false;
|
||||
|
|
|
@ -87,59 +87,71 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
|
|||
function getVNodeTag(type: any) {
|
||||
let vNodeTag = ClsOrFunComponent;
|
||||
let isLazy = false;
|
||||
const componentType = typeof type;
|
||||
|
||||
if (typeof type === 'function') {
|
||||
if (componentType === 'function') {
|
||||
if (isClassComponent(type)) {
|
||||
vNodeTag = ClassComponent;
|
||||
}
|
||||
} else if (typeof type === 'string') {
|
||||
} else if (componentType === 'string') {
|
||||
vNodeTag = DomComponent;
|
||||
} else if (type === TYPE_SUSPENSE) {
|
||||
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];
|
||||
isLazy = type.vtype === TYPE_LAZY;
|
||||
} 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 };
|
||||
}
|
||||
|
||||
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) {
|
||||
let vNode = null;
|
||||
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:
|
||||
// 创建treeRoot
|
||||
vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]);
|
||||
|
@ -162,9 +174,9 @@ export function createVNodeFromElement(element: JSXElement): VNode {
|
|||
const props = element.props;
|
||||
|
||||
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
|
||||
return createVNode(Fragment, key, props.children);
|
||||
return createFragmentVNode(key, props.children);
|
||||
} else {
|
||||
return createVNode('props', type, key, props);
|
||||
return createUndeterminedVNode(type, key, props);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,14 +39,16 @@ export class FlagUtils {
|
|||
});
|
||||
}
|
||||
static hasAnyFlag(node: VNode) { // 有标志位
|
||||
let keyFlag = false;
|
||||
FlagArr.forEach(key => {
|
||||
if (node.flags[key]) {
|
||||
keyFlag = true;
|
||||
return;
|
||||
const flags = node.flags;
|
||||
const arrLength = FlagArr.length;
|
||||
let key;
|
||||
for (let i = 0; i < arrLength; i++) {
|
||||
key = FlagArr[i];
|
||||
if (flags[key]) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return keyFlag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static setNoFlags(node: VNode) {
|
||||
|
@ -91,7 +93,6 @@ export class FlagUtils {
|
|||
static markForceUpdate(node: VNode) {
|
||||
node.flags.ForceUpdate = true;
|
||||
}
|
||||
|
||||
static markClear(node: VNode) {
|
||||
node.flags.Clear = true;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ export function updateParentsChildShouldUpdate(vNode: VNode) {
|
|||
let isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate;
|
||||
|
||||
if (isShouldUpdate) { // 开始节点是shouldUpdate或childShouldUpdate
|
||||
// 更新从当前节点到根节点的childShouldUpdate为true
|
||||
setParentsChildShouldUpdate(node);
|
||||
} else {
|
||||
while (node !== null) {
|
||||
|
|
|
@ -81,7 +81,6 @@ export function clearVNode(vNode: VNode) {
|
|||
vNode.next = null;
|
||||
vNode.depContexts = [];
|
||||
vNode.dirtyNodes = [];
|
||||
vNode.oldProps = null;
|
||||
vNode.state = null;
|
||||
vNode.hooks = [];
|
||||
vNode.suspenseChildStatus = '';
|
||||
|
@ -91,7 +90,9 @@ export function clearVNode(vNode: VNode) {
|
|||
vNode.changeList = null;
|
||||
vNode.effectList = [];
|
||||
vNode.updates = null;
|
||||
vNode.realNode = null;
|
||||
|
||||
vNode.oldProps = null;
|
||||
vNode.oldHooks = [];
|
||||
vNode.oldState = null;
|
||||
vNode.oldRef = null;
|
||||
|
@ -260,14 +261,14 @@ export function getExactNode(targetVNode, targetContainer) {
|
|||
if (isPortalRoot(vNode, targetContainer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (container !== null) {
|
||||
const parentNode = getNearestVNode(container);
|
||||
if (parentNode === null) {
|
||||
return null;
|
||||
}
|
||||
if (parentNode.tag === DomComponent || parentNode.tag === DomText) {
|
||||
vNode = parentNode;
|
||||
return getExactNode(vNode, targetContainer);
|
||||
return getExactNode(parentNode, targetContainer);
|
||||
}
|
||||
container = container.parentNode;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue