Match-id-fda5629a5eba25fc511063c1cebf7752506caf2d
This commit is contained in:
commit
5b6f0a11d0
|
@ -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;
|
||||||
|
|
||||||
// 根节点挂接全量事件
|
// 根节点挂接全量事件
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)) {
|
||||||
// 事件监听属性处理
|
// 事件监听属性处理
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 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()) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,5 +12,3 @@ export type ListenerUnit = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListenerUnitList = Array<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']],
|
['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']],
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阻止默认行为
|
// 阻止默认行为
|
||||||
|
|
|
@ -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';
|
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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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中调用setState,parent可能是null,因为startUpdateVNode会被clear
|
// 当在componentWillUnmount中调用setState,parent可能是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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非批量:即同步执行的,没有必要去执行RenderQueue,RenderQueue放的是异步的
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue