Match-id-7a3ed6e1385eb54f9a78cec3e512e7f55900f577
This commit is contained in:
commit
90201b46cb
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
['@babel/plugin-transform-spread', { loose: true, useBuiltIns: true }],
|
||||
'@babel/plugin-transform-parameters',
|
||||
['@babel/plugin-transform-destructuring', { loose: true, useBuiltIns: true }],
|
||||
['@babel/plugin-transform-block-scoping', { throwIfClosureRequired: true }],
|
||||
['@babel/plugin-transform-block-scoping', { throwIfClosureRequired: false }],
|
||||
'@babel/plugin-transform-classes',
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
|
|
|
@ -14,13 +14,9 @@ import {
|
|||
TreeRoot,
|
||||
} from '../renderer/vnode/VNodeTags';
|
||||
|
||||
const prefix = '_horizon';
|
||||
|
||||
const internalKeys = {
|
||||
VNode: `${prefix}VNode`,
|
||||
props: `${prefix}Props`,
|
||||
nonDelegatedEvents: `${prefix}NonDelegatedEvents`,
|
||||
};
|
||||
const INTERNAL_VNODE = '_horizon_VNode';
|
||||
const INTERNAL_PROPS = '_horizon_Props';
|
||||
const INTERNAL_NONDELEGATEEVENTS = '_horizon_NonDelegatedEvents';
|
||||
|
||||
// 通过 VNode 实例获取 DOM 节点
|
||||
export function getDom(vNode: VNode): Element | Text | null {
|
||||
|
@ -36,12 +32,12 @@ export function saveVNode(
|
|||
vNode: VNode,
|
||||
dom: Element | Text | Container,
|
||||
): void {
|
||||
dom[internalKeys.VNode] = vNode;
|
||||
dom[INTERNAL_VNODE] = vNode;
|
||||
}
|
||||
|
||||
// 用 DOM 节点,来找其对应的 VNode 实例
|
||||
export function getVNode(dom: Node|Container): VNode | null {
|
||||
const vNode = dom[internalKeys.VNode] || (dom as Container)._treeRoot;
|
||||
const vNode = dom[INTERNAL_VNODE] || (dom as Container)._treeRoot;
|
||||
if (vNode) {
|
||||
const {tag} = vNode;
|
||||
if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
|
||||
|
@ -53,7 +49,7 @@ export function getVNode(dom: Node|Container): VNode | null {
|
|||
|
||||
// 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode
|
||||
export function getNearestVNode(dom: Node): null | VNode {
|
||||
let vNode = dom[internalKeys.VNode];
|
||||
let vNode = dom[INTERNAL_VNODE];
|
||||
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
|
||||
return vNode;
|
||||
}
|
||||
|
@ -62,7 +58,7 @@ export function getNearestVNode(dom: Node): null | VNode {
|
|||
let parentDom = dom.parentNode;
|
||||
let nearVNode = null;
|
||||
while (parentDom) {
|
||||
vNode = parentDom[internalKeys.VNode];
|
||||
vNode = parentDom[INTERNAL_VNODE];
|
||||
if (vNode) {
|
||||
nearVNode = vNode;
|
||||
break;
|
||||
|
@ -74,19 +70,19 @@ export function getNearestVNode(dom: Node): null | VNode {
|
|||
|
||||
// 获取 vNode 上的属性相关信息
|
||||
export function getVNodeProps(dom: Element | Text): Props | null{
|
||||
return dom[internalKeys.props] || null;
|
||||
return dom[INTERNAL_PROPS] || null;
|
||||
}
|
||||
|
||||
// 将 DOM 属性相关信息挂到 DOM 对象的特定属性上
|
||||
export function updateVNodeProps(dom: Element | Text, props: Props): void {
|
||||
dom[internalKeys.props] = props;
|
||||
dom[INTERNAL_PROPS] = props;
|
||||
}
|
||||
|
||||
export function getNonDelegatedListenerMap(dom: Element | Text): Map<string, EventListener> {
|
||||
let eventsMap = dom[internalKeys.nonDelegatedEvents];
|
||||
let eventsMap = dom[INTERNAL_NONDELEGATEEVENTS];
|
||||
if (!eventsMap) {
|
||||
eventsMap = new Map();
|
||||
dom[internalKeys.nonDelegatedEvents] = eventsMap;
|
||||
dom[INTERNAL_NONDELEGATEEVENTS] = eventsMap;
|
||||
}
|
||||
return eventsMap;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
createDom,
|
||||
} from './utils/DomCreator';
|
||||
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
|
||||
import { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common';
|
||||
import { shouldAutoFocus } from './utils/Common';
|
||||
import { NSS } from './utils/DomCreator';
|
||||
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
|
||||
|
||||
|
@ -165,7 +165,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) {
|
|||
// 应用diff更新Properties.
|
||||
// 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false.
|
||||
if (type === 'input' && newProps.type === 'radio' && newProps.name != null && newProps.checked != null) {
|
||||
updateCommonProp(element, 'checked', newProps.checked);
|
||||
updateCommonProp(element, 'checked', newProps.checked, true);
|
||||
}
|
||||
const isNativeTag = isNativeElement(type, newProps);
|
||||
updateDomProps(element, changeList, isNativeTag);
|
||||
|
|
|
@ -11,20 +11,18 @@ import { isEventProp, isNativeElement } from '../validators/ValidateProps';
|
|||
function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
|
||||
if (propName === 'style') {
|
||||
setStyles(dom, propVal);
|
||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||
dom.innerHTML = propVal.__html;
|
||||
} else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理
|
||||
const type = typeof propVal;
|
||||
if (type === 'string') {
|
||||
dom.textContent = propVal;
|
||||
} else if (type === 'number') {
|
||||
dom.textContent = propVal + ''; // 这种数字转字符串的方式效率最高
|
||||
}
|
||||
} else if (isEventProp(propName)) {
|
||||
// 事件监听属性处理
|
||||
if (!allDelegatedHorizonEvents.has(propName)) {
|
||||
listenNonDelegatedEvent(propName, dom, propVal);
|
||||
}
|
||||
} else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理
|
||||
const type = typeof propVal;
|
||||
if (type === 'string' || type === 'number') {
|
||||
dom.textContent = propVal;
|
||||
}
|
||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||
dom.innerHTML = propVal.__html;
|
||||
} else {
|
||||
if (!isInit || (isInit && propVal != null)) {
|
||||
updateCommonProp(dom, propName, propVal, isNativeTag);
|
||||
|
@ -40,10 +38,12 @@ export function setDomProps(
|
|||
): void {
|
||||
const isNativeTag = isNativeElement(tagName, props);
|
||||
const keysOfProps = Object.keys(props);
|
||||
|
||||
for (let i = 0; i < keysOfProps.length; i++) {
|
||||
const propName = keysOfProps[i];
|
||||
const propVal = props[propName];
|
||||
let propName;
|
||||
let propVal;
|
||||
const keyLength = keysOfProps.length;
|
||||
for (let i = 0; i < keyLength; i++) {
|
||||
propName = keysOfProps[i];
|
||||
propVal = props[propName];
|
||||
|
||||
updateOneProp(dom, propName, isNativeTag, propVal, true);
|
||||
}
|
||||
|
@ -55,9 +55,12 @@ export function updateDomProps(
|
|||
changeList: Array<any>,
|
||||
isNativeTag: boolean,
|
||||
): void {
|
||||
for (let i = 0; i < changeList.length; i++) {
|
||||
const { propName, propVal } = changeList[i];
|
||||
|
||||
const listLength = changeList.length;
|
||||
let propName;
|
||||
let propVal;
|
||||
for (let i = 0; i < listLength; i++) {
|
||||
propName = changeList[i].propName;
|
||||
propVal = changeList[i].propVal;
|
||||
updateOneProp(dom, propName, isNativeTag, propVal);
|
||||
}
|
||||
}
|
||||
|
@ -73,19 +76,24 @@ export function compareProps(
|
|||
const keysOfOldProps = Object.keys(oldProps);
|
||||
const keysOfNewProps = Object.keys(newProps);
|
||||
|
||||
const oldPropsLength = keysOfOldProps.length;
|
||||
let propName;
|
||||
let oldStyle;
|
||||
let styleProps;
|
||||
let styleProp;
|
||||
// 找到旧属性中需要删除的属性
|
||||
for (let i = 0; i < keysOfOldProps.length; i++) {
|
||||
const propName = keysOfOldProps[i];
|
||||
for (let i = 0; i < oldPropsLength; i++) {
|
||||
propName = keysOfOldProps[i];
|
||||
// 新属性中包含该属性或者该属性为空值的属性不需要处理
|
||||
if (keysOfNewProps.includes(propName) || oldProps[propName] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propName === 'style') {
|
||||
const oldStyle = oldProps[propName];
|
||||
const styleProps = Object.keys(oldStyle);
|
||||
oldStyle = oldProps[propName];
|
||||
styleProps = Object.keys(oldStyle);
|
||||
for (let j = 0; j < styleProps.length; j++) {
|
||||
const styleProp = styleProps[j];
|
||||
styleProp = styleProps[j];
|
||||
updatesForStyle[styleProp] = '';
|
||||
}
|
||||
} else if (
|
||||
|
@ -110,11 +118,17 @@ export function compareProps(
|
|||
}
|
||||
}
|
||||
|
||||
let newPropValue;
|
||||
let oldPropValue;
|
||||
let oldStyleProps;
|
||||
let newStyleProps;
|
||||
let newHTML;
|
||||
let oldHTML;
|
||||
// 遍历新属性,获取新增和变更属性
|
||||
for (let i = 0; i < keysOfNewProps.length; i++) {
|
||||
const propName = keysOfNewProps[i];
|
||||
const newPropValue = newProps[propName];
|
||||
const oldPropValue = oldProps != null ? oldProps[propName] : null;
|
||||
propName = keysOfNewProps[i];
|
||||
newPropValue = newProps[propName];
|
||||
oldPropValue = oldProps != null ? oldProps[propName] : null;
|
||||
|
||||
if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) {
|
||||
// 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理
|
||||
|
@ -124,18 +138,18 @@ export function compareProps(
|
|||
if (propName === 'style') {
|
||||
if (oldPropValue) { // 之前 style 属性有设置非空值
|
||||
// 原来有这个 style,但现在没这个 style 了
|
||||
const oldStyleProps = Object.keys(oldPropValue);
|
||||
oldStyleProps = Object.keys(oldPropValue);
|
||||
for (let j = 0; j < oldStyleProps.length; j++) {
|
||||
const styleProp = oldStyleProps[j];
|
||||
styleProp = oldStyleProps[j];
|
||||
if (!newPropValue || !Object.prototype.hasOwnProperty.call(newPropValue, styleProp)) {
|
||||
updatesForStyle[styleProp] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 现在有这个 style,但是和原来不相等
|
||||
const newStyleProps = newPropValue ? Object.keys(newPropValue) : [];
|
||||
newStyleProps = newPropValue ? Object.keys(newPropValue) : [];
|
||||
for (let j = 0; j < newStyleProps.length; j++) {
|
||||
const styleProp = newStyleProps[j];
|
||||
styleProp = newStyleProps[j];
|
||||
if (oldPropValue[styleProp] !== newPropValue[styleProp]) {
|
||||
updatesForStyle[styleProp] = newPropValue[styleProp];
|
||||
}
|
||||
|
@ -150,8 +164,8 @@ export function compareProps(
|
|||
updatesForStyle = newPropValue;
|
||||
}
|
||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||
const newHTML = newPropValue ? newPropValue.__html : undefined;
|
||||
const oldHTML = oldPropValue ? oldPropValue.__html : undefined;
|
||||
newHTML = newPropValue ? newPropValue.__html : undefined;
|
||||
oldHTML = oldPropValue ? oldPropValue.__html : undefined;
|
||||
if (newHTML != null) {
|
||||
if (oldHTML !== newHTML) {
|
||||
toBeUpdatedProps.push({
|
||||
|
|
|
@ -29,7 +29,7 @@ function convertToLowerCase(str) {
|
|||
* attrName 指代码中属性设置的属性名称(如 class)
|
||||
* 多数情况 attrName 仅用作初始 DOM 节点对象使用,而 property 更多用于页面交互
|
||||
*/
|
||||
export function updateCommonProp(dom: Element, attrName: string, value: any, isNativeTag: boolean = true) {
|
||||
export function updateCommonProp(dom: Element, attrName: string, value: any, isNativeTag: boolean) {
|
||||
const propDetails = getPropDetails(attrName);
|
||||
|
||||
if (isInvalidValue(attrName, value, propDetails, isNativeTag)) {
|
||||
|
|
|
@ -6,21 +6,9 @@ import {Props} from '../DOMOperator';
|
|||
* @param doc 指定 document
|
||||
*/
|
||||
export function getFocusedDom(doc?: Document): HorizonDom | null {
|
||||
let currentDocument;
|
||||
if (doc) {
|
||||
currentDocument = doc;
|
||||
} else {
|
||||
if (document) {
|
||||
currentDocument = document;
|
||||
}
|
||||
}
|
||||
if (!currentDocument) {
|
||||
return null;
|
||||
} else if (currentDocument.activeElement) {
|
||||
return currentDocument.activeElement;
|
||||
} else {
|
||||
return currentDocument.body;
|
||||
}
|
||||
let currentDocument = doc ?? document;
|
||||
|
||||
return currentDocument.activeElement ?? currentDocument.body;
|
||||
}
|
||||
|
||||
// 如果 input 或者 textarea 元素中有文字被选中时,activeElement 属性就会返回该元素
|
||||
|
|
|
@ -5,13 +5,6 @@ export const NSS = {
|
|||
svg: 'http://www.w3.org/2000/svg',
|
||||
};
|
||||
|
||||
const div = document.createElement('div');
|
||||
const span = document.createElement('span');
|
||||
const tr = document.createElement('tr');
|
||||
const td = document.createElement('td');
|
||||
const a = document.createElement('a');
|
||||
const p = document.createElement('p');
|
||||
|
||||
// 创建DOM元素
|
||||
export function createDom(
|
||||
tagName: string,
|
||||
|
@ -23,18 +16,6 @@ export function createDom(
|
|||
|
||||
if (ns !== NSS.html) {
|
||||
dom = document.createElementNS(ns, tagName);
|
||||
} else if (tagName === 'div') {
|
||||
dom = div.cloneNode(false);
|
||||
} else if (tagName === 'span') {
|
||||
dom = span.cloneNode(false);
|
||||
} else if (tagName === 'tr') {
|
||||
dom = tr.cloneNode(false);
|
||||
} else if (tagName === 'td') {
|
||||
dom = td.cloneNode(false);
|
||||
} else if (tagName === 'a') {
|
||||
dom = a.cloneNode(false);
|
||||
} else if (tagName === 'p') {
|
||||
dom = p.cloneNode(false);
|
||||
} else {
|
||||
dom = document.createElement(tagName);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ export function updateInputValue(dom: HTMLInputElement, properties: IProperty) {
|
|||
dom.value = String(value);
|
||||
}
|
||||
} else if (checked != null) {
|
||||
updateCommonProp(dom, 'checked', checked);
|
||||
updateCommonProp(dom, 'checked', checked, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import {
|
|||
getNearestVNode,
|
||||
getNonDelegatedListenerMap,
|
||||
} from '../dom/DOMInternalKeys';
|
||||
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';
|
||||
import {decorateNativeEvent} from './customEvents/EventFactory';
|
||||
|
||||
const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
|
||||
|
||||
|
@ -98,7 +98,7 @@ function isCaptureEvent(horizonEventName) {
|
|||
// 封装监听函数
|
||||
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
|
||||
return event => {
|
||||
const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement);
|
||||
const customEvent = decorateNativeEvent(horizonEventName, nativeEvtName, event);
|
||||
listener(customEvent);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,13 +8,12 @@ import {
|
|||
EVENT_TYPE_CAPTURE,
|
||||
} from './const';
|
||||
import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler';
|
||||
import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler';
|
||||
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
|
||||
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
|
||||
import {
|
||||
addOnPrefix,
|
||||
addOnPrefix, setPropertyWritable,
|
||||
} from './utils';
|
||||
import { createCustomEvent } from './customEvents/EventFactory';
|
||||
import { decorateNativeEvent } from './customEvents/EventFactory';
|
||||
import { getListenersFromTree } from './ListenerGetter';
|
||||
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
|
||||
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
|
||||
|
@ -47,7 +46,7 @@ function getCommonListeners(
|
|||
nativeEvtName = 'blur';
|
||||
}
|
||||
|
||||
const horizonEvent = createCustomEvent(horizonEvtName, nativeEvtName, nativeEvent, null, target);
|
||||
const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent);
|
||||
return getListenersFromTree(
|
||||
vNode,
|
||||
horizonEvtName,
|
||||
|
@ -63,13 +62,15 @@ function processListeners(listenerList: ListenerUnitList): void {
|
|||
if (event.isPropagationStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyWritable(event, 'currentTarget');
|
||||
event.currentTarget = currentTarget;
|
||||
listener(event);
|
||||
event.currentTarget = null;
|
||||
});
|
||||
}
|
||||
|
||||
function getProcessListenersFacade(
|
||||
function getProcessListeners(
|
||||
nativeEvtName: string,
|
||||
vNode: VNode | null,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
|
@ -105,17 +106,6 @@ function getProcessListenersFacade(
|
|||
));
|
||||
}
|
||||
|
||||
if (nativeEvtName === 'compositionend' ||
|
||||
nativeEvtName === 'compositionstart' ||
|
||||
nativeEvtName === 'compositionupdate') {
|
||||
listenerList = listenerList.concat(getCompositionListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
target,
|
||||
));
|
||||
}
|
||||
|
||||
if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) {
|
||||
listenerList = listenerList.concat(getBeforeInputListeners(
|
||||
nativeEvtName,
|
||||
|
@ -138,7 +128,7 @@ function triggerHorizonEvents(
|
|||
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
|
||||
|
||||
// 获取委托事件队列
|
||||
const listenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
||||
const listenerList = getProcessListeners(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
||||
|
||||
// 处理触发的事件队列
|
||||
processListeners(listenerList);
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import {VNode} from '../renderer/Types';
|
||||
import {DomComponent} from '../renderer/vnode/VNodeTags';
|
||||
import {EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE} from './const';
|
||||
import {ListenerUnitList} from './Types';
|
||||
import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
|
||||
import {AnyNativeEvent, ListenerUnitList} from './Types';
|
||||
|
||||
// 获取监听事件
|
||||
export function getListenersFromTree(
|
||||
targetVNode: VNode | null,
|
||||
horizonEvtName: string | null,
|
||||
horizonEvent: CustomBaseEvent,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
eventType: string,
|
||||
): ListenerUnitList {
|
||||
if (!horizonEvtName) {
|
||||
|
@ -31,7 +30,7 @@ export function getListenersFromTree(
|
|||
vNode,
|
||||
listener: captureListener,
|
||||
currentTarget: realNode,
|
||||
event: horizonEvent,
|
||||
event: nativeEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ export function getListenersFromTree(
|
|||
vNode,
|
||||
listener: bubbleListener,
|
||||
currentTarget: realNode,
|
||||
event: horizonEvent,
|
||||
event: nativeEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* style中的动画事件
|
||||
*/
|
||||
|
||||
// style事件浏览器兼容前缀
|
||||
const vendorPrefixes = {
|
||||
animationend: {
|
||||
MozAnimation: 'mozAnimationEnd',
|
||||
WebkitAnimation: 'webkitAnimationEnd',
|
||||
animation: 'animationend',
|
||||
},
|
||||
animationiteration: {
|
||||
MozAnimation: 'mozAnimationIteration',
|
||||
WebkitAnimation: 'webkitAnimationIteration',
|
||||
animation: 'animationiteration',
|
||||
},
|
||||
animationstart: {
|
||||
MozAnimation: 'mozAnimationStart',
|
||||
WebkitAnimation: 'webkitAnimationStart',
|
||||
animation: 'animationstart',
|
||||
},
|
||||
transitionend: {
|
||||
MozTransition: 'mozTransitionEnd',
|
||||
WebkitTransition: 'webkitTransitionEnd',
|
||||
transition: 'transitionend',
|
||||
},
|
||||
};
|
||||
|
||||
// 获取属性中对应事件名
|
||||
function getEventNameByStyle(eventName) {
|
||||
const prefixMap = vendorPrefixes[eventName];
|
||||
if (!prefixMap) {
|
||||
return eventName;
|
||||
}
|
||||
const style = document.createElement('div').style
|
||||
for (const styleProp in prefixMap) {
|
||||
if (styleProp in style) {
|
||||
return prefixMap[styleProp];
|
||||
}
|
||||
}
|
||||
return eventName;
|
||||
}
|
||||
|
||||
export const STYLE_AMT_END: string = getEventNameByStyle(
|
||||
'animationend',
|
||||
);
|
||||
export const STYLE_AMT_ITERATION: string = getEventNameByStyle(
|
||||
'animationiteration',
|
||||
);
|
||||
export const STYLE_AMT_START: string = getEventNameByStyle(
|
||||
'animationstart',
|
||||
);
|
||||
export const STYLE_TRANS_END: string = getEventNameByStyle(
|
||||
'transitionend',
|
||||
);
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import type {VNode} from '../renderer/Types';
|
||||
import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
|
||||
|
||||
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
|
||||
|
||||
|
@ -8,7 +7,7 @@ export type ListenerUnit = {
|
|||
vNode: null | VNode;
|
||||
listener: Function;
|
||||
currentTarget: EventTarget;
|
||||
event: CustomBaseEvent;
|
||||
event: AnyNativeEvent;
|
||||
};
|
||||
|
||||
export type ListenerUnitList = Array<ListenerUnit>;
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
import {
|
||||
STYLE_AMT_END,
|
||||
STYLE_AMT_ITERATION,
|
||||
STYLE_AMT_START,
|
||||
STYLE_TRANS_END
|
||||
} from './StyleEventNames';
|
||||
|
||||
// Horizon事件和原生事件对应关系
|
||||
export const horizonEventToNativeMap = new Map([
|
||||
|
@ -38,10 +32,10 @@ export const horizonEventToNativeMap = new Map([
|
|||
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
|
||||
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
|
||||
|
||||
['onAnimationEnd', [STYLE_AMT_END]],
|
||||
['onAnimationIteration', [STYLE_AMT_ITERATION]],
|
||||
['onAnimationStart', [STYLE_AMT_START]],
|
||||
['onTransitionEnd', [STYLE_TRANS_END]]
|
||||
['onAnimationEnd', ['animationend']],
|
||||
['onAnimationIteration', ['animationiteration']],
|
||||
['onAnimationStart', ['animationstart']],
|
||||
['onTransitionEnd', ['transitionend']]
|
||||
]);
|
||||
|
||||
export const CommonEventToHorizonMap = {
|
||||
|
@ -68,10 +62,13 @@ export const CommonEventToHorizonMap = {
|
|||
selectionchange: 'selectChange',
|
||||
textInput: 'textInput',
|
||||
touchmove: 'touchMove',
|
||||
[STYLE_AMT_END]: 'animationEnd',
|
||||
[STYLE_AMT_ITERATION]: 'animationIteration',
|
||||
[STYLE_AMT_START]: 'animationStart',
|
||||
[STYLE_TRANS_END]: 'transitionEnd',
|
||||
animationend: 'animationEnd',
|
||||
animationiteration: 'animationIteration',
|
||||
animationstart: 'animationStart',
|
||||
transitionend: 'transitionEnd',
|
||||
compositionstart: 'compositionStart',
|
||||
compositionend: 'compositionEnd',
|
||||
compositionupdate: 'compositionUpdate',
|
||||
};
|
||||
|
||||
export const CHAR_CODE_ENTER = 13;
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
/**
|
||||
* 自定义的基本事件类型
|
||||
*/
|
||||
|
||||
import {VNode} from '../../renderer/Types';
|
||||
|
||||
// 从原生事件中复制属性到自定义事件中
|
||||
function extendAttribute(target, source) {
|
||||
let val;
|
||||
let attr;
|
||||
for (attr in source) {
|
||||
// 这两个方法需要override
|
||||
if (attr === 'preventDefault' || attr === 'stopPropagation') {
|
||||
continue;
|
||||
}
|
||||
|
||||
val = source[attr];
|
||||
if (val !== undefined) {
|
||||
if (typeof val === 'function') {
|
||||
target[attr] = function() {
|
||||
return source[attr].apply(source, arguments);
|
||||
};
|
||||
} else {
|
||||
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 {
|
||||
|
||||
defaultPrevented: boolean;
|
||||
target: EventTarget;
|
||||
isDefaultPrevented: () => boolean;
|
||||
isPropagationStopped: () => boolean;
|
||||
currentTarget: EventTarget;
|
||||
relatedTarget: EventTarget;
|
||||
|
||||
// custom事件自定义属性
|
||||
customEventName: string;
|
||||
targetVNode: VNode;
|
||||
type: string;
|
||||
timeStamp: number;
|
||||
nativeEvent: any;
|
||||
|
||||
// 键盘事件属性
|
||||
key: string;
|
||||
charCode: number;
|
||||
keyCode: number;
|
||||
which: number;
|
||||
|
||||
constructor(
|
||||
customEvtName: string | null,
|
||||
nativeEvtName: string,
|
||||
nativeEvt: { [propName: string]: any },
|
||||
vNode: VNode | null,
|
||||
target: null | EventTarget
|
||||
) {
|
||||
// 复制原生属性到自定义事件
|
||||
extendAttribute(this, nativeEvt);
|
||||
|
||||
const defaultPrevented = nativeEvt.defaultPrevented != null ?
|
||||
nativeEvt.defaultPrevented :
|
||||
nativeEvt.returnValue === false;
|
||||
this.defaultPrevented = defaultPrevented;
|
||||
this.preventDefault = this.preventDefault.bind(this);
|
||||
this.stopPropagation = this.stopPropagation.bind(this);
|
||||
this.isDefaultPrevented = () => defaultPrevented;
|
||||
this.isPropagationStopped = () => false;
|
||||
this.relatedTarget = nativeEvt.relatedTarget;
|
||||
this.target = target;
|
||||
|
||||
// 键盘事件属性
|
||||
this.key = uniqueKeyMap.get(nativeEvt.key) || nativeEvt.key;
|
||||
|
||||
// custom事件自定义属性
|
||||
this.customEventName = customEvtName;
|
||||
this.targetVNode = vNode;
|
||||
this.type = nativeEvtName;
|
||||
this.nativeEvent = nativeEvt;
|
||||
}
|
||||
|
||||
// 阻止默认行为
|
||||
preventDefault() {
|
||||
this.defaultPrevented = true;
|
||||
if (!this.nativeEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.nativeEvent.preventDefault === 'function') {
|
||||
this.nativeEvent.preventDefault();
|
||||
}
|
||||
this.nativeEvent.returnValue = false;
|
||||
this.isDefaultPrevented = () => true;
|
||||
}
|
||||
|
||||
// 停止冒泡
|
||||
stopPropagation() {
|
||||
if (!this.nativeEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.nativeEvent.stopPropagation === 'function') {
|
||||
this.nativeEvent.stopPropagation();
|
||||
}
|
||||
this.nativeEvent.cancelBubble = true;
|
||||
this.isPropagationStopped = () => true;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,33 @@
|
|||
import {CustomBaseEvent} from './CustomBaseEvent';
|
||||
|
||||
// 兼容IE的event key
|
||||
const uniqueKeyMap = new Map([
|
||||
['Esc', 'Escape'],
|
||||
['Spacebar', ' '],
|
||||
['Left', 'ArrowLeft'],
|
||||
['Up', 'ArrowUp'],
|
||||
['Right', 'ArrowRight'],
|
||||
['Down', 'ArrowDown'],
|
||||
['Del', 'Delete'],
|
||||
]);
|
||||
|
||||
// 创建普通自定义事件对象实例,和原生事件对应
|
||||
export function createCustomEvent(customEventName, nativeEvtName, nativeEvent, vNode, currentTarget) {
|
||||
return new CustomBaseEvent(
|
||||
customEventName,
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
currentTarget,
|
||||
);
|
||||
export function decorateNativeEvent(customEventName, nativeEvtName, nativeEvent) {
|
||||
|
||||
nativeEvent.isDefaultPrevented = () => nativeEvent.defaultPrevented;
|
||||
nativeEvent.isPropagationStopped = () => nativeEvent.cancelBubble;
|
||||
|
||||
// custom事件自定义属性
|
||||
nativeEvent.customEventName = customEventName;
|
||||
nativeEvent.nativeEvent = nativeEvent;
|
||||
// 保存原生的事件类型,因为下面会修改
|
||||
nativeEvent.nativeEventType = nativeEvent.type;
|
||||
|
||||
Object.defineProperty(nativeEvent, 'type', { writable: true });
|
||||
nativeEvent.type = nativeEvtName;
|
||||
|
||||
const orgKey = nativeEvent.key;
|
||||
Object.defineProperty(nativeEvent, 'key', { writable: true });
|
||||
nativeEvent.key = uniqueKeyMap.get(orgKey) || orgKey;
|
||||
|
||||
return nativeEvent;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import type {VNode} from '../../renderer/Types';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
||||
import {CHAR_CODE_SPACE, EVENT_TYPE_ALL} from '../const';
|
||||
import {CustomBaseEvent} from '../customEvents/CustomBaseEvent';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE);
|
||||
|
||||
|
@ -36,12 +35,10 @@ export function getListeners(
|
|||
return [];
|
||||
}
|
||||
|
||||
const event: CustomBaseEvent = createCustomEvent(
|
||||
const event: AnyNativeEvent = decorateNativeEvent(
|
||||
'onBeforeInput',
|
||||
'beforeinput',
|
||||
nativeEvent,
|
||||
null,
|
||||
target,
|
||||
);
|
||||
event.data = chars;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
||||
import {getDom} from '../../dom/DOMInternalKeys';
|
||||
import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler';
|
||||
import {addValueUpdateList} from '../ControlledValueUpdater';
|
||||
import {isTextInputElement} from '../utils';
|
||||
import {isInputElement} from '../utils';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {AnyNativeEvent, ListenerUnitList} from '../Types';
|
||||
import {
|
||||
|
@ -18,14 +18,14 @@ function shouldTriggerChangeEvent(targetDom, evtName) {
|
|||
|
||||
if (domTag === 'select' || (domTag === 'input' && type === 'file')) {
|
||||
return evtName === 'change';
|
||||
} else if (isTextInputElement(targetDom)) {
|
||||
if (evtName === 'input' || evtName === 'change') {
|
||||
return isInputValueChanged(targetDom);
|
||||
}
|
||||
} else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) {
|
||||
if (evtName === 'click') {
|
||||
return isInputValueChanged(targetDom);
|
||||
}
|
||||
} else if (isInputElement(targetDom)) {
|
||||
if (evtName === 'input' || evtName === 'change') {
|
||||
return isInputValueChanged(targetDom);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -48,12 +48,10 @@ export function getListeners(
|
|||
// 判断是否需要触发change事件
|
||||
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
|
||||
addValueUpdateList(target);
|
||||
const event = createCustomEvent(
|
||||
const event = decorateNativeEvent(
|
||||
'onChange',
|
||||
'change',
|
||||
nativeEvt,
|
||||
null,
|
||||
target,
|
||||
);
|
||||
return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL);
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import type {VNode} from '../../renderer/Types';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
|
||||
const compositionEventObj = {
|
||||
compositionstart: 'onCompositionStart',
|
||||
compositionend: 'onCompositionEnd',
|
||||
compositionupdate: 'onCompositionUpdate',
|
||||
};
|
||||
|
||||
// compoisition事件主要处理中文输入法输入时的触发事件
|
||||
export function getListeners(
|
||||
evtName: string,
|
||||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ListenerUnitList {
|
||||
const evtType = compositionEventObj[evtName];
|
||||
|
||||
const event = createCustomEvent(
|
||||
evtType,
|
||||
evtName,
|
||||
nativeEvt,
|
||||
null,
|
||||
target,
|
||||
);
|
||||
return getListenersFromTree(vNode, evtType, event, EVENT_TYPE_ALL);
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
import {createCustomEvent} from '../customEvents/EventFactory';
|
||||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
||||
import {shallowCompare} from '../../renderer/utils/compare';
|
||||
import {getFocusedDom} from '../../dom/utils/Common';
|
||||
import {getDom} from '../../dom/DOMInternalKeys';
|
||||
import {isDocument} from '../../dom/utils/Common';
|
||||
import {isTextInputElement} from '../utils';
|
||||
import {isInputElement, setPropertyWritable} from '../utils';
|
||||
import type {AnyNativeEvent} from '../Types';
|
||||
import {getListenersFromTree} from '../ListenerGetter';
|
||||
import type {VNode} from '../../renderer/Types';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {ListenerUnitList} from '../Types';
|
||||
|
||||
const horizonEventName = 'onSelect'
|
||||
const horizonEventName = 'onSelect';
|
||||
|
||||
let currentElement = null;
|
||||
let currentVNode = null;
|
||||
let lastSelection: Selection | null = null;
|
||||
|
||||
function initTargetCache(dom, vNode) {
|
||||
if (isTextInputElement(dom) || dom.contentEditable === 'true') {
|
||||
if (isInputElement(dom) || dom.contentEditable === 'true') {
|
||||
currentElement = dom;
|
||||
currentVNode = vNode;
|
||||
lastSelection = null;
|
||||
|
@ -54,13 +54,12 @@ function getSelectEvent(nativeEvent, target) {
|
|||
if (!shallowCompare(lastSelection, currentSelection)) {
|
||||
lastSelection = currentSelection;
|
||||
|
||||
const event = createCustomEvent(
|
||||
const event = decorateNativeEvent(
|
||||
horizonEventName,
|
||||
'select',
|
||||
nativeEvent,
|
||||
null,
|
||||
target,
|
||||
);
|
||||
setPropertyWritable(nativeEvent, 'target');
|
||||
event.target = currentElement;
|
||||
|
||||
return getListenersFromTree(
|
||||
|
@ -80,23 +79,23 @@ function getSelectEvent(nativeEvent, target) {
|
|||
* 触发场景:用户输入、折叠选择、文本选择
|
||||
*/
|
||||
export function getListeners(
|
||||
name: string,
|
||||
nativeEvtName: string,
|
||||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
target: null | EventTarget,
|
||||
): ListenerUnitList {
|
||||
const targetNode = vNode ? getDom(vNode) : window;
|
||||
let eventUnitList: ListenerUnitList = [];
|
||||
switch (name) {
|
||||
switch (nativeEvtName) {
|
||||
case 'focusin':
|
||||
initTargetCache(targetNode, vNode);
|
||||
return eventUnitList;
|
||||
break;
|
||||
case 'focusout':
|
||||
clearTargetCache();
|
||||
return eventUnitList;
|
||||
break;
|
||||
case 'mousedown':
|
||||
isInMouseEvent = true;
|
||||
return eventUnitList;
|
||||
break;
|
||||
case 'contextmenu':
|
||||
case 'mouseup':
|
||||
case 'dragend':
|
||||
|
@ -108,5 +107,6 @@ export function getListeners(
|
|||
case 'keyup':
|
||||
eventUnitList = getSelectEvent(nativeEvt, target);
|
||||
}
|
||||
|
||||
return eventUnitList;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
|
||||
// 支持的输入框类型
|
||||
const supportedInputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month',
|
||||
'number', 'password', 'range', 'search', 'tel', 'text', 'time', 'url', 'week'];
|
||||
|
||||
export function isTextInputElement(dom?: HTMLElement): boolean {
|
||||
if (dom instanceof HTMLInputElement) {
|
||||
return supportedInputTypes.includes(dom.type);
|
||||
export function isInputElement(dom?: HTMLElement): boolean {
|
||||
if (dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const nodeName = dom && dom.nodeName && dom.nodeName.toLowerCase();
|
||||
return nodeName === 'textarea';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,3 +14,10 @@ export function addOnPrefix(name) {
|
|||
}
|
||||
return 'on' + name[0].toUpperCase() + name.slice(1);
|
||||
}
|
||||
|
||||
export function setPropertyWritable(obj, propName) {
|
||||
const desc = Object.getOwnPropertyDescriptor(obj, propName);
|
||||
if (!desc || !desc.writable) {
|
||||
Object.defineProperty(obj, propName, { writable : true });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,17 +29,32 @@ let ctxOldContext: Object = {};
|
|||
let ctxOldChange: Boolean = false;
|
||||
let ctxOldPreviousContext: Object = {};
|
||||
|
||||
function setContext(vNode: VNode, contextName, value) {
|
||||
if (vNode.contexts === null) {
|
||||
vNode.contexts = {
|
||||
[contextName]: value,
|
||||
};
|
||||
} else {
|
||||
vNode.contexts[contextName] = value;
|
||||
}
|
||||
}
|
||||
function getContext(vNode: VNode, contextName) {
|
||||
if (vNode.contexts !== null) {
|
||||
return vNode.contexts[contextName];
|
||||
}
|
||||
}
|
||||
|
||||
// capture阶段设置
|
||||
function setNamespaceCtx(vNode: VNode, dom?: Container) {
|
||||
const nextContext = getNSCtx(ctxNamespace, vNode.type, dom);
|
||||
|
||||
vNode.setContext(CTX_NAMESPACE, ctxNamespace);
|
||||
setContext(vNode, CTX_NAMESPACE, ctxNamespace);
|
||||
ctxNamespace = nextContext;
|
||||
}
|
||||
|
||||
// bubble阶段恢复
|
||||
function resetNamespaceCtx(vNode: VNode) {
|
||||
ctxNamespace = vNode.getContext(CTX_NAMESPACE);
|
||||
ctxNamespace = getContext(vNode, CTX_NAMESPACE);
|
||||
}
|
||||
|
||||
function getNamespaceCtx(): string {
|
||||
|
@ -49,14 +64,14 @@ function getNamespaceCtx(): string {
|
|||
function setContextCtx<T>(providerVNode: VNode, nextValue: T) {
|
||||
const context: ContextType<T> = providerVNode.type._context;
|
||||
|
||||
providerVNode.setContext(CTX_CONTEXT, context.value);
|
||||
setContext(providerVNode, CTX_CONTEXT, context.value);
|
||||
context.value = nextValue;
|
||||
}
|
||||
|
||||
function resetContextCtx(providerVNode: VNode) {
|
||||
const context: ContextType<any> = providerVNode.type._context;
|
||||
|
||||
context.value = providerVNode.getContext(CTX_CONTEXT);
|
||||
context.value = getContext(providerVNode, CTX_CONTEXT);
|
||||
}
|
||||
|
||||
// 在局部更新时,恢复父节点的context
|
||||
|
@ -74,11 +89,11 @@ function recoverParentsContextCtx(vNode: VNode) {
|
|||
|
||||
// ctxOldContext是 旧context提供者的context
|
||||
function setVNodeOldContext(providerVNode: VNode, context: Object) {
|
||||
providerVNode.setContext(CTX_OLD_CONTEXT, context);
|
||||
setContext(providerVNode, CTX_OLD_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldContext(vNode: VNode) {
|
||||
return vNode.getContext(CTX_OLD_CONTEXT);
|
||||
return getContext(vNode, CTX_OLD_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldContextCtx(providerVNode: VNode, context: Object) {
|
||||
|
@ -95,11 +110,11 @@ function resetOldContextCtx(vNode: VNode) {
|
|||
}
|
||||
|
||||
function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) {
|
||||
providerVNode.setContext(CTX_OLD_PREVIOUS_CONTEXT, context);
|
||||
setContext(providerVNode, CTX_OLD_PREVIOUS_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldPreviousContext(vNode: VNode) {
|
||||
return vNode.getContext(CTX_OLD_PREVIOUS_CONTEXT);
|
||||
return getContext(vNode, CTX_OLD_PREVIOUS_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldPreviousContextCtx(context: Object) {
|
||||
|
@ -111,7 +126,7 @@ function getOldPreviousContextCtx() {
|
|||
}
|
||||
|
||||
function setContextChangeCtx(providerVNode: VNode, didChange: boolean) {
|
||||
providerVNode.setContext(CTX_OLD_CHANGE, didChange);
|
||||
setContext(providerVNode, CTX_OLD_CHANGE, didChange);
|
||||
ctxOldChange = didChange;
|
||||
}
|
||||
|
||||
|
@ -120,7 +135,7 @@ function getContextChangeCtx() {
|
|||
}
|
||||
|
||||
function resetContextChangeCtx(vNode: VNode) {
|
||||
ctxOldChange = vNode.getContext(CTX_OLD_CHANGE);
|
||||
ctxOldChange = getContext(vNode, CTX_OLD_CHANGE);
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
|
@ -6,7 +6,7 @@ import type {VNode} from './Types';
|
|||
import type {Update} from './UpdateHandler';
|
||||
|
||||
import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
|
||||
import {FlagUtils, Interrupted} from './vnode/VNodeFlags';
|
||||
import {FlagUtils, Interrupted, DidCapture, InitFlag} from './vnode/VNodeFlags';
|
||||
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
|
||||
import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder';
|
||||
import {setRootThrowError} from './submit/Submit';
|
||||
|
@ -71,7 +71,7 @@ export function handleRenderThrowError(
|
|||
// vNode抛出了异常,标记Interrupted中断
|
||||
FlagUtils.markInterrupted(sourceVNode);
|
||||
// dirtyNodes 不再有效
|
||||
sourceVNode.dirtyNodes = [];
|
||||
sourceVNode.dirtyNodes = null;
|
||||
|
||||
// error是个promise
|
||||
if (error !== null && typeof error === 'object' && typeof error.then === 'function') {
|
||||
|
@ -99,7 +99,7 @@ export function handleRenderThrowError(
|
|||
const ctor = vNode.type;
|
||||
const instance = vNode.realNode;
|
||||
if (
|
||||
!vNode.flags.DidCapture &&
|
||||
(vNode.flags & DidCapture) === InitFlag &&
|
||||
(
|
||||
typeof ctor.getDerivedStateFromError === 'function' ||
|
||||
(instance !== null && typeof instance.componentDidCatch === 'function')
|
||||
|
|
|
@ -2,8 +2,8 @@ import type { VNode } from './Types';
|
|||
|
||||
import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue';
|
||||
import { updateVNode } from './vnode/VNodeCreator';
|
||||
import { TreeRoot } from './vnode/VNodeTags';
|
||||
import { FlagUtils } from './vnode/VNodeFlags';
|
||||
import { TreeRoot, DomComponent, DomPortal } from './vnode/VNodeTags';
|
||||
import { FlagUtils, InitFlag, Interrupted } from './vnode/VNodeFlags';
|
||||
import { captureVNode } from './render/BaseComponent';
|
||||
import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit';
|
||||
import { runAsyncEffects } from './submit/HookEffectHandler';
|
||||
|
@ -18,7 +18,6 @@ import {
|
|||
setProcessingClassVNode,
|
||||
setStartVNode
|
||||
} from './GlobalVar';
|
||||
import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils';
|
||||
import {
|
||||
ByAsync,
|
||||
BySync,
|
||||
|
@ -57,10 +56,23 @@ function resetProcessingVariables(startUpdateVNode: VNode) {
|
|||
// 收集有变化的节点,在submit阶段继续处理
|
||||
function collectDirtyNodes(vNode: VNode, parent: VNode): void {
|
||||
// 将子树和此vNode的所有效果附加到父树的效果列表中,子项的完成顺序会影响副作用顺序。
|
||||
parent.dirtyNodes.push(...vNode.dirtyNodes);
|
||||
const dirtyNodes = vNode.dirtyNodes;
|
||||
if (dirtyNodes !== null && dirtyNodes.length) {
|
||||
if (parent.dirtyNodes === null) {
|
||||
parent.dirtyNodes = [...vNode.dirtyNodes];
|
||||
} else {
|
||||
parent.dirtyNodes.push(...vNode.dirtyNodes);
|
||||
}
|
||||
dirtyNodes.length = 0;
|
||||
vNode.dirtyNodes = null;
|
||||
}
|
||||
|
||||
if (FlagUtils.hasAnyFlag(vNode)) {
|
||||
parent.dirtyNodes.push(vNode);
|
||||
if (parent.dirtyNodes === null) {
|
||||
parent.dirtyNodes = [vNode];
|
||||
} else {
|
||||
parent.dirtyNodes.push(vNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,13 +85,13 @@ function bubbleVNode(vNode: VNode): void {
|
|||
do {
|
||||
const parent = node.parent;
|
||||
|
||||
if (!node.flags.Interrupted) { // vNode没有抛出异常
|
||||
if ((node.flags & Interrupted) === InitFlag) { // vNode没有抛出异常
|
||||
componentRenders[node.tag].bubbleRender(node);
|
||||
|
||||
// 设置node的childShouldUpdate属性
|
||||
updateChildShouldUpdate(node);
|
||||
|
||||
if (parent !== null && node !== getStartVNode() && !parent.flags.Interrupted) {
|
||||
if (parent !== null && node !== getStartVNode() && (parent.flags & Interrupted) === InitFlag) {
|
||||
collectDirtyNodes(node, parent);
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +107,7 @@ function bubbleVNode(vNode: VNode): void {
|
|||
break;
|
||||
}
|
||||
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
const siblingVNode = node.next;
|
||||
if (siblingVNode !== null) { // 有兄弟vNode
|
||||
processing = siblingVNode;
|
||||
return;
|
||||
|
@ -210,13 +222,21 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
|
||||
if (startVNode.tag !== TreeRoot) { // 不是根节点
|
||||
// 设置namespace,用于createElement
|
||||
const parentObj = findDomParent(startVNode);
|
||||
|
||||
let parent = startVNode.parent;
|
||||
while (parent !== null) {
|
||||
const tag = parent.tag;
|
||||
if (tag === DomComponent) {
|
||||
break;
|
||||
} else if (tag === TreeRoot || tag === DomPortal) {
|
||||
break;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
// 当在componentWillUnmount中调用setState,parent可能是null,因为startVNode会被clear
|
||||
if (parentObj !== null) {
|
||||
const domParent = parentObj.parent;
|
||||
resetNamespaceCtx(domParent);
|
||||
setNamespaceCtx(domParent, domParent.outerDom);
|
||||
if (parent !== null) {
|
||||
resetNamespaceCtx(parent);
|
||||
setNamespaceCtx(parent, parent.outerDom);
|
||||
}
|
||||
|
||||
// 恢复父节点的context
|
||||
|
|
|
@ -72,7 +72,11 @@ function calcState(
|
|||
function collectCallbacks(vNode: VNode, update: Update) {
|
||||
if (update.callback !== null) {
|
||||
FlagUtils.markCallback(vNode);
|
||||
vNode.stateCallbacks.push(update.callback);
|
||||
if (vNode.stateCallbacks === null) {
|
||||
vNode.stateCallbacks = [update.callback];
|
||||
} else {
|
||||
vNode.stateCallbacks.push(update.callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,17 +4,19 @@ import {throwNotInFuncError} from '../../hooks/BaseHook';
|
|||
|
||||
// 重置依赖
|
||||
export function resetDepContexts(vNode: VNode): void {
|
||||
vNode.depContexts = [];
|
||||
vNode.depContexts = null;
|
||||
}
|
||||
|
||||
// 收集依赖
|
||||
function collectDeps<T>(vNode: VNode, context: ContextType<T>) {
|
||||
const depContexts = vNode.depContexts;
|
||||
if (!depContexts.length) {
|
||||
if (depContexts === null) {
|
||||
vNode.depContexts = [context];
|
||||
} else {
|
||||
vNode.isDepContextChange = false;
|
||||
}
|
||||
if (!depContexts.includes(context)) {
|
||||
depContexts.push(context);
|
||||
if (!depContexts.includes(context)) {
|
||||
depContexts.push(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,6 @@ export function isTextType(newChild: any) {
|
|||
return typeof newChild === 'string' || typeof newChild === 'number';
|
||||
}
|
||||
|
||||
export function isArrayType(newChild: any) {
|
||||
return Array.isArray(newChild);
|
||||
}
|
||||
|
||||
export function isIteratorType(newChild: any) {
|
||||
return (typeof Symbol === 'function' && newChild[Symbol.iterator]) || newChild['@@iterator'];
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@ import {
|
|||
isSameType,
|
||||
getIteratorFn,
|
||||
isTextType,
|
||||
isArrayType,
|
||||
isIteratorType,
|
||||
isObjectType,
|
||||
} from './DiffTools';
|
||||
import {getSiblingVNode, travelChildren} from '../vnode/VNodeUtils';
|
||||
import { travelChildren } from '../vnode/VNodeUtils';
|
||||
|
||||
enum DiffCategory {
|
||||
TEXT_NODE = 'TEXT_NODE',
|
||||
|
@ -27,6 +26,10 @@ function isNoKeyFragment(child: any) {
|
|||
// 清除单个节点
|
||||
function deleteVNode(parentNode: VNode, delVNode: VNode): void {
|
||||
FlagUtils.setDeletion(delVNode);
|
||||
if (parentNode.dirtyNodes === null) {
|
||||
parentNode.dirtyNodes = [delVNode];
|
||||
return;
|
||||
}
|
||||
parentNode.dirtyNodes.push(delVNode);
|
||||
}
|
||||
|
||||
|
@ -56,7 +59,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean {
|
|||
}
|
||||
|
||||
if (isObjectType(newChild)) {
|
||||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
if (Array.isArray(newChild) || isIteratorType(newChild)) {
|
||||
return oldKey === null;
|
||||
}
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
|
@ -75,7 +78,7 @@ function getNodeType(newChild: any): string | null {
|
|||
return DiffCategory.TEXT_NODE;
|
||||
}
|
||||
if (isObjectType(newChild)) {
|
||||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
if (Array.isArray(newChild) || isIteratorType(newChild)) {
|
||||
return DiffCategory.ARR_NODE;
|
||||
}
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
|
@ -196,7 +199,7 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
|
|||
return nodeMap.get(newIdx) || null;
|
||||
}
|
||||
if (isObjectType(newChild)) {
|
||||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
if (Array.isArray(newChild) || isIteratorType(newChild)) {
|
||||
return nodeMap.get(newIdx) || null;
|
||||
}
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
|
@ -212,11 +215,11 @@ function setCIndex(vNode: VNode, idx: number) {
|
|||
}
|
||||
|
||||
// diff数组类型的节点,核心算法
|
||||
function diffArrayNodes(
|
||||
function diffArrayNodesHandler(
|
||||
parentNode: VNode,
|
||||
firstChild: VNode | null,
|
||||
newChildren: Array<any>,
|
||||
isComparing: boolean = true
|
||||
isComparing: boolean
|
||||
): VNode | null {
|
||||
let resultingFirstChild: VNode | null = null;
|
||||
|
||||
|
@ -238,6 +241,8 @@ function diffArrayNodes(
|
|||
prevNewNode = newNode;
|
||||
}
|
||||
|
||||
let canBeReuse;
|
||||
let newNode;
|
||||
// 1. 从左侧开始比对currentVNode和newChildren,若不能复用则跳出循环
|
||||
for (; oldNode !== null && leftIdx < newChildren.length; leftIdx++) {
|
||||
if (oldNode.eIndex > leftIdx) {
|
||||
|
@ -245,17 +250,17 @@ function diffArrayNodes(
|
|||
nextOldNode = oldNode;
|
||||
oldNode = null;
|
||||
} else {
|
||||
nextOldNode = getSiblingVNode(oldNode);
|
||||
nextOldNode = oldNode.next;
|
||||
}
|
||||
|
||||
const canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]);
|
||||
canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]);
|
||||
// 不能复用,break
|
||||
if (!canBeReuse) {
|
||||
oldNode = oldNode ?? nextOldNode;
|
||||
break;
|
||||
}
|
||||
|
||||
const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode);
|
||||
newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode);
|
||||
// 没有生成新节点,break
|
||||
if (!newNode) {
|
||||
oldNode = oldNode ?? nextOldNode;
|
||||
|
@ -283,19 +288,20 @@ function diffArrayNodes(
|
|||
let rightOldIndex: number | null = rightRemainingOldChildren.length - 1;
|
||||
|
||||
// 2. 从右侧开始比对currentVNode和newChildren,若不能复用则跳出循环
|
||||
let rightOldNode;
|
||||
for (; rightIdx > leftIdx; rightIdx--) {
|
||||
const rightOldNode = rightRemainingOldChildren[rightOldIndex];
|
||||
rightOldNode = rightRemainingOldChildren[rightOldIndex];
|
||||
if (rightOldIndex < 0 || rightOldNode === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
const canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]);
|
||||
canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]);
|
||||
// 不能复用,break
|
||||
if (!canBeReuse) {
|
||||
break;
|
||||
}
|
||||
|
||||
const newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode);
|
||||
newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode);
|
||||
// 没有生成新节点,break
|
||||
if (newNode === null) {
|
||||
break;
|
||||
|
@ -342,7 +348,7 @@ function diffArrayNodes(
|
|||
// 4. 新节点还有一部分,但是老节点已经没有了
|
||||
if (oldNode === null) {
|
||||
for (; leftIdx < rightIdx; leftIdx++) {
|
||||
const newNode = getNewNode(parentNode, newChildren[leftIdx], null);
|
||||
newNode = getNewNode(parentNode, newChildren[leftIdx], null);
|
||||
|
||||
if (newNode !== null) {
|
||||
theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing);
|
||||
|
@ -368,9 +374,11 @@ function diffArrayNodes(
|
|||
const preIndex: Array<number> = []; // 贪心算法在替换的过程中会使得数组不正确,通过记录preIndex找到正确值
|
||||
const reuseNodes = []; // 记录复用的 VNode
|
||||
let i = 0;
|
||||
let oldNodeFromMap;
|
||||
let last;
|
||||
for (; leftIdx < rightIdx; leftIdx++) {
|
||||
const oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]);
|
||||
const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap);
|
||||
oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]);
|
||||
newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap);
|
||||
if (newNode !== null) {
|
||||
if (isComparing && !newNode.isCreated) {
|
||||
// 从Map删除,后面不会deleteVNode
|
||||
|
@ -380,7 +388,7 @@ function diffArrayNodes(
|
|||
if (oldNodeFromMap !== null) {
|
||||
let eIndex = newNode.eIndex;
|
||||
eIndexes.push(eIndex);
|
||||
const last: number | undefined = eIndexes[result[result.length - 1]];
|
||||
last = eIndexes[result[result.length - 1]];
|
||||
if (eIndex > last || last === undefined) { // 大的 eIndex直接放在最后
|
||||
preIndex[i] = result[result.length - 1];
|
||||
result.push(i);
|
||||
|
@ -458,16 +466,6 @@ function setVNodesCIndex(startChild: VNode, startIdx: number) {
|
|||
}
|
||||
}
|
||||
|
||||
// 新节点是数组类型
|
||||
function diffArrayNodesHandler(
|
||||
parentNode: VNode,
|
||||
firstChild: VNode | null,
|
||||
newChildren: Array<any>,
|
||||
isComparing: boolean = true
|
||||
): VNode | null {
|
||||
return diffArrayNodes(parentNode, firstChild, newChildren, isComparing);
|
||||
}
|
||||
|
||||
// 新节点是迭代器类型
|
||||
function diffIteratorNodesHandler(
|
||||
parentNode: VNode,
|
||||
|
@ -486,7 +484,7 @@ function diffIteratorNodesHandler(
|
|||
result = iteratorObj.next();
|
||||
}
|
||||
|
||||
return diffArrayNodes(parentNode, firstChild, childrenArray, isComparing);
|
||||
return diffArrayNodesHandler(parentNode, firstChild, childrenArray, isComparing);
|
||||
}
|
||||
|
||||
// 新节点是字符串类型
|
||||
|
@ -548,13 +546,13 @@ function diffObjectNodeHandler(
|
|||
// 可以复用
|
||||
if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) {
|
||||
resultNode = updateVNode(canReuseNode, newChild.props.children);
|
||||
startDelVNode = getSiblingVNode(canReuseNode);
|
||||
startDelVNode = canReuseNode.next;
|
||||
resultNode.next = null;
|
||||
} else if (isSameType(canReuseNode, newChild)) {
|
||||
resultNode = updateVNode(canReuseNode, newChild.props);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
startDelVNode = getSiblingVNode(resultNode);
|
||||
startDelVNode = resultNode.next;
|
||||
resultNode.next = null;
|
||||
}
|
||||
}
|
||||
|
@ -574,7 +572,7 @@ function diffObjectNodeHandler(
|
|||
// 可以复用
|
||||
if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) {
|
||||
resultNode = updateVNode(canReuseNode, newChild.children || []);
|
||||
startDelVNode = getSiblingVNode(canReuseNode);
|
||||
startDelVNode = canReuseNode.next;
|
||||
resultNode.next = null;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +623,7 @@ export function createChildrenByDiff(
|
|||
}
|
||||
|
||||
// 3. newChild是数组类型
|
||||
if (isArrayType(newChild)) {
|
||||
if (Array.isArray(newChild)) {
|
||||
return diffArrayNodesHandler(parentNode, firstChild, newChild, isComparing);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,9 +66,7 @@ export function captureVNode(processing: VNode): VNode | null {
|
|||
|
||||
// 创建孩子节点
|
||||
export function createVNodeChildren(processing: VNode, nextChildren: any) {
|
||||
const isComparing = !processing.isCreated;
|
||||
|
||||
return createChildrenByDiff(processing, processing.child, nextChildren, isComparing);
|
||||
return createChildrenByDiff(processing, processing.child, nextChildren, !processing.isCreated);
|
||||
}
|
||||
|
||||
export function markRef(processing: VNode) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
markComponentDidUpdate,
|
||||
markGetSnapshotBeforeUpdate,
|
||||
} from './class/ClassLifeCycleProcessor';
|
||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||
import { FlagUtils, DidCapture } from '../vnode/VNodeFlags';
|
||||
import { createVNodeChildren, markRef } from './BaseComponent';
|
||||
import {
|
||||
createUpdateArray,
|
||||
|
@ -73,7 +73,7 @@ function createChildren(clazz: any, processing: VNode) {
|
|||
processing.state = processing.realNode.state;
|
||||
|
||||
const inst = processing.realNode;
|
||||
const isCatchError = processing.flags.DidCapture;
|
||||
const isCatchError = (processing.flags & DidCapture) === DidCapture;
|
||||
|
||||
// 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null
|
||||
const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function'
|
||||
|
@ -122,7 +122,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
|
|||
const newContext = getCurrentContext(clazz, processing);
|
||||
|
||||
// 子节点抛出异常时,如果本class是个捕获异常的处理节点,这时候oldProps是null,所以需要使用props
|
||||
let oldProps = processing.flags.DidCapture ? processing.props : processing.oldProps;
|
||||
let oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps;
|
||||
if (processing.isLazyComponent) {
|
||||
oldProps = mergeDefaultProps(processing.type, oldProps);
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
|
|||
inst.props = nextProps;
|
||||
}
|
||||
// 如果捕获了 error,必须更新
|
||||
const isCatchError = processing.flags.DidCapture;
|
||||
const isCatchError = (processing.flags & DidCapture) === DidCapture;
|
||||
shouldUpdate = isCatchError || shouldUpdate;
|
||||
|
||||
// 更新ref
|
||||
|
|
|
@ -50,7 +50,7 @@ function handleContextChange(processing: VNode, context: ContextType<any>): void
|
|||
// 从vNode开始遍历
|
||||
travelVNodeTree(vNode, node => {
|
||||
const depContexts = node.depContexts;
|
||||
if (depContexts.length) {
|
||||
if (depContexts && depContexts.length) {
|
||||
isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
|
||||
}
|
||||
}, node =>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type {VNode} from '../Types';
|
||||
import type {Props} from '../../dom/DOMOperator';
|
||||
import type { VNode } from '../Types';
|
||||
import type { Props } from '../../dom/DOMOperator';
|
||||
|
||||
import {
|
||||
getNamespaceCtx,
|
||||
|
@ -12,10 +12,10 @@ import {
|
|||
initDomProps, getPropChangeList,
|
||||
isTextChild,
|
||||
} from '../../dom/DOMOperator';
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import {createVNodeChildren, markRef} from './BaseComponent';
|
||||
import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags';
|
||||
import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils';
|
||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||
import { createVNodeChildren, markRef } from './BaseComponent';
|
||||
import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags';
|
||||
import { travelVNodeTree } from '../vnode/VNodeUtils';
|
||||
|
||||
function updateDom(
|
||||
processing: VNode,
|
||||
|
@ -50,24 +50,6 @@ function updateDom(
|
|||
}
|
||||
}
|
||||
|
||||
// 把dom类型的子节点append到parent dom中
|
||||
function appendAllChildren(parent: Element, processing: VNode) {
|
||||
const vNode = processing.child;
|
||||
if (vNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 向下递归它的子节点,查找所有终端节点。
|
||||
travelVNodeTree(vNode, node => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
appendChildElement(parent, node.realNode);
|
||||
}
|
||||
}, node =>
|
||||
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
||||
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal
|
||||
, processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
|
||||
|
@ -95,7 +77,19 @@ export function bubbleRender(processing: VNode) {
|
|||
processing,
|
||||
);
|
||||
|
||||
appendAllChildren(dom, processing);
|
||||
// 把dom类型的子节点append到parent dom中
|
||||
const vNode = processing.child;
|
||||
if (vNode !== null) {
|
||||
// 向下递归它的子节点,查找所有终端节点。
|
||||
travelVNodeTree(vNode, node => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
appendChildElement(dom, node.realNode);
|
||||
}
|
||||
}, node =>
|
||||
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
||||
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal
|
||||
, processing);
|
||||
}
|
||||
|
||||
processing.realNode = dom;
|
||||
|
||||
|
|
|
@ -27,23 +27,24 @@ export function isSchedulingEffects() {
|
|||
|
||||
export function callUseEffects(vNode: VNode) {
|
||||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
effectList.forEach(effect => {
|
||||
const {effectConstant} = effect;
|
||||
if (
|
||||
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
|
||||
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
|
||||
) {
|
||||
hookEffects.push(effect);
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
// 异步调用
|
||||
if (!isScheduling) {
|
||||
isScheduling = true;
|
||||
runAsync(runAsyncEffects);
|
||||
if (effectList !== null) {
|
||||
effectList.forEach(effect => {
|
||||
const {effectConstant} = effect;
|
||||
if (
|
||||
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
|
||||
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
|
||||
) {
|
||||
hookEffects.push(effect);
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
// 异步调用
|
||||
if (!isScheduling) {
|
||||
isScheduling = true;
|
||||
runAsync(runAsyncEffects);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function runAsyncEffects() {
|
||||
|
@ -85,23 +86,24 @@ export function runAsyncEffects() {
|
|||
// 在销毁vNode的时候调用remove
|
||||
export function callEffectRemove(vNode: VNode) {
|
||||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
effectList.forEach(effect => {
|
||||
const {removeEffect, effectConstant} = effect;
|
||||
|
||||
if (removeEffect !== undefined) {
|
||||
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect,就异步调用
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
if (!isScheduling) {
|
||||
isScheduling = true;
|
||||
runAsync(runAsyncEffects);
|
||||
if (effectList !== null) {
|
||||
effectList.forEach(effect => {
|
||||
const {removeEffect, effectConstant} = effect;
|
||||
|
||||
if (removeEffect !== undefined) {
|
||||
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect,就异步调用
|
||||
hookRemoveEffects.push(effect);
|
||||
|
||||
if (!isScheduling) {
|
||||
isScheduling = true;
|
||||
runAsync(runAsyncEffects);
|
||||
}
|
||||
} else { // 是useLayoutEffect,直接执行
|
||||
removeEffect();
|
||||
}
|
||||
} else { // 是useLayoutEffect,直接执行
|
||||
removeEffect();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 同步执行UseLayoutEffect的remove
|
||||
|
@ -109,26 +111,29 @@ export function callUseLayoutEffectRemove(vNode: VNode) {
|
|||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const remove = effect.removeEffect;
|
||||
effect.removeEffect = undefined;
|
||||
if (typeof remove === 'function') {
|
||||
remove();
|
||||
if (effectList !== null) {
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const remove = effect.removeEffect;
|
||||
effect.removeEffect = undefined;
|
||||
if (typeof remove === 'function') {
|
||||
remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 同步执行UseLayoutEffect
|
||||
export function callUseLayoutEffectCreate(vNode: VNode) {
|
||||
const effectList: EffectList = vNode.effectList;
|
||||
|
||||
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const create = effect.effect;
|
||||
effect.removeEffect = create();
|
||||
}
|
||||
});
|
||||
if (effectList !== null) {
|
||||
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||
effectList.forEach(effect => {
|
||||
if ((effect.effectConstant & layoutLabel) === layoutLabel) {
|
||||
const create = effect.effect;
|
||||
effect.removeEffect = create();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
SuspenseComponent,
|
||||
MemoComponent,
|
||||
} from '../vnode/VNodeTags';
|
||||
import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags';
|
||||
import { FlagUtils, ResetText, Clear, Update } from '../vnode/VNodeFlags';
|
||||
import { mergeDefaultProps } from '../render/LazyComponent';
|
||||
import {
|
||||
submitDomUpdate,
|
||||
|
@ -39,7 +39,7 @@ import {
|
|||
travelVNodeTree,
|
||||
clearVNode,
|
||||
isDomVNode,
|
||||
findDomParent, getSiblingDom,
|
||||
getSiblingDom,
|
||||
} from '../vnode/VNodeUtils';
|
||||
import { shouldAutoFocus } from '../../dom/utils/Common';
|
||||
|
||||
|
@ -72,13 +72,14 @@ function callBeforeSubmitLifeCycles(
|
|||
// 调用vNode.stateCallbacks
|
||||
function callStateCallback(vNode: VNode, obj: any): void {
|
||||
const stateCallbacks = vNode.stateCallbacks;
|
||||
vNode.stateCallbacks = [];
|
||||
|
||||
stateCallbacks.forEach(callback => {
|
||||
if (typeof callback === 'function') {
|
||||
callback.call(obj);
|
||||
}
|
||||
});
|
||||
vNode.stateCallbacks = null;
|
||||
if (stateCallbacks !== null) {
|
||||
stateCallbacks.forEach(callback => {
|
||||
if (typeof callback === 'function') {
|
||||
callback.call(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 调用界面变化后的生命周期
|
||||
|
@ -96,7 +97,7 @@ function callAfterSubmitLifeCycles(
|
|||
}
|
||||
case ClassComponent: {
|
||||
const instance = vNode.realNode;
|
||||
if (vNode.flags.Update) {
|
||||
if ((vNode.flags & Update) === Update) {
|
||||
if (vNode.isCreated) {
|
||||
instance.componentDidMount();
|
||||
} else {
|
||||
|
@ -123,7 +124,7 @@ function callAfterSubmitLifeCycles(
|
|||
return;
|
||||
}
|
||||
case DomComponent: {
|
||||
if (vNode.isCreated && vNode.flags.Update) {
|
||||
if (vNode.isCreated && (vNode.flags & Update) === Update) {
|
||||
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||
if (shouldAutoFocus(vNode.type, vNode.props)) {
|
||||
vNode.realNode.focus();
|
||||
|
@ -219,9 +220,22 @@ function unmountNestedVNodes(vNode: VNode): void {
|
|||
}
|
||||
|
||||
function submitAddition(vNode: VNode): void {
|
||||
const { parent, parentDom } = findDomParent(vNode);
|
||||
let parent = vNode.parent;
|
||||
let parentDom;
|
||||
let tag;
|
||||
while (parent !== null) {
|
||||
tag = parent.tag;
|
||||
if (tag === DomComponent) {
|
||||
parentDom = parent.realNode;
|
||||
break;
|
||||
} else if (tag === TreeRoot || tag === DomPortal) {
|
||||
parentDom = parent.outerDom;
|
||||
break;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
if (parent.flags.ResetText) {
|
||||
if ((parent.flags & ResetText) === ResetText) {
|
||||
// 在insert之前先reset
|
||||
clearText(parentDom);
|
||||
FlagUtils.removeFlag(parent, ResetText);
|
||||
|
@ -239,11 +253,7 @@ function insertOrAppendPlacementNode(
|
|||
const { tag, realNode } = node;
|
||||
|
||||
if (isDomVNode(node)) {
|
||||
if (beforeDom) {
|
||||
insertDomBefore(parent, realNode, beforeDom);
|
||||
} else {
|
||||
appendChildElement(parent, realNode);
|
||||
}
|
||||
insertDom(parent, realNode, beforeDom);
|
||||
} else if (tag === DomPortal) {
|
||||
// 这里不做处理,直接在portal中处理
|
||||
} else {
|
||||
|
@ -256,6 +266,14 @@ function insertOrAppendPlacementNode(
|
|||
}
|
||||
}
|
||||
|
||||
function insertDom(parent, realNode, beforeDom) {
|
||||
if (beforeDom) {
|
||||
insertDomBefore(parent, realNode, beforeDom);
|
||||
} else {
|
||||
appendChildElement(parent, realNode);
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount()
|
||||
function unmountDomComponents(vNode: VNode): void {
|
||||
let currentParentIsValid = false;
|
||||
|
@ -265,8 +283,19 @@ function unmountDomComponents(vNode: VNode): void {
|
|||
|
||||
travelVNodeTree(vNode, (node) => {
|
||||
if (!currentParentIsValid) {
|
||||
const parentObj = findDomParent(node);
|
||||
currentParent = parentObj.parentDom;
|
||||
let parent = node.parent;
|
||||
let tag;
|
||||
while (parent !== null) {
|
||||
tag = parent.tag;
|
||||
if (tag === DomComponent) {
|
||||
currentParent = parent.realNode;
|
||||
break;
|
||||
} else if (tag === TreeRoot || tag === DomPortal) {
|
||||
currentParent = parent.outerDom;
|
||||
break;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
currentParentIsValid = true;
|
||||
}
|
||||
|
||||
|
@ -310,8 +339,20 @@ function submitClear(vNode: VNode): void {
|
|||
}
|
||||
}
|
||||
|
||||
const parentObj = findDomParent(vNode);
|
||||
const currentParent = parentObj.parentDom;
|
||||
let parent = vNode.parent;
|
||||
let parentDom;
|
||||
let tag;
|
||||
while (parent !== null) {
|
||||
tag = parent.tag;
|
||||
if (tag === DomComponent) {
|
||||
parentDom = parent.realNode;
|
||||
break;
|
||||
} else if (tag === TreeRoot || tag === DomPortal) {
|
||||
parentDom = parent.outerDom;
|
||||
break;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
let clearChild = vNode.clearChild as VNode; // 上次渲染的child保存在clearChild属性中
|
||||
// 卸载 clearChild 和 它的兄弟节点
|
||||
while(clearChild) {
|
||||
|
@ -322,8 +363,9 @@ function submitClear(vNode: VNode): void {
|
|||
}
|
||||
|
||||
// 在所有子项都卸载后,删除dom树中的节点
|
||||
removeChildDom(currentParent, vNode.realNode);
|
||||
currentParent.append(cloneDom);
|
||||
removeChildDom(parentDom, vNode.realNode);
|
||||
const realNodeNext = getSiblingDom(vNode);
|
||||
insertDom(parentDom, cloneDom, realNodeNext);
|
||||
vNode.realNode = cloneDom;
|
||||
attachRef(vNode);
|
||||
FlagUtils.removeFlag(vNode, Clear);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {callRenderQueueImmediate} from '../taskExecutor/RenderQueue';
|
||||
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||
import {FlagUtils, Addition as AdditionFlag} from '../vnode/VNodeFlags';
|
||||
import {FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback} from '../vnode/VNodeFlags';
|
||||
import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator';
|
||||
import {handleSubmitError} from '../ErrorHandler';
|
||||
import {
|
||||
|
@ -40,11 +38,15 @@ export function submitToRender(treeRoot) {
|
|||
|
||||
if (FlagUtils.hasAnyFlag(startVNode)) {
|
||||
// 把自己加上
|
||||
startVNode.dirtyNodes.push(startVNode);
|
||||
if (startVNode.dirtyNodes === null) {
|
||||
startVNode.dirtyNodes = [startVNode];
|
||||
} else {
|
||||
startVNode.dirtyNodes.push(startVNode);
|
||||
}
|
||||
}
|
||||
|
||||
const dirtyNodes = startVNode.dirtyNodes;
|
||||
if (dirtyNodes.length) {
|
||||
if (dirtyNodes !== null && dirtyNodes.length) {
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(InRender, true);
|
||||
|
||||
|
@ -60,7 +62,9 @@ export function submitToRender(treeRoot) {
|
|||
// after submit阶段
|
||||
afterSubmit(dirtyNodes);
|
||||
|
||||
setExecuteMode(preMode)
|
||||
setExecuteMode(preMode);
|
||||
dirtyNodes.length = 0;
|
||||
startVNode.dirtyNodes = null;
|
||||
}
|
||||
|
||||
if (isSchedulingEffects()) {
|
||||
|
@ -85,7 +89,7 @@ export function submitToRender(treeRoot) {
|
|||
function beforeSubmit(dirtyNodes: Array<VNode>) {
|
||||
dirtyNodes.forEach(node => {
|
||||
try {
|
||||
if (node.flags.Snapshot) {
|
||||
if ((node.flags & Snapshot) === Snapshot) {
|
||||
callBeforeSubmitLifeCycles(node);
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -97,35 +101,38 @@ function beforeSubmit(dirtyNodes: Array<VNode>) {
|
|||
function submit(dirtyNodes: Array<VNode>) {
|
||||
dirtyNodes.forEach(node => {
|
||||
try {
|
||||
if (node.flags.ResetText) {
|
||||
if ((node.flags & ResetText) === ResetText) {
|
||||
submitResetTextContent(node);
|
||||
}
|
||||
|
||||
if (node.flags.Ref) {
|
||||
if ((node.flags & Ref) === Ref) {
|
||||
if (!node.isCreated) {
|
||||
// 需要执行
|
||||
detachRef(node, true);
|
||||
}
|
||||
}
|
||||
|
||||
const {Addition, Update, Deletion, Clear} = node.flags;
|
||||
if (Addition && Update) {
|
||||
const isAdd = (node.flags & Addition) === Addition;
|
||||
const isUpdate = (node.flags & Update) === Update;
|
||||
if (isAdd && isUpdate) {
|
||||
// Addition
|
||||
submitAddition(node);
|
||||
FlagUtils.removeFlag(node, AdditionFlag);
|
||||
FlagUtils.removeFlag(node, Addition);
|
||||
|
||||
// Update
|
||||
submitUpdate(node);
|
||||
} else {
|
||||
if (Addition) {
|
||||
const isDeletion = (node.flags & Deletion) === Deletion;
|
||||
const isClear = (node.flags & Clear) === Clear;
|
||||
if (isAdd) {
|
||||
submitAddition(node);
|
||||
FlagUtils.removeFlag(node, AdditionFlag);
|
||||
} else if (Update) {
|
||||
FlagUtils.removeFlag(node, Addition);
|
||||
} else if (isUpdate) {
|
||||
submitUpdate(node);
|
||||
} else if (Deletion) {
|
||||
} else if (isDeletion) {
|
||||
submitDeletion(node);
|
||||
}
|
||||
if (Clear) {
|
||||
if (isClear) {
|
||||
submitClear(node);
|
||||
}
|
||||
}
|
||||
|
@ -138,11 +145,11 @@ function submit(dirtyNodes: Array<VNode>) {
|
|||
function afterSubmit(dirtyNodes: Array<VNode>) {
|
||||
dirtyNodes.forEach(node => {
|
||||
try {
|
||||
if (node.flags.Update || node.flags.Callback) {
|
||||
if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) {
|
||||
callAfterSubmitLifeCycles(node);
|
||||
}
|
||||
|
||||
if (node.flags.Ref) {
|
||||
if ((node.flags & Ref) === Ref) {
|
||||
attachRef(node);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
/**
|
||||
* 虚拟DOM结构体
|
||||
*/
|
||||
import {TreeRoot} from './VNodeTags';
|
||||
import type {VNodeTag} from './VNodeTags';
|
||||
import type {RefType, ContextType} from '../Types';
|
||||
import type {Hook} from '../hooks/HookType';
|
||||
import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, ClsOrFunComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags';
|
||||
import type { VNodeTag } from './VNodeTags';
|
||||
import type { RefType, ContextType } from '../Types';
|
||||
import type { Hook } from '../hooks/HookType';
|
||||
import { InitFlag } from './VNodeFlags';
|
||||
|
||||
export class VNode {
|
||||
tag: VNodeTag;
|
||||
key: string | null; // 唯一标识符
|
||||
props: any; // 传给组件的props的值,类组件包含defaultProps,Lazy组件不包含
|
||||
type: any = null;
|
||||
realNode: any = null; // 如果是类,则存放实例;如果是div这种,则存放真实DOM;
|
||||
realNode: any; // 如果是类,则存放实例;如果是div这种,则存放真实DOM;
|
||||
|
||||
// 关系结构
|
||||
parent: VNode | null = null; // 父节点
|
||||
|
@ -20,62 +22,48 @@ export class VNode {
|
|||
eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致
|
||||
|
||||
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上
|
||||
props: any; // 传给组件的props的值,类组件包含defaultProps,Lazy组件不包含
|
||||
oldProps: any = null;
|
||||
|
||||
suspensePromises: any = null; // suspense组件的promise列表
|
||||
changeList: any = null; // DOM的变更列表
|
||||
effectList: any[] = []; // useEffect 的更新数组
|
||||
updates: any[] | null = null; // TreeRoot和ClassComponent使用的更新数组
|
||||
stateCallbacks: any[] = []; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
|
||||
isForceUpdate: boolean = false; // 是否使用强制更新
|
||||
suspensePromises: any; // suspense组件的promise列表
|
||||
changeList: any; // DOM的变更列表
|
||||
effectList: any[] | null; // useEffect 的更新数组
|
||||
updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组
|
||||
stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
|
||||
isForceUpdate: boolean; // 是否使用强制更新
|
||||
|
||||
state: any = null; // ClassComponent和TreeRoot的状态
|
||||
hooks: Array<Hook<any, any>> = []; // 保存hook
|
||||
state: any; // ClassComponent和TreeRoot的状态
|
||||
hooks: Array<Hook<any, any>> | null; // 保存hook
|
||||
suspenseChildStatus: string = ''; // Suspense的Children是否显示
|
||||
depContexts: Array<ContextType<any>> = []; // FunctionComponent和ClassComponent对context的依赖列表
|
||||
isDepContextChange: boolean = false; // context是否变更
|
||||
dirtyNodes: Array<VNode> = []; // 需要改动的节点数组
|
||||
depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表
|
||||
isDepContextChange: boolean; // context是否变更
|
||||
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
|
||||
shouldUpdate: boolean = false;
|
||||
childShouldUpdate: boolean = false;
|
||||
outerDom: any;
|
||||
task: any;
|
||||
|
||||
// 使用这个变量来记录修改前的值,用于恢复。
|
||||
contexts = {};
|
||||
contexts: any;
|
||||
// 因为LazyComponent会修改tag和type属性,为了能识别,增加一个属性
|
||||
isLazyComponent: boolean = false;
|
||||
isLazyComponent: boolean;
|
||||
|
||||
// 因为LazyComponent会修改type属性,为了在diff中判断是否可以复用,需要增加一个lazyType
|
||||
lazyType: any = null;
|
||||
flags: {
|
||||
Addition?: boolean;
|
||||
Update?: boolean;
|
||||
Deletion?: boolean;
|
||||
ResetText?: boolean;
|
||||
Callback?: boolean;
|
||||
DidCapture?: boolean;
|
||||
Ref?: boolean;
|
||||
Snapshot?: boolean;
|
||||
Interrupted?: boolean;
|
||||
ShouldCapture?: boolean;
|
||||
ForceUpdate?: boolean;
|
||||
Clear?: boolean;
|
||||
} = {};
|
||||
clearChild: VNode | null = null;
|
||||
lazyType: any;
|
||||
flags = InitFlag;
|
||||
clearChild: VNode | null;
|
||||
// one tree相关属性
|
||||
isCreated: boolean = true;
|
||||
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook
|
||||
oldState: any = null;
|
||||
oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook
|
||||
oldState: any;
|
||||
oldRef: RefType | ((handle: any) => void) | null = null;
|
||||
suspenseChildThrow = false;
|
||||
oldSuspenseChildStatus: string = ''; // 上一次Suspense的Children是否显示
|
||||
suspenseChildThrow: boolean;
|
||||
oldSuspenseChildStatus: string; // 上一次Suspense的Children是否显示
|
||||
oldChild: VNode | null = null;
|
||||
suspenseDidCapture: boolean = false; // suspense是否捕获了异常
|
||||
promiseResolve: boolean = false; // suspense的promise是否resolve
|
||||
suspenseDidCapture: boolean; // suspense是否捕获了异常
|
||||
promiseResolve: boolean; // suspense的promise是否resolve
|
||||
|
||||
path: Array<number> = []; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null = null; // 保存要更新的节点
|
||||
path: string = ''; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||
|
||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
|
||||
|
@ -85,18 +73,82 @@ export class VNode {
|
|||
|
||||
this.props = props;
|
||||
|
||||
// 根节点
|
||||
if (tag === TreeRoot) {
|
||||
this.outerDom = outerDom;
|
||||
this.task = null;
|
||||
this.toUpdateNodes = new Set<VNode>();
|
||||
switch (tag) {
|
||||
case TreeRoot:
|
||||
this.outerDom = outerDom;
|
||||
this.task = null;
|
||||
this.toUpdateNodes = new Set<VNode>();
|
||||
this.realNode = null;
|
||||
this.updates = null;
|
||||
this.stateCallbacks = null;
|
||||
this.state = null;
|
||||
this.oldState = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case FunctionComponent:
|
||||
this.effectList = null;
|
||||
this.hooks = null;
|
||||
this.depContexts = null;
|
||||
this.isDepContextChange = false;
|
||||
this.oldHooks = null;
|
||||
break;
|
||||
case ClassComponent:
|
||||
this.realNode = null;
|
||||
this.updates = null;
|
||||
this.stateCallbacks = null;
|
||||
this.isForceUpdate = false;
|
||||
this.state = null;
|
||||
this.depContexts = null;
|
||||
this.isDepContextChange = false;
|
||||
this.oldState = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case ClsOrFunComponent:
|
||||
this.realNode = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case DomPortal:
|
||||
this.realNode = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case DomComponent:
|
||||
this.realNode = null;
|
||||
this.changeList = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case DomText:
|
||||
this.realNode = null;
|
||||
break;
|
||||
case SuspenseComponent:
|
||||
this.realNode = null;
|
||||
this.suspensePromises = null;
|
||||
this.suspenseChildThrow = false;
|
||||
this.suspenseDidCapture = false;
|
||||
this.promiseResolve = false;
|
||||
this.oldSuspenseChildStatus = '';
|
||||
break;
|
||||
case ContextProvider:
|
||||
this.contexts = null;
|
||||
break;
|
||||
case MemoComponent:
|
||||
this.effectList = null;
|
||||
break;
|
||||
case LazyComponent:
|
||||
this.realNode = null;
|
||||
this.stateCallbacks = null;
|
||||
this.isLazyComponent = true;
|
||||
this.lazyType = null;
|
||||
break;
|
||||
case Fragment:
|
||||
break;
|
||||
case ContextConsumer:
|
||||
break;
|
||||
case ForwardRef:
|
||||
break;
|
||||
case Profiler:
|
||||
break;
|
||||
case IncompleteClassComponent:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setContext(contextName, value) {
|
||||
this.contexts[contextName] = value;
|
||||
}
|
||||
getContext(contextName) {
|
||||
return this.contexts[contextName];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,34 +78,12 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
|
|||
vNode.oldRef = vNode.ref;
|
||||
|
||||
FlagUtils.setNoFlags(vNode);
|
||||
vNode.dirtyNodes = [];
|
||||
vNode.dirtyNodes = null;
|
||||
vNode.isCreated = false;
|
||||
|
||||
return vNode;
|
||||
}
|
||||
|
||||
function getVNodeTag(type: any) {
|
||||
let vNodeTag = ClsOrFunComponent;
|
||||
let isLazy = false;
|
||||
const componentType = typeof type;
|
||||
|
||||
if (componentType === 'function') {
|
||||
if (isClassComponent(type)) {
|
||||
vNodeTag = ClassComponent;
|
||||
}
|
||||
} else if (componentType === 'string') {
|
||||
vNodeTag = DomComponent;
|
||||
} else if (type === TYPE_SUSPENSE) {
|
||||
vNodeTag = SuspenseComponent;
|
||||
} else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {
|
||||
vNodeTag = typeMap[type.vtype];
|
||||
isLazy = type.vtype === TYPE_LAZY;
|
||||
} else {
|
||||
throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`);
|
||||
}
|
||||
return { vNodeTag, isLazy };
|
||||
}
|
||||
|
||||
export function createFragmentVNode(fragmentKey, fragmentProps) {
|
||||
const vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps);
|
||||
vNode.shouldUpdate = true;
|
||||
|
@ -127,14 +105,29 @@ export function createPortalVNode(portal) {
|
|||
}
|
||||
|
||||
export function createUndeterminedVNode(type, key, props) {
|
||||
const { vNodeTag, isLazy } = getVNodeTag(type);
|
||||
let vNodeTag = ClsOrFunComponent;
|
||||
let isLazy = false;
|
||||
const componentType = typeof type;
|
||||
|
||||
if (componentType === 'function') {
|
||||
if (isClassComponent(type)) {
|
||||
vNodeTag = ClassComponent;
|
||||
}
|
||||
} else if (componentType === 'string') {
|
||||
vNodeTag = DomComponent;
|
||||
} else if (type === TYPE_SUSPENSE) {
|
||||
vNodeTag = SuspenseComponent;
|
||||
} else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {
|
||||
vNodeTag = typeMap[type.vtype];
|
||||
isLazy = type.vtype === TYPE_LAZY;
|
||||
} else {
|
||||
throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`);
|
||||
}
|
||||
|
||||
const vNode = newVirtualNode(vNodeTag, key, props);
|
||||
vNode.type = type;
|
||||
vNode.shouldUpdate = true;
|
||||
|
||||
// lazy类型的特殊处理
|
||||
vNode.isLazyComponent = isLazy;
|
||||
if (isLazy) {
|
||||
vNode.lazyType = type;
|
||||
}
|
||||
|
@ -143,7 +136,7 @@ export function createUndeterminedVNode(type, key, props) {
|
|||
|
||||
export function createTreeRootVNode(container) {
|
||||
const vNode = newVirtualNode(TreeRoot, null, null, container);
|
||||
vNode.path.push(0);
|
||||
vNode.path += 0;
|
||||
createUpdateArray(vNode);
|
||||
return vNode;
|
||||
}
|
||||
|
@ -155,7 +148,7 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) {
|
|||
case TreeRoot:
|
||||
// 创建treeRoot
|
||||
vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]);
|
||||
vNode.path.push(0);
|
||||
vNode.path += 0;
|
||||
|
||||
createUpdateArray(vNode);
|
||||
break;
|
||||
|
@ -165,7 +158,7 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) {
|
|||
}
|
||||
|
||||
export function updateVNodePath(vNode: VNode) {
|
||||
vNode.path = [...vNode.parent.path, vNode.cIndex];
|
||||
vNode.path = vNode.parent.path + vNode.cIndex;
|
||||
}
|
||||
|
||||
export function createVNodeFromElement(element: JSXElement): VNode {
|
||||
|
|
|
@ -4,97 +4,79 @@
|
|||
|
||||
import type { VNode } from '../Types';
|
||||
|
||||
|
||||
export const InitFlag = /** */ 0b000000000000;
|
||||
// vNode节点的flags
|
||||
export const Addition = 'Addition';
|
||||
export const Update = 'Update';
|
||||
export const Deletion = 'Deletion';
|
||||
export const ResetText = 'ResetText';
|
||||
export const Callback = 'Callback';
|
||||
export const DidCapture = 'DidCapture';
|
||||
export const Ref = 'Ref';
|
||||
export const Snapshot = 'Snapshot';
|
||||
// 被中断了,抛出错误的vNode以及它的父vNode
|
||||
export const Interrupted = 'Interrupted';
|
||||
export const ShouldCapture = 'ShouldCapture';
|
||||
// For suspense
|
||||
export const ForceUpdate = 'ForceUpdate';
|
||||
export const Clear = 'Clear';
|
||||
|
||||
const FlagArr = [Addition, Update, Deletion, Clear, ResetText, Callback,
|
||||
DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
|
||||
|
||||
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
|
||||
|
||||
function resetFlag(node) {
|
||||
node.flags = {};
|
||||
}
|
||||
export const Addition = /** */ 0b100000000000;
|
||||
export const Update = /** */ 0b010000000000;
|
||||
export const Deletion = /** */ 0b001000000000;
|
||||
export const ResetText =/** */ 0b000100000000;
|
||||
export const Callback = /** */ 0b000010000000;
|
||||
export const DidCapture =/** */ 0b000001000000;
|
||||
export const Ref = /** */ 0b000000100000;
|
||||
export const Snapshot = /** */ 0b000000010000;
|
||||
export const Interrupted = /** */ 0b000000001000; // 被中断了,抛出错误的vNode以及它的父vNode
|
||||
export const ShouldCapture =/** */ 0b000000000100;
|
||||
export const ForceUpdate = /** */ 0b000000000010; // For suspense
|
||||
export const Clear = /** */ 0b000000000001;
|
||||
const LifecycleEffectArr = Update | Callback | Ref | Snapshot;
|
||||
|
||||
export class FlagUtils {
|
||||
static removeFlag(node: VNode, flag: string) {
|
||||
node.flags[flag] = false;
|
||||
static removeFlag(node: VNode, flag: number) {
|
||||
const flags = node.flags;
|
||||
node.flags = flags & (~flag);
|
||||
}
|
||||
static removeLifecycleEffectFlags(node) {
|
||||
LifecycleEffectArr.forEach(key => {
|
||||
node.flags[key] = false;
|
||||
});
|
||||
const flags = node.flags;
|
||||
node.flags = flags & (~LifecycleEffectArr);
|
||||
}
|
||||
static hasAnyFlag(node: VNode) { // 有标志位
|
||||
const flags = node.flags;
|
||||
const arrLength = FlagArr.length;
|
||||
let key;
|
||||
for (let i = 0; i < arrLength; i++) {
|
||||
key = FlagArr[i];
|
||||
if (flags[key]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return node.flags !== InitFlag;
|
||||
}
|
||||
|
||||
static setNoFlags(node: VNode) {
|
||||
resetFlag(node);
|
||||
node.flags = InitFlag;
|
||||
}
|
||||
|
||||
static markAddition(node: VNode) {
|
||||
node.flags.Addition = true;
|
||||
node.flags |= Addition;
|
||||
}
|
||||
static setAddition(node: VNode) {
|
||||
resetFlag(node);
|
||||
node.flags.Addition = true;
|
||||
node.flags = Addition;
|
||||
}
|
||||
static markUpdate(node: VNode) {
|
||||
node.flags.Update = true;
|
||||
node.flags |= Update;
|
||||
}
|
||||
static setDeletion(node: VNode) {
|
||||
resetFlag(node);
|
||||
node.flags.Deletion = true;
|
||||
node.flags = Deletion;
|
||||
}
|
||||
static markContentReset(node: VNode) {
|
||||
node.flags.ResetText = true;
|
||||
node.flags |= ResetText;
|
||||
}
|
||||
static markCallback(node: VNode) {
|
||||
node.flags.Callback = true;
|
||||
node.flags |= Callback;
|
||||
}
|
||||
static markDidCapture(node: VNode) {
|
||||
node.flags.DidCapture = true;
|
||||
node.flags |= DidCapture;
|
||||
}
|
||||
static markShouldCapture(node: VNode) {
|
||||
node.flags.ShouldCapture = true;
|
||||
node.flags |= ShouldCapture;
|
||||
}
|
||||
static markRef(node: VNode) {
|
||||
node.flags.Ref = true;
|
||||
node.flags |= Ref;
|
||||
}
|
||||
static markSnapshot(node: VNode) {
|
||||
node.flags.Snapshot = true;
|
||||
node.flags |= Snapshot;
|
||||
}
|
||||
static markInterrupted(node: VNode) {
|
||||
node.flags.Interrupted = true;
|
||||
node.flags |= Interrupted;
|
||||
}
|
||||
static markForceUpdate(node: VNode) {
|
||||
node.flags.ForceUpdate = true;
|
||||
node.flags |= ForceUpdate;
|
||||
}
|
||||
|
||||
static markClear(node: VNode) {
|
||||
node.flags.Clear = true;
|
||||
node.flags |= Clear;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,7 @@ import type {VNode} from '../Types';
|
|||
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
|
||||
import {isComment} from '../../dom/utils/Common';
|
||||
import {getNearestVNode} from '../../dom/DOMInternalKeys';
|
||||
|
||||
export function getSiblingVNode(node) {
|
||||
return node.next;
|
||||
}
|
||||
import { Addition, InitFlag } from './VNodeFlags';
|
||||
|
||||
export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) {
|
||||
let node: VNode | null = beginVNode;
|
||||
|
@ -58,7 +55,7 @@ export function travelVNodeTree(
|
|||
}
|
||||
|
||||
// 找兄弟,没有就往上再找兄弟
|
||||
while (getSiblingVNode(node) === null) {
|
||||
while (node.next === null) {
|
||||
if (node.parent === null || node.parent === overVNode) {
|
||||
return null;
|
||||
}
|
||||
|
@ -69,7 +66,7 @@ export function travelVNodeTree(
|
|||
}
|
||||
}
|
||||
// 找到兄弟
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
const siblingVNode = node.next;
|
||||
siblingVNode.parent = node.parent;
|
||||
node = siblingVNode;
|
||||
}
|
||||
|
@ -79,28 +76,24 @@ export function travelVNodeTree(
|
|||
export function clearVNode(vNode: VNode) {
|
||||
vNode.child = null;
|
||||
vNode.next = null;
|
||||
vNode.depContexts = [];
|
||||
vNode.dirtyNodes = [];
|
||||
vNode.depContexts = null;
|
||||
vNode.dirtyNodes = null;
|
||||
vNode.state = null;
|
||||
vNode.hooks = [];
|
||||
vNode.suspenseChildStatus = '';
|
||||
vNode.hooks = null;
|
||||
vNode.props = null;
|
||||
vNode.parent = null;
|
||||
vNode.suspensePromises = null;
|
||||
vNode.changeList = null;
|
||||
vNode.effectList = [];
|
||||
vNode.effectList = null;
|
||||
vNode.updates = null;
|
||||
vNode.realNode = null;
|
||||
|
||||
vNode.oldProps = null;
|
||||
vNode.oldHooks = [];
|
||||
vNode.oldHooks = null;
|
||||
vNode.oldState = null;
|
||||
vNode.oldRef = null;
|
||||
vNode.suspenseChildThrow = false;
|
||||
vNode.oldSuspenseChildStatus = '';
|
||||
vNode.oldChild = null;
|
||||
|
||||
vNode.path = [];
|
||||
vNode.toUpdateNodes = null;
|
||||
|
||||
vNode.belongClassVNode = null;
|
||||
|
@ -120,24 +113,6 @@ function isDomContainer(vNode: VNode): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
// 找到DOM类型的父
|
||||
export function findDomParent(vNode: VNode) {
|
||||
let parent = vNode.parent;
|
||||
|
||||
while (parent !== null) {
|
||||
switch (parent.tag) {
|
||||
case DomComponent:
|
||||
return {parent, parentDom: parent.realNode};
|
||||
case TreeRoot:
|
||||
case DomPortal:
|
||||
return {parent, parentDom: parent.outerDom};
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findDomVNode(vNode: VNode): VNode | null {
|
||||
return travelVNodeTree(vNode, (node) => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
|
@ -179,7 +154,7 @@ export function getSiblingDom(vNode: VNode): Element | null {
|
|||
|
||||
findSibling: while (true) {
|
||||
// 没有兄弟节点,找父节点
|
||||
while (getSiblingVNode(node) === null) {
|
||||
while (node.next === null) {
|
||||
// 没父节点,或父节点已经是根节点,则返回
|
||||
if (node.parent === null || isDomContainer(node.parent)) {
|
||||
return null;
|
||||
|
@ -187,14 +162,14 @@ export function getSiblingDom(vNode: VNode): Element | null {
|
|||
node = node.parent;
|
||||
}
|
||||
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
const siblingVNode = node.next;
|
||||
siblingVNode.parent = node.parent;
|
||||
node = siblingVNode;
|
||||
|
||||
// 如果不是dom节点,往下找
|
||||
while (!isDomVNode(node)) {
|
||||
// 如果节点也是Addition
|
||||
if (node.flags.Addition) {
|
||||
if ((node.flags & Addition) ===Addition) {
|
||||
continue findSibling;
|
||||
}
|
||||
|
||||
|
@ -208,7 +183,7 @@ export function getSiblingDom(vNode: VNode): Element | null {
|
|||
}
|
||||
}
|
||||
|
||||
if (!node.flags.Addition) {
|
||||
if ((node.flags & Addition) ===InitFlag) {
|
||||
// 找到
|
||||
return node.realNode;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue