Match-id-2cd3434dfcebed0f77a3028ccc5bca82c3fd9326

This commit is contained in:
* 2022-02-17 19:59:13 +08:00 committed by *
commit 305ab79bc5
20 changed files with 310 additions and 292 deletions

View File

@ -14,13 +14,9 @@ import {
TreeRoot, TreeRoot,
} from '../renderer/vnode/VNodeTags'; } from '../renderer/vnode/VNodeTags';
const prefix = '_horizon'; const INTERNAL_VNODE = '_horizon_VNode';
const INTERNAL_PROPS = '_horizon_Props';
const internalKeys = { const INTERNAL_NONDELEGATEEVENTS = '_horizon_NonDelegatedEvents';
VNode: `${prefix}VNode`,
props: `${prefix}Props`,
nonDelegatedEvents: `${prefix}NonDelegatedEvents`,
};
// 通过 VNode 实例获取 DOM 节点 // 通过 VNode 实例获取 DOM 节点
export function getDom(vNode: VNode): Element | Text | null { export function getDom(vNode: VNode): Element | Text | null {
@ -36,12 +32,12 @@ export function saveVNode(
vNode: VNode, vNode: VNode,
dom: Element | Text | Container, dom: Element | Text | Container,
): void { ): void {
dom[internalKeys.VNode] = vNode; dom[INTERNAL_VNODE] = vNode;
} }
// 用 DOM 节点,来找其对应的 VNode 实例 // 用 DOM 节点,来找其对应的 VNode 实例
export function getVNode(dom: Node|Container): VNode | null { 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) { if (vNode) {
const {tag} = vNode; const {tag} = vNode;
if (tag === DomComponent || tag === DomText || tag === TreeRoot) { if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
@ -53,7 +49,7 @@ export function getVNode(dom: Node|Container): VNode | null {
// 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode // 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode
export function getNearestVNode(dom: Node): null | VNode { export function getNearestVNode(dom: Node): null | VNode {
let vNode = dom[internalKeys.VNode]; let vNode = dom[INTERNAL_VNODE];
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例 if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
return vNode; return vNode;
} }
@ -62,7 +58,7 @@ export function getNearestVNode(dom: Node): null | VNode {
let parentDom = dom.parentNode; let parentDom = dom.parentNode;
let nearVNode = null; let nearVNode = null;
while (parentDom) { while (parentDom) {
vNode = parentDom[internalKeys.VNode]; vNode = parentDom[INTERNAL_VNODE];
if (vNode) { if (vNode) {
nearVNode = vNode; nearVNode = vNode;
break; break;
@ -74,19 +70,19 @@ export function getNearestVNode(dom: Node): null | VNode {
// 获取 vNode 上的属性相关信息 // 获取 vNode 上的属性相关信息
export function getVNodeProps(dom: Element | Text): Props | null{ export function getVNodeProps(dom: Element | Text): Props | null{
return dom[internalKeys.props] || null; return dom[INTERNAL_PROPS] || null;
} }
// 将 DOM 属性相关信息挂到 DOM 对象的特定属性上 // 将 DOM 属性相关信息挂到 DOM 对象的特定属性上
export function updateVNodeProps(dom: Element | Text, props: Props): void { 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> { export function getNonDelegatedListenerMap(dom: Element | Text): Map<string, EventListener> {
let eventsMap = dom[internalKeys.nonDelegatedEvents]; let eventsMap = dom[INTERNAL_NONDELEGATEEVENTS];
if (!eventsMap) { if (!eventsMap) {
eventsMap = new Map(); eventsMap = new Map();
dom[internalKeys.nonDelegatedEvents] = eventsMap; dom[INTERNAL_NONDELEGATEEVENTS] = eventsMap;
} }
return eventsMap; return eventsMap;
} }

View File

@ -6,7 +6,7 @@ import {
createDom, createDom,
} from './utils/DomCreator'; } from './utils/DomCreator';
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler'; 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 { NSS } from './utils/DomCreator';
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
@ -165,7 +165,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) {
// 应用diff更新Properties. // 应用diff更新Properties.
// 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false. // 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false.
if (type === 'input' && newProps.type === 'radio' && newProps.name != null && newProps.checked != null) { 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); const isNativeTag = isNativeElement(type, newProps);
updateDomProps(element, changeList, isNativeTag); updateDomProps(element, changeList, isNativeTag);

View File

@ -11,20 +11,18 @@ import { isEventProp, isNativeElement } from '../validators/ValidateProps';
function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) { function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
if (propName === 'style') { if (propName === 'style') {
setStyles(dom, propVal); 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)) { } else if (isEventProp(propName)) {
// 事件监听属性处理 // 事件监听属性处理
if (!allDelegatedHorizonEvents.has(propName)) { if (!allDelegatedHorizonEvents.has(propName)) {
listenNonDelegatedEvent(propName, dom, propVal); 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 { } else {
if (!isInit || (isInit && propVal != null)) { if (!isInit || (isInit && propVal != null)) {
updateCommonProp(dom, propName, propVal, isNativeTag); updateCommonProp(dom, propName, propVal, isNativeTag);
@ -40,10 +38,12 @@ export function setDomProps(
): void { ): void {
const isNativeTag = isNativeElement(tagName, props); const isNativeTag = isNativeElement(tagName, props);
const keysOfProps = Object.keys(props); const keysOfProps = Object.keys(props);
let propName;
for (let i = 0; i < keysOfProps.length; i++) { let propVal;
const propName = keysOfProps[i]; const keyLength = keysOfProps.length;
const propVal = props[propName]; for (let i = 0; i < keyLength; i++) {
propName = keysOfProps[i];
propVal = props[propName];
updateOneProp(dom, propName, isNativeTag, propVal, true); updateOneProp(dom, propName, isNativeTag, propVal, true);
} }
@ -55,9 +55,12 @@ export function updateDomProps(
changeList: Array<any>, changeList: Array<any>,
isNativeTag: boolean, isNativeTag: boolean,
): void { ): void {
for (let i = 0; i < changeList.length; i++) { const listLength = changeList.length;
const { propName, propVal } = changeList[i]; let propName;
let propVal;
for (let i = 0; i < listLength; i++) {
propName = changeList[i].propName;
propVal = changeList[i].propVal;
updateOneProp(dom, propName, isNativeTag, propVal); updateOneProp(dom, propName, isNativeTag, propVal);
} }
} }
@ -73,19 +76,24 @@ export function compareProps(
const keysOfOldProps = Object.keys(oldProps); const keysOfOldProps = Object.keys(oldProps);
const keysOfNewProps = Object.keys(newProps); 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++) { for (let i = 0; i < oldPropsLength; i++) {
const propName = keysOfOldProps[i]; propName = keysOfOldProps[i];
// 新属性中包含该属性或者该属性为空值的属性不需要处理 // 新属性中包含该属性或者该属性为空值的属性不需要处理
if (keysOfNewProps.includes(propName) || oldProps[propName] == null) { if (keysOfNewProps.includes(propName) || oldProps[propName] == null) {
continue; continue;
} }
if (propName === 'style') { if (propName === 'style') {
const oldStyle = oldProps[propName]; oldStyle = oldProps[propName];
const styleProps = Object.keys(oldStyle); styleProps = Object.keys(oldStyle);
for (let j = 0; j < styleProps.length; j++) { for (let j = 0; j < styleProps.length; j++) {
const styleProp = styleProps[j]; styleProp = styleProps[j];
updatesForStyle[styleProp] = ''; updatesForStyle[styleProp] = '';
} }
} else if ( } 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++) { for (let i = 0; i < keysOfNewProps.length; i++) {
const propName = keysOfNewProps[i]; propName = keysOfNewProps[i];
const newPropValue = newProps[propName]; newPropValue = newProps[propName];
const oldPropValue = oldProps != null ? oldProps[propName] : null; oldPropValue = oldProps != null ? oldProps[propName] : null;
if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) { if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) {
// 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理 // 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理
@ -124,18 +138,18 @@ export function compareProps(
if (propName === 'style') { if (propName === 'style') {
if (oldPropValue) { // 之前 style 属性有设置非空值 if (oldPropValue) { // 之前 style 属性有设置非空值
// 原来有这个 style但现在没这个 style 了 // 原来有这个 style但现在没这个 style 了
const oldStyleProps = Object.keys(oldPropValue); oldStyleProps = Object.keys(oldPropValue);
for (let j = 0; j < oldStyleProps.length; j++) { for (let j = 0; j < oldStyleProps.length; j++) {
const styleProp = oldStyleProps[j]; styleProp = oldStyleProps[j];
if (!newPropValue || !Object.prototype.hasOwnProperty.call(newPropValue, styleProp)) { if (!newPropValue || !Object.prototype.hasOwnProperty.call(newPropValue, styleProp)) {
updatesForStyle[styleProp] = ''; updatesForStyle[styleProp] = '';
} }
} }
// 现在有这个 style但是和原来不相等 // 现在有这个 style但是和原来不相等
const newStyleProps = newPropValue ? Object.keys(newPropValue) : []; newStyleProps = newPropValue ? Object.keys(newPropValue) : [];
for (let j = 0; j < newStyleProps.length; j++) { for (let j = 0; j < newStyleProps.length; j++) {
const styleProp = newStyleProps[j]; styleProp = newStyleProps[j];
if (oldPropValue[styleProp] !== newPropValue[styleProp]) { if (oldPropValue[styleProp] !== newPropValue[styleProp]) {
updatesForStyle[styleProp] = newPropValue[styleProp]; updatesForStyle[styleProp] = newPropValue[styleProp];
} }
@ -150,8 +164,8 @@ export function compareProps(
updatesForStyle = newPropValue; updatesForStyle = newPropValue;
} }
} else if (propName === 'dangerouslySetInnerHTML') { } else if (propName === 'dangerouslySetInnerHTML') {
const newHTML = newPropValue ? newPropValue.__html : undefined; newHTML = newPropValue ? newPropValue.__html : undefined;
const oldHTML = oldPropValue ? oldPropValue.__html : undefined; oldHTML = oldPropValue ? oldPropValue.__html : undefined;
if (newHTML != null) { if (newHTML != null) {
if (oldHTML !== newHTML) { if (oldHTML !== newHTML) {
toBeUpdatedProps.push({ toBeUpdatedProps.push({

View File

@ -29,7 +29,7 @@ function convertToLowerCase(str) {
* attrName class * attrName class
* attrName DOM 使 property * 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); const propDetails = getPropDetails(attrName);
if (isInvalidValue(attrName, value, propDetails, isNativeTag)) { if (isInvalidValue(attrName, value, propDetails, isNativeTag)) {

View File

@ -5,13 +5,6 @@ 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,
@ -23,18 +16,6 @@ export function createDom(
if (ns !== NSS.html) { if (ns !== NSS.html) {
dom = document.createElementNS(ns, tagName); dom = document.createElementNS(ns, tagName);
} else if (tagName === 'div') {
dom = div.cloneNode(false);
} else if (tagName === 'span') {
dom = span.cloneNode(false);
} else if (tagName === 'tr') {
dom = tr.cloneNode(false);
} else if (tagName === 'td') {
dom = td.cloneNode(false);
} else if (tagName === 'a') {
dom = a.cloneNode(false);
} else if (tagName === 'p') {
dom = p.cloneNode(false);
} else { } else {
dom = document.createElement(tagName); dom = document.createElement(tagName);
} }

View File

@ -37,7 +37,7 @@ export function updateInputValue(dom: HTMLInputElement, properties: IProperty) {
dom.value = String(value); dom.value = String(value);
} }
} else if (checked != null) { } else if (checked != null) {
updateCommonProp(dom, 'checked', checked); updateCommonProp(dom, 'checked', checked, true);
} }
} }

View File

@ -29,17 +29,32 @@ let ctxOldContext: Object = {};
let ctxOldChange: Boolean = false; let ctxOldChange: Boolean = false;
let ctxOldPreviousContext: Object = {}; 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阶段设置 // capture阶段设置
function setNamespaceCtx(vNode: VNode, dom?: Container) { function setNamespaceCtx(vNode: VNode, dom?: Container) {
const nextContext = getNSCtx(ctxNamespace, vNode.type, dom); const nextContext = getNSCtx(ctxNamespace, vNode.type, dom);
vNode.setContext(CTX_NAMESPACE, ctxNamespace); setContext(vNode, CTX_NAMESPACE, ctxNamespace);
ctxNamespace = nextContext; ctxNamespace = nextContext;
} }
// bubble阶段恢复 // bubble阶段恢复
function resetNamespaceCtx(vNode: VNode) { function resetNamespaceCtx(vNode: VNode) {
ctxNamespace = vNode.getContext(CTX_NAMESPACE); ctxNamespace = getContext(vNode, CTX_NAMESPACE);
} }
function getNamespaceCtx(): string { function getNamespaceCtx(): string {
@ -49,14 +64,14 @@ function getNamespaceCtx(): string {
function setContextCtx<T>(providerVNode: VNode, nextValue: T) { function setContextCtx<T>(providerVNode: VNode, nextValue: T) {
const context: ContextType<T> = providerVNode.type._context; const context: ContextType<T> = providerVNode.type._context;
providerVNode.setContext(CTX_CONTEXT, context.value); setContext(providerVNode, CTX_CONTEXT, context.value);
context.value = nextValue; context.value = nextValue;
} }
function resetContextCtx(providerVNode: VNode) { function resetContextCtx(providerVNode: VNode) {
const context: ContextType<any> = providerVNode.type._context; const context: ContextType<any> = providerVNode.type._context;
context.value = providerVNode.getContext(CTX_CONTEXT); context.value = getContext(providerVNode, CTX_CONTEXT);
} }
// 在局部更新时恢复父节点的context // 在局部更新时恢复父节点的context
@ -74,11 +89,11 @@ function recoverParentsContextCtx(vNode: VNode) {
// ctxOldContext是 旧context提供者的context // ctxOldContext是 旧context提供者的context
function setVNodeOldContext(providerVNode: VNode, context: Object) { function setVNodeOldContext(providerVNode: VNode, context: Object) {
providerVNode.setContext(CTX_OLD_CONTEXT, context); setContext(providerVNode, CTX_OLD_CONTEXT, context);
} }
function getVNodeOldContext(vNode: VNode) { function getVNodeOldContext(vNode: VNode) {
return vNode.getContext(CTX_OLD_CONTEXT); return getContext(vNode, CTX_OLD_CONTEXT);
} }
function setOldContextCtx(providerVNode: VNode, context: Object) { function setOldContextCtx(providerVNode: VNode, context: Object) {
@ -95,11 +110,11 @@ function resetOldContextCtx(vNode: VNode) {
} }
function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) { function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) {
providerVNode.setContext(CTX_OLD_PREVIOUS_CONTEXT, context); setContext(providerVNode, CTX_OLD_PREVIOUS_CONTEXT, context);
} }
function getVNodeOldPreviousContext(vNode: VNode) { function getVNodeOldPreviousContext(vNode: VNode) {
return vNode.getContext(CTX_OLD_PREVIOUS_CONTEXT); return getContext(vNode, CTX_OLD_PREVIOUS_CONTEXT);
} }
function setOldPreviousContextCtx(context: Object) { function setOldPreviousContextCtx(context: Object) {
@ -111,7 +126,7 @@ function getOldPreviousContextCtx() {
} }
function setContextChangeCtx(providerVNode: VNode, didChange: boolean) { function setContextChangeCtx(providerVNode: VNode, didChange: boolean) {
providerVNode.setContext(CTX_OLD_CHANGE, didChange); setContext(providerVNode, CTX_OLD_CHANGE, didChange);
ctxOldChange = didChange; ctxOldChange = didChange;
} }
@ -120,7 +135,7 @@ function getContextChangeCtx() {
} }
function resetContextChangeCtx(vNode: VNode) { function resetContextChangeCtx(vNode: VNode) {
ctxOldChange = vNode.getContext(CTX_OLD_CHANGE); ctxOldChange = getContext(vNode, CTX_OLD_CHANGE);
} }
export { export {

View File

@ -6,7 +6,7 @@ import type {VNode} from './Types';
import type {Update} from './UpdateHandler'; 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, DidCapture, InitFlag} from './vnode/VNodeFlags';
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler'; import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder'; import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder';
import {setRootThrowError} from './submit/Submit'; import {setRootThrowError} from './submit/Submit';
@ -99,7 +99,7 @@ export function handleRenderThrowError(
const ctor = vNode.type; const ctor = vNode.type;
const instance = vNode.realNode; const instance = vNode.realNode;
if ( if (
!vNode.flags.DidCapture && (vNode.flags & DidCapture) === InitFlag &&
( (
typeof ctor.getDerivedStateFromError === 'function' || typeof ctor.getDerivedStateFromError === 'function' ||
(instance !== null && typeof instance.componentDidCatch === 'function') (instance !== null && typeof instance.componentDidCatch === 'function')

View File

@ -3,7 +3,7 @@ import type { VNode } from './Types';
import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue'; import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue';
import { updateVNode } from './vnode/VNodeCreator'; import { updateVNode } from './vnode/VNodeCreator';
import { TreeRoot } from './vnode/VNodeTags'; import { TreeRoot } from './vnode/VNodeTags';
import { FlagUtils } from './vnode/VNodeFlags'; import { FlagUtils, InitFlag, Interrupted } from './vnode/VNodeFlags';
import { captureVNode } from './render/BaseComponent'; import { captureVNode } from './render/BaseComponent';
import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit'; import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit';
import { runAsyncEffects } from './submit/HookEffectHandler'; import { runAsyncEffects } from './submit/HookEffectHandler';
@ -18,7 +18,7 @@ import {
setProcessingClassVNode, setProcessingClassVNode,
setStartVNode setStartVNode
} from './GlobalVar'; } from './GlobalVar';
import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils'; import { findDomParent } from './vnode/VNodeUtils';
import { import {
ByAsync, ByAsync,
BySync, BySync,
@ -86,13 +86,13 @@ function bubbleVNode(vNode: VNode): void {
do { do {
const parent = node.parent; const parent = node.parent;
if (!node.flags.Interrupted) { // vNode没有抛出异常 if ((node.flags & Interrupted) === InitFlag) { // vNode没有抛出异常
componentRenders[node.tag].bubbleRender(node); componentRenders[node.tag].bubbleRender(node);
// 设置node的childShouldUpdate属性 // 设置node的childShouldUpdate属性
updateChildShouldUpdate(node); updateChildShouldUpdate(node);
if (parent !== null && node !== getStartVNode() && !parent.flags.Interrupted) { if (parent !== null && node !== getStartVNode() && (parent.flags & Interrupted) === InitFlag) {
collectDirtyNodes(node, parent); collectDirtyNodes(node, parent);
} }
} }
@ -108,7 +108,7 @@ function bubbleVNode(vNode: VNode): void {
break; break;
} }
const siblingVNode = getSiblingVNode(node); const siblingVNode = node.next;
if (siblingVNode !== null) { // 有兄弟vNode if (siblingVNode !== null) { // 有兄弟vNode
processing = siblingVNode; processing = siblingVNode;
return; return;

View File

@ -9,10 +9,6 @@ export function isTextType(newChild: any) {
return typeof newChild === 'string' || typeof newChild === 'number'; return typeof newChild === 'string' || typeof newChild === 'number';
} }
export function isArrayType(newChild: any) {
return Array.isArray(newChild);
}
export function isIteratorType(newChild: any) { export function isIteratorType(newChild: any) {
return (typeof Symbol === 'function' && newChild[Symbol.iterator]) || newChild['@@iterator']; return (typeof Symbol === 'function' && newChild[Symbol.iterator]) || newChild['@@iterator'];
} }

View File

@ -7,11 +7,10 @@ import {
isSameType, isSameType,
getIteratorFn, getIteratorFn,
isTextType, isTextType,
isArrayType,
isIteratorType, isIteratorType,
isObjectType, isObjectType,
} from './DiffTools'; } from './DiffTools';
import {getSiblingVNode, travelChildren} from '../vnode/VNodeUtils'; import { travelChildren } from '../vnode/VNodeUtils';
enum DiffCategory { enum DiffCategory {
TEXT_NODE = 'TEXT_NODE', TEXT_NODE = 'TEXT_NODE',
@ -60,7 +59,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean {
} }
if (isObjectType(newChild)) { if (isObjectType(newChild)) {
if (isArrayType(newChild) || isIteratorType(newChild)) { if (Array.isArray(newChild) || isIteratorType(newChild)) {
return oldKey === null; return oldKey === null;
} }
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
@ -79,7 +78,7 @@ function getNodeType(newChild: any): string | null {
return DiffCategory.TEXT_NODE; return DiffCategory.TEXT_NODE;
} }
if (isObjectType(newChild)) { if (isObjectType(newChild)) {
if (isArrayType(newChild) || isIteratorType(newChild)) { if (Array.isArray(newChild) || isIteratorType(newChild)) {
return DiffCategory.ARR_NODE; return DiffCategory.ARR_NODE;
} }
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
@ -200,7 +199,7 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
return nodeMap.get(newIdx) || null; return nodeMap.get(newIdx) || null;
} }
if (isObjectType(newChild)) { if (isObjectType(newChild)) {
if (isArrayType(newChild) || isIteratorType(newChild)) { if (Array.isArray(newChild) || isIteratorType(newChild)) {
return nodeMap.get(newIdx) || null; return nodeMap.get(newIdx) || null;
} }
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
@ -216,11 +215,11 @@ function setCIndex(vNode: VNode, idx: number) {
} }
// diff数组类型的节点核心算法 // diff数组类型的节点核心算法
function diffArrayNodes( function diffArrayNodesHandler(
parentNode: VNode, parentNode: VNode,
firstChild: VNode | null, firstChild: VNode | null,
newChildren: Array<any>, newChildren: Array<any>,
isComparing: boolean = true isComparing: boolean
): VNode | null { ): VNode | null {
let resultingFirstChild: VNode | null = null; let resultingFirstChild: VNode | null = null;
@ -242,6 +241,8 @@ function diffArrayNodes(
prevNewNode = newNode; prevNewNode = newNode;
} }
let canBeReuse;
let newNode;
// 1. 从左侧开始比对currentVNode和newChildren若不能复用则跳出循环 // 1. 从左侧开始比对currentVNode和newChildren若不能复用则跳出循环
for (; oldNode !== null && leftIdx < newChildren.length; leftIdx++) { for (; oldNode !== null && leftIdx < newChildren.length; leftIdx++) {
if (oldNode.eIndex > leftIdx) { if (oldNode.eIndex > leftIdx) {
@ -249,17 +250,17 @@ function diffArrayNodes(
nextOldNode = oldNode; nextOldNode = oldNode;
oldNode = null; oldNode = null;
} else { } else {
nextOldNode = getSiblingVNode(oldNode); nextOldNode = oldNode.next;
} }
const canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]); canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]);
// 不能复用break // 不能复用break
if (!canBeReuse) { if (!canBeReuse) {
oldNode = oldNode ?? nextOldNode; oldNode = oldNode ?? nextOldNode;
break; break;
} }
const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode); newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode);
// 没有生成新节点break // 没有生成新节点break
if (!newNode) { if (!newNode) {
oldNode = oldNode ?? nextOldNode; oldNode = oldNode ?? nextOldNode;
@ -287,19 +288,20 @@ function diffArrayNodes(
let rightOldIndex: number | null = rightRemainingOldChildren.length - 1; let rightOldIndex: number | null = rightRemainingOldChildren.length - 1;
// 2. 从右侧开始比对currentVNode和newChildren若不能复用则跳出循环 // 2. 从右侧开始比对currentVNode和newChildren若不能复用则跳出循环
let rightOldNode;
for (; rightIdx > leftIdx; rightIdx--) { for (; rightIdx > leftIdx; rightIdx--) {
const rightOldNode = rightRemainingOldChildren[rightOldIndex]; rightOldNode = rightRemainingOldChildren[rightOldIndex];
if (rightOldIndex < 0 || rightOldNode === null) { if (rightOldIndex < 0 || rightOldNode === null) {
break; break;
} }
const canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]); canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]);
// 不能复用break // 不能复用break
if (!canBeReuse) { if (!canBeReuse) {
break; break;
} }
const newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode); newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode);
// 没有生成新节点break // 没有生成新节点break
if (newNode === null) { if (newNode === null) {
break; break;
@ -346,7 +348,7 @@ function diffArrayNodes(
// 4. 新节点还有一部分,但是老节点已经没有了 // 4. 新节点还有一部分,但是老节点已经没有了
if (oldNode === null) { if (oldNode === null) {
for (; leftIdx < rightIdx; leftIdx++) { for (; leftIdx < rightIdx; leftIdx++) {
const newNode = getNewNode(parentNode, newChildren[leftIdx], null); newNode = getNewNode(parentNode, newChildren[leftIdx], null);
if (newNode !== null) { if (newNode !== null) {
theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing); theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing);
@ -372,9 +374,11 @@ function diffArrayNodes(
const preIndex: Array<number> = []; // 贪心算法在替换的过程中会使得数组不正确通过记录preIndex找到正确值 const preIndex: Array<number> = []; // 贪心算法在替换的过程中会使得数组不正确通过记录preIndex找到正确值
const reuseNodes = []; // 记录复用的 VNode const reuseNodes = []; // 记录复用的 VNode
let i = 0; let i = 0;
let oldNodeFromMap;
let last;
for (; leftIdx < rightIdx; leftIdx++) { for (; leftIdx < rightIdx; leftIdx++) {
const oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]); oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]);
const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap); newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap);
if (newNode !== null) { if (newNode !== null) {
if (isComparing && !newNode.isCreated) { if (isComparing && !newNode.isCreated) {
// 从Map删除后面不会deleteVNode // 从Map删除后面不会deleteVNode
@ -384,7 +388,7 @@ function diffArrayNodes(
if (oldNodeFromMap !== null) { if (oldNodeFromMap !== null) {
let eIndex = newNode.eIndex; let eIndex = newNode.eIndex;
eIndexes.push(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直接放在最后 if (eIndex > last || last === undefined) { // 大的 eIndex直接放在最后
preIndex[i] = result[result.length - 1]; preIndex[i] = result[result.length - 1];
result.push(i); result.push(i);
@ -462,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( function diffIteratorNodesHandler(
parentNode: VNode, parentNode: VNode,
@ -490,7 +484,7 @@ function diffIteratorNodesHandler(
result = iteratorObj.next(); result = iteratorObj.next();
} }
return diffArrayNodes(parentNode, firstChild, childrenArray, isComparing); return diffArrayNodesHandler(parentNode, firstChild, childrenArray, isComparing);
} }
// 新节点是字符串类型 // 新节点是字符串类型
@ -552,13 +546,13 @@ function diffObjectNodeHandler(
// 可以复用 // 可以复用
if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) { if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) {
resultNode = updateVNode(canReuseNode, newChild.props.children); resultNode = updateVNode(canReuseNode, newChild.props.children);
startDelVNode = getSiblingVNode(canReuseNode); startDelVNode = canReuseNode.next;
resultNode.next = null; resultNode.next = null;
} else if (isSameType(canReuseNode, newChild)) { } else if (isSameType(canReuseNode, newChild)) {
resultNode = updateVNode(canReuseNode, newChild.props); resultNode = updateVNode(canReuseNode, newChild.props);
resultNode.ref = newChild.ref; resultNode.ref = newChild.ref;
resultNode.belongClassVNode = newChild.belongClassVNode; resultNode.belongClassVNode = newChild.belongClassVNode;
startDelVNode = getSiblingVNode(resultNode); startDelVNode = resultNode.next;
resultNode.next = null; resultNode.next = null;
} }
} }
@ -578,7 +572,7 @@ function diffObjectNodeHandler(
// 可以复用 // 可以复用
if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) { if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) {
resultNode = updateVNode(canReuseNode, newChild.children || []); resultNode = updateVNode(canReuseNode, newChild.children || []);
startDelVNode = getSiblingVNode(canReuseNode); startDelVNode = canReuseNode.next;
resultNode.next = null; resultNode.next = null;
} }
} }
@ -629,7 +623,7 @@ export function createChildrenByDiff(
} }
// 3. newChild是数组类型 // 3. newChild是数组类型
if (isArrayType(newChild)) { if (Array.isArray(newChild)) {
return diffArrayNodesHandler(parentNode, firstChild, newChild, isComparing); return diffArrayNodesHandler(parentNode, firstChild, newChild, isComparing);
} }

View File

@ -66,9 +66,7 @@ export function captureVNode(processing: VNode): VNode | null {
// 创建孩子节点 // 创建孩子节点
export function createVNodeChildren(processing: VNode, nextChildren: any) { export function createVNodeChildren(processing: VNode, nextChildren: any) {
const isComparing = !processing.isCreated; return createChildrenByDiff(processing, processing.child, nextChildren, !processing.isCreated);
return createChildrenByDiff(processing, processing.child, nextChildren, isComparing);
} }
export function markRef(processing: VNode) { export function markRef(processing: VNode) {

View File

@ -20,7 +20,7 @@ import {
markComponentDidUpdate, markComponentDidUpdate,
markGetSnapshotBeforeUpdate, markGetSnapshotBeforeUpdate,
} from './class/ClassLifeCycleProcessor'; } from './class/ClassLifeCycleProcessor';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils, DidCapture } from '../vnode/VNodeFlags';
import { createVNodeChildren, markRef } from './BaseComponent'; import { createVNodeChildren, markRef } from './BaseComponent';
import { import {
createUpdateArray, createUpdateArray,
@ -73,7 +73,7 @@ function createChildren(clazz: any, processing: VNode) {
processing.state = processing.realNode.state; processing.state = processing.realNode.state;
const inst = processing.realNode; const inst = processing.realNode;
const isCatchError = processing.flags.DidCapture; const isCatchError = (processing.flags & DidCapture) === DidCapture;
// 按照已有规格如果捕获了错误却没有定义getDerivedStateFromError函数返回的child为null // 按照已有规格如果捕获了错误却没有定义getDerivedStateFromError函数返回的child为null
const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function' const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function'
@ -122,7 +122,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
const newContext = getCurrentContext(clazz, processing); const newContext = getCurrentContext(clazz, processing);
// 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props // 子节点抛出异常时如果本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) { if (processing.isLazyComponent) {
oldProps = mergeDefaultProps(processing.type, oldProps); oldProps = mergeDefaultProps(processing.type, oldProps);
} }
@ -161,7 +161,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
inst.props = nextProps; inst.props = nextProps;
} }
// 如果捕获了 error必须更新 // 如果捕获了 error必须更新
const isCatchError = processing.flags.DidCapture; const isCatchError = (processing.flags & DidCapture) === DidCapture;
shouldUpdate = isCatchError || shouldUpdate; shouldUpdate = isCatchError || shouldUpdate;
// 更新ref // 更新ref

View File

@ -1,5 +1,5 @@
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import type {Props} from '../../dom/DOMOperator'; import type { Props } from '../../dom/DOMOperator';
import { import {
getNamespaceCtx, getNamespaceCtx,
@ -12,10 +12,10 @@ import {
initDomProps, getPropChangeList, initDomProps, getPropChangeList,
isTextChild, isTextChild,
} from '../../dom/DOMOperator'; } from '../../dom/DOMOperator';
import {FlagUtils} from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import {createVNodeChildren, markRef} from './BaseComponent'; import { createVNodeChildren, markRef } from './BaseComponent';
import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags';
import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils'; import { travelVNodeTree } from '../vnode/VNodeUtils';
function updateDom( function updateDom(
processing: VNode, 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) { export function bubbleRender(processing: VNode) {
resetNamespaceCtx(processing); resetNamespaceCtx(processing);
@ -95,7 +77,19 @@ export function bubbleRender(processing: VNode) {
processing, 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; processing.realNode = dom;

View File

@ -17,7 +17,7 @@ import {
SuspenseComponent, SuspenseComponent,
MemoComponent, MemoComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags'; import { FlagUtils, ResetText, Clear, Update } from '../vnode/VNodeFlags';
import { mergeDefaultProps } from '../render/LazyComponent'; import { mergeDefaultProps } from '../render/LazyComponent';
import { import {
submitDomUpdate, submitDomUpdate,
@ -97,7 +97,7 @@ function callAfterSubmitLifeCycles(
} }
case ClassComponent: { case ClassComponent: {
const instance = vNode.realNode; const instance = vNode.realNode;
if (vNode.flags.Update) { if ((vNode.flags & Update) === Update) {
if (vNode.isCreated) { if (vNode.isCreated) {
instance.componentDidMount(); instance.componentDidMount();
} else { } else {
@ -124,7 +124,7 @@ function callAfterSubmitLifeCycles(
return; return;
} }
case DomComponent: { case DomComponent: {
if (vNode.isCreated && vNode.flags.Update) { if (vNode.isCreated && (vNode.flags & Update) === 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)) {
vNode.realNode.focus(); vNode.realNode.focus();
@ -222,7 +222,7 @@ function unmountNestedVNodes(vNode: VNode): void {
function submitAddition(vNode: VNode): void { function submitAddition(vNode: VNode): void {
const { parent, parentDom } = findDomParent(vNode); const { parent, parentDom } = findDomParent(vNode);
if (parent.flags.ResetText) { if ((vNode.flags & ResetText) === ResetText) {
// 在insert之前先reset // 在insert之前先reset
clearText(parentDom); clearText(parentDom);
FlagUtils.removeFlag(parent, ResetText); FlagUtils.removeFlag(parent, ResetText);

View File

@ -1,8 +1,6 @@
import type {VNode} from '../Types'; import type {VNode} from '../Types';
import {callRenderQueueImmediate} from '../taskExecutor/RenderQueue'; import {FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback} from '../vnode/VNodeFlags';
import {throwIfTrue} from '../utils/throwIfTrue';
import {FlagUtils, Addition as AdditionFlag} from '../vnode/VNodeFlags';
import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator'; import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator';
import {handleSubmitError} from '../ErrorHandler'; import {handleSubmitError} from '../ErrorHandler';
import { import {
@ -91,7 +89,7 @@ export function submitToRender(treeRoot) {
function beforeSubmit(dirtyNodes: Array<VNode>) { function beforeSubmit(dirtyNodes: Array<VNode>) {
dirtyNodes.forEach(node => { dirtyNodes.forEach(node => {
try { try {
if (node.flags.Snapshot) { if ((node.flags & Snapshot) === Snapshot) {
callBeforeSubmitLifeCycles(node); callBeforeSubmitLifeCycles(node);
} }
} catch (error) { } catch (error) {
@ -103,35 +101,38 @@ function beforeSubmit(dirtyNodes: Array<VNode>) {
function submit(dirtyNodes: Array<VNode>) { function submit(dirtyNodes: Array<VNode>) {
dirtyNodes.forEach(node => { dirtyNodes.forEach(node => {
try { try {
if (node.flags.ResetText) { if ((node.flags & ResetText) === ResetText) {
submitResetTextContent(node); submitResetTextContent(node);
} }
if (node.flags.Ref) { if ((node.flags & Ref) === Ref) {
if (!node.isCreated) { if (!node.isCreated) {
// 需要执行 // 需要执行
detachRef(node, true); detachRef(node, true);
} }
} }
const {Addition, Update, Deletion, Clear} = node.flags; const isAdd = (node.flags & Addition) === Addition;
if (Addition && Update) { const isUpdate = (node.flags & Update) === Update;
if (isAdd && isUpdate) {
// Addition // Addition
submitAddition(node); submitAddition(node);
FlagUtils.removeFlag(node, AdditionFlag); FlagUtils.removeFlag(node, Addition);
// Update // Update
submitUpdate(node); submitUpdate(node);
} else { } else {
if (Addition) { const isDeletion = (node.flags & Deletion) === Deletion;
const isClear = (node.flags & Clear) === Clear;
if (isAdd) {
submitAddition(node); submitAddition(node);
FlagUtils.removeFlag(node, AdditionFlag); FlagUtils.removeFlag(node, Addition);
} else if (Update) { } else if (isUpdate) {
submitUpdate(node); submitUpdate(node);
} else if (Deletion) { } else if (isDeletion) {
submitDeletion(node); submitDeletion(node);
} }
if (Clear) { if (isClear) {
submitClear(node); submitClear(node);
} }
} }
@ -144,11 +145,11 @@ function submit(dirtyNodes: Array<VNode>) {
function afterSubmit(dirtyNodes: Array<VNode>) { function afterSubmit(dirtyNodes: Array<VNode>) {
dirtyNodes.forEach(node => { dirtyNodes.forEach(node => {
try { try {
if (node.flags.Update || node.flags.Callback) { if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) {
callAfterSubmitLifeCycles(node); callAfterSubmitLifeCycles(node);
} }
if (node.flags.Ref) { if ((node.flags & Ref) === Ref) {
attachRef(node); attachRef(node);
} }
} catch (error) { } catch (error) {

View File

@ -1,16 +1,18 @@
/** /**
* DOM结构体 * DOM结构体
*/ */
import {TreeRoot} from './VNodeTags'; 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 { VNodeTag } from './VNodeTags';
import type {RefType, ContextType} from '../Types'; import type { RefType, ContextType } from '../Types';
import type {Hook} from '../hooks/HookType'; import type { Hook } from '../hooks/HookType';
import { InitFlag } from './VNodeFlags';
export class VNode { export class VNode {
tag: VNodeTag; tag: VNodeTag;
key: string | null; // 唯一标识符 key: string | null; // 唯一标识符
props: any; // 传给组件的props的值类组件包含defaultPropsLazy组件不包含
type: any = null; type: any = null;
realNode: any = null; // 如果是类则存放实例如果是div这种则存放真实DOM realNode: any; // 如果是类则存放实例如果是div这种则存放真实DOM
// 关系结构 // 关系结构
parent: VNode | null = null; // 父节点 parent: VNode | null = null; // 父节点
@ -20,21 +22,20 @@ export class VNode {
eIndex: number = 0; // HorizonElement在jsx中的位置例如jsx中的null不会生成vNode所以eIndex和cIndex不一致 eIndex: number = 0; // HorizonElement在jsx中的位置例如jsx中的null不会生成vNode所以eIndex和cIndex不一致
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数submit阶段使用比如将外部useRef生成的对象赋值到ref上 ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数submit阶段使用比如将外部useRef生成的对象赋值到ref上
props: any; // 传给组件的props的值类组件包含defaultPropsLazy组件不包含
oldProps: any = null; oldProps: any = null;
suspensePromises: any = null; // suspense组件的promise列表 suspensePromises: any; // suspense组件的promise列表
changeList: any = null; // DOM的变更列表 changeList: any; // DOM的变更列表
effectList: any[] | null = null; // useEffect 的更新数组 effectList: any[] | null; // useEffect 的更新数组
updates: any[] | null = null; // TreeRoot和ClassComponent使用的更新数组 updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组
stateCallbacks: any[] | null = null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组 stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
isForceUpdate: boolean = false; // 是否使用强制更新 isForceUpdate: boolean; // 是否使用强制更新
state: any = null; // ClassComponent和TreeRoot的状态 state: any; // ClassComponent和TreeRoot的状态
hooks: Array<Hook<any, any>> | null = null; // 保存hook hooks: Array<Hook<any, any>> | null; // 保存hook
suspenseChildStatus: string = ''; // Suspense的Children是否显示 suspenseChildStatus: string = ''; // Suspense的Children是否显示
depContexts: Array<ContextType<any>> | null = null; // FunctionComponent和ClassComponent对context的依赖列表 depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表
isDepContextChange: boolean = false; // context是否变更 isDepContextChange: boolean; // context是否变更
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组 dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
shouldUpdate: boolean = false; shouldUpdate: boolean = false;
childShouldUpdate: boolean = false; childShouldUpdate: boolean = false;
@ -42,40 +43,27 @@ export class VNode {
task: any; task: any;
// 使用这个变量来记录修改前的值,用于恢复。 // 使用这个变量来记录修改前的值,用于恢复。
contexts = {}; contexts: any;
// 因为LazyComponent会修改tag和type属性为了能识别增加一个属性 // 因为LazyComponent会修改tag和type属性为了能识别增加一个属性
isLazyComponent: boolean = false; isLazyComponent: boolean;
// 因为LazyComponent会修改type属性为了在diff中判断是否可以复用需要增加一个lazyType // 因为LazyComponent会修改type属性为了在diff中判断是否可以复用需要增加一个lazyType
lazyType: any = null; lazyType: any;
flags: { flags = InitFlag;
Addition?: boolean; clearChild: VNode | null;
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;
// one tree相关属性 // one tree相关属性
isCreated: boolean = true; isCreated: boolean = true;
oldHooks: Array<Hook<any, any>> | null = []; // 保存上一次执行的hook oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook
oldState: any = null; oldState: any;
oldRef: RefType | ((handle: any) => void) | null = null; oldRef: RefType | ((handle: any) => void) | null = null;
suspenseChildThrow = false; suspenseChildThrow: boolean;
oldSuspenseChildStatus: string = ''; // 上一次Suspense的Children是否显示 oldSuspenseChildStatus: string; // 上一次Suspense的Children是否显示
oldChild: VNode | null = null; oldChild: VNode | null = null;
suspenseDidCapture: boolean = false; // suspense是否捕获了异常 suspenseDidCapture: boolean; // suspense是否捕获了异常
promiseResolve: boolean = false; // suspense的promise是否resolve promiseResolve: boolean; // suspense的promise是否resolve
path: string = ''; // 保存从根到本节点的路径 path: string = ''; // 保存从根到本节点的路径
toUpdateNodes: Set<VNode> | null = null; // 保存要更新的节点 toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode处理ref的时候使用 belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode处理ref的时候使用
@ -85,18 +73,80 @@ export class VNode {
this.props = props; this.props = props;
// 根节点 switch (tag) {
if (tag === TreeRoot) { case TreeRoot:
this.outerDom = outerDom; this.outerDom = outerDom;
this.task = null; this.task = null;
this.toUpdateNodes = new Set<VNode>(); 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;
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];
}
} }

View File

@ -133,9 +133,8 @@ export function createUndeterminedVNode(type, key, props) {
vNode.type = type; vNode.type = type;
vNode.shouldUpdate = true; vNode.shouldUpdate = true;
// lazy类型的特殊处理
vNode.isLazyComponent = isLazy;
if (isLazy) { if (isLazy) {
vNode.isLazyComponent = isLazy;
vNode.lazyType = type; vNode.lazyType = type;
} }
return vNode; return vNode;

View File

@ -4,97 +4,79 @@
import type { VNode } from '../Types'; import type { VNode } from '../Types';
export const InitFlag = /** */ 0b000000000000;
// vNode节点的flags // vNode节点的flags
export const Addition = 'Addition'; export const Addition = /** */ 0b100000000000;
export const Update = 'Update'; export const Update = /** */ 0b010000000000;
export const Deletion = 'Deletion'; export const Deletion = /** */ 0b001000000000;
export const ResetText = 'ResetText'; export const ResetText =/** */ 0b000100000000;
export const Callback = 'Callback'; export const Callback = /** */ 0b000010000000;
export const DidCapture = 'DidCapture'; export const DidCapture =/** */ 0b000001000000;
export const Ref = 'Ref'; export const Ref = /** */ 0b000000100000;
export const Snapshot = 'Snapshot'; export const Snapshot = /** */ 0b000000010000;
// 被中断了抛出错误的vNode以及它的父vNode export const Interrupted = /** */ 0b000000001000; // 被中断了抛出错误的vNode以及它的父vNode
export const Interrupted = 'Interrupted'; export const ShouldCapture =/** */ 0b000000000100;
export const ShouldCapture = 'ShouldCapture'; export const ForceUpdate = /** */ 0b000000000010; // For suspense
// For suspense export const Clear = /** */ 0b000000000001;
export const ForceUpdate = 'ForceUpdate'; const LifecycleEffectArr = Update | Callback | Ref | Snapshot;
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 class FlagUtils { export class FlagUtils {
static removeFlag(node: VNode, flag: string) { static removeFlag(node: VNode, flag: number) {
node.flags[flag] = false; const flags = node.flags;
node.flags = flags & (~flag);
} }
static removeLifecycleEffectFlags(node) { static removeLifecycleEffectFlags(node) {
LifecycleEffectArr.forEach(key => { const flags = node.flags;
node.flags[key] = false; node.flags = flags & (~LifecycleEffectArr);
});
} }
static hasAnyFlag(node: VNode) { // 有标志位 static hasAnyFlag(node: VNode) { // 有标志位
const flags = node.flags; return node.flags !== InitFlag;
const arrLength = FlagArr.length;
let key;
for (let i = 0; i < arrLength; i++) {
key = FlagArr[i];
if (flags[key]) {
return true;
}
}
return false;
} }
static setNoFlags(node: VNode) { static setNoFlags(node: VNode) {
resetFlag(node); node.flags = InitFlag;
} }
static markAddition(node: VNode) { static markAddition(node: VNode) {
node.flags.Addition = true; node.flags |= Addition;
} }
static setAddition(node: VNode) { static setAddition(node: VNode) {
resetFlag(node); node.flags = Addition;
node.flags.Addition = true;
} }
static markUpdate(node: VNode) { static markUpdate(node: VNode) {
node.flags.Update = true; node.flags |= Update;
} }
static setDeletion(node: VNode) { static setDeletion(node: VNode) {
resetFlag(node); node.flags = Deletion;
node.flags.Deletion = true;
} }
static markContentReset(node: VNode) { static markContentReset(node: VNode) {
node.flags.ResetText = true; node.flags |= ResetText;
} }
static markCallback(node: VNode) { static markCallback(node: VNode) {
node.flags.Callback = true; node.flags |= Callback;
} }
static markDidCapture(node: VNode) { static markDidCapture(node: VNode) {
node.flags.DidCapture = true; node.flags |= DidCapture;
} }
static markShouldCapture(node: VNode) { static markShouldCapture(node: VNode) {
node.flags.ShouldCapture = true; node.flags |= ShouldCapture;
} }
static markRef(node: VNode) { static markRef(node: VNode) {
node.flags.Ref = true; node.flags |= Ref;
} }
static markSnapshot(node: VNode) { static markSnapshot(node: VNode) {
node.flags.Snapshot = true; node.flags |= Snapshot;
} }
static markInterrupted(node: VNode) { static markInterrupted(node: VNode) {
node.flags.Interrupted = true; node.flags |= Interrupted;
} }
static markForceUpdate(node: VNode) { static markForceUpdate(node: VNode) {
node.flags.ForceUpdate = true; node.flags |= ForceUpdate;
} }
static markClear(node: VNode) { static markClear(node: VNode) {
node.flags.Clear = true; node.flags |= Clear;
} }
} }

View File

@ -7,10 +7,7 @@ import type {VNode} from '../Types';
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags'; import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
import {isComment} from '../../dom/utils/Common'; import {isComment} from '../../dom/utils/Common';
import {getNearestVNode} from '../../dom/DOMInternalKeys'; import {getNearestVNode} from '../../dom/DOMInternalKeys';
import { Addition, InitFlag } from './VNodeFlags';
export function getSiblingVNode(node) {
return node.next;
}
export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) { export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) {
let node: VNode | null = beginVNode; 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) { if (node.parent === null || node.parent === overVNode) {
return null; return null;
} }
@ -69,7 +66,7 @@ export function travelVNodeTree(
} }
} }
// 找到兄弟 // 找到兄弟
const siblingVNode = getSiblingVNode(node); const siblingVNode = node.next;
siblingVNode.parent = node.parent; siblingVNode.parent = node.parent;
node = siblingVNode; node = siblingVNode;
} }
@ -96,6 +93,7 @@ export function clearVNode(vNode: VNode) {
vNode.oldState = null; vNode.oldState = null;
vNode.oldRef = null; vNode.oldRef = null;
vNode.oldChild = null; vNode.oldChild = null;
vNode.flags = InitFlag;
vNode.toUpdateNodes = null; vNode.toUpdateNodes = null;
@ -175,7 +173,7 @@ export function getSiblingDom(vNode: VNode): Element | null {
findSibling: while (true) { findSibling: while (true) {
// 没有兄弟节点,找父节点 // 没有兄弟节点,找父节点
while (getSiblingVNode(node) === null) { while (node.next === null) {
// 没父节点,或父节点已经是根节点,则返回 // 没父节点,或父节点已经是根节点,则返回
if (node.parent === null || isDomContainer(node.parent)) { if (node.parent === null || isDomContainer(node.parent)) {
return null; return null;
@ -183,14 +181,14 @@ export function getSiblingDom(vNode: VNode): Element | null {
node = node.parent; node = node.parent;
} }
const siblingVNode = getSiblingVNode(node); const siblingVNode = node.next;
siblingVNode.parent = node.parent; siblingVNode.parent = node.parent;
node = siblingVNode; node = siblingVNode;
// 如果不是dom节点往下找 // 如果不是dom节点往下找
while (!isDomVNode(node)) { while (!isDomVNode(node)) {
// 如果节点也是Addition // 如果节点也是Addition
if (node.flags.Addition) { if ((node.flags & Addition) ===Addition) {
continue findSibling; continue findSibling;
} }
@ -204,7 +202,7 @@ export function getSiblingDom(vNode: VNode): Element | null {
} }
} }
if (!node.flags.Addition) { if ((node.flags & Addition) ===InitFlag) {
// 找到 // 找到
return node.realNode; return node.realNode;
} }