Match-id-ea5015573e0ed720bfa40f7a8c1e1c44978bfe09

This commit is contained in:
* 2022-01-27 16:38:36 +08:00 committed by *
commit 56d98e4dce
47 changed files with 769 additions and 752 deletions

View File

@ -10,30 +10,6 @@ import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
import {TreeRoot} from '../renderer/vnode/VNodeTags';
import {Callback} from '../renderer/UpdateHandler';
function executeRender(
children: any,
container: Container,
callback?: Callback,
) {
let treeRoot = container._treeRoot;
if (!treeRoot) {
treeRoot = createRoot(children, container, callback);
} else { // container被render过
if (typeof callback === 'function') {
const cb = callback;
callback = function () {
const instance = getFirstCustomDom(treeRoot);
cb.call(instance);
};
}
// 执行更新操作
startUpdate(children, treeRoot, callback);
}
return getFirstCustomDom(treeRoot);
}
function createRoot(children: any, container: Container, callback?: Callback) {
// 清空容器
let child = container.lastChild;
@ -66,7 +42,31 @@ function createRoot(children: any, container: Container, callback?: Callback) {
return treeRoot;
}
function findDOMNode(domOrEle: Element): null | Element | Text {
function executeRender(
children: any,
container: Container,
callback?: Callback,
) {
let treeRoot = container._treeRoot;
if (!treeRoot) {
treeRoot = createRoot(children, container, callback);
} else { // container被render过
if (typeof callback === 'function') {
const cb = callback;
callback = function () {
const instance = getFirstCustomDom(treeRoot);
cb.call(instance);
};
}
// 执行更新操作
startUpdate(children, treeRoot, callback);
}
return getFirstCustomDom(treeRoot);
}
function findDOMNode(domOrEle?: Element): null | Element | Text {
if (domOrEle == null) {
return null;
}

View File

@ -24,11 +24,12 @@ const internalKeys = {
};
// 通过 VNode 实例获取 DOM 节点
export function getDom(vNode: VNode): Element | Text | void {
export function getDom(vNode: VNode): Element | Text | null {
const {tag} = vNode;
if (tag === DomComponent || tag === DomText) {
return vNode.realNode;
}
return null;
}
// 将 VNode 属性相关信息挂到 DOM 对象的特定属性上
@ -72,7 +73,7 @@ export function getNearestVNode(dom: Node): null | VNode {
}
// 获取 vNode 上的属性相关信息
export function getVNodeProps(dom: Element | Text): Props {
export function getVNodeProps(dom: Element | Text): Props | null{
return dom[internalKeys.props] || null;
}
@ -93,7 +94,8 @@ export function getEventListeners(dom: EventTarget): Set<string> {
export function getNonDelegatedListenerMap(target: EventTarget): Map<string, EventListener> {
let eventsMap = target[internalKeys.nonDelegatedEvents];
if (!eventsMap) {
eventsMap = target[internalKeys.nonDelegatedEvents] = new Map();
eventsMap = new Map();
target[internalKeys.nonDelegatedEvents] = eventsMap;
}
return eventsMap;
}

View File

@ -5,13 +5,13 @@ import {
import {
createDom,
} from './utils/DomCreator';
import {getSelectionInfo, resetSelectionRange, selectionData} from './SelectionRangeHandler';
import {isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus} from './utils/Common';
import {NSS} from './utils/DomCreator';
import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler';
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
import { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common';
import { NSS } from './utils/DomCreator';
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
import {listenDelegatedEvents} from '../event/EventBinding';
import type {VNode} from '../renderer/Types';
import { listenDelegatedEvents } from '../event/EventBinding';
import type { VNode } from '../renderer/Types';
import {
setInitValue,
getPropsWithoutValue,
@ -21,10 +21,10 @@ import {
compareProps,
setDomProps, updateDomProps
} from './DOMPropertiesHandler/DOMPropertiesHandler';
import {isNativeElement, validateProps} from './validators/ValidateProps';
import {watchValueChange} from './valueHandler/ValueChangeHandler';
import {DomComponent, DomText} from '../renderer/vnode/VNodeTags';
import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp';
import { isNativeElement, validateProps } from './validators/ValidateProps';
import { watchValueChange } from './valueHandler/ValueChangeHandler';
import { DomComponent, DomText } from '../renderer/vnode/VNodeTags';
import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp';
export type Props = {
autoFocus?: boolean;
@ -37,7 +37,7 @@ export type Props = {
export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode });
let selectionInfo: null | selectionData = null;
let selectionInfo: null | SelectionData = null;
function getChildNS(parentNS: string | null, tagName: string): string {
if (parentNS === NSS.svg && tagName === 'foreignObject') {
@ -54,18 +54,12 @@ function getChildNS(parentNS: string | null, tagName: string): string {
}
// 获取容器
export function getNSCtx(dom: Container, parentNS: string, type: string): string {
let namespace;
if (dom) {
namespace = getChildNS(dom.namespaceURI ?? null, dom.nodeName);
} else {
namespace = getChildNS(parentNS, type);
}
return namespace;
export function getNSCtx(parentNS: string, type: string, dom?: Container): string {
return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type);
}
export function prepareForSubmit(): void {
selectionInfo = <selectionData>getSelectionInfo();
selectionInfo = <SelectionData>getSelectionInfo();
}
export function resetAfterSubmit(): void {
@ -156,7 +150,7 @@ export function newTextDom(
// 提交vNode的类型为Component或者Text的更新
export function submitDomUpdate(tag: string, vNode: VNode) {
const newProps = vNode.props;
const element: Element = vNode.realNode;
const element: Element | null = vNode.realNode;
if (tag === DomComponent) {
// DomComponent类型
@ -179,8 +173,10 @@ export function submitDomUpdate(tag: string, vNode: VNode) {
}
}
} else if (tag === DomText) {
// text类型
element.textContent = newProps;
if (element != null) {
// text类型
element.textContent = newProps;
}
}
}

View File

@ -8,6 +8,30 @@ import {
} from '../../event/EventBinding';
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 (!isInit || (isInit && propVal != null)) {
updateCommonProp(dom, propName, propVal, isNativeTag);
}
}
}
// 初始化DOM属性
export function setDomProps(
tagName: string,
@ -21,7 +45,7 @@ export function setDomProps(
const propName = keysOfProps[i];
const propVal = props[propName];
updateOneProp(dom, propName, propVal, isNativeTag, true);
updateOneProp(dom, propName, isNativeTag, propVal, true);
}
}
@ -34,28 +58,7 @@ export function updateDomProps(
for (let i = 0; i < changeList.length; i++) {
const { propName, propVal } = changeList[i];
updateOneProp(dom, propName, propVal, isNativeTag);
}
}
function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) {
if (propName === 'style') {
setStyles(dom, propVal);
} else if (propName === 'dangerouslySetInnerHTML') {
dom.innerHTML = propVal.__html;
} else if (propName === 'children') { // 只处理纯文本子节点其他children在VNode树中处理
if (typeof propVal === 'string' || typeof propVal === 'number') {
dom.textContent = String(propVal);
}
} else if (isEventProp(propName)) {
// 事件监听属性处理
if (!allDelegatedHorizonEvents.has(propName)) {
listenNonDelegatedEvent(propName, dom, propVal);
}
} else {
if (!isInit || (isInit && propVal != null)) {
updateCommonProp(dom, propName, propVal, isNativeTag);
}
updateOneProp(dom, propName, isNativeTag, propVal);
}
}
@ -111,7 +114,7 @@ export function compareProps(
for (let i = 0; i < keysOfNewProps.length; i++) {
const propName = keysOfNewProps[i];
const newPropValue = newProps[propName];
const oldPropValue = oldProps != null ? oldProps[propName] : undefined;
const oldPropValue = oldProps != null ? oldProps[propName] : null;
if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) {
// 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理

View File

@ -1,3 +1,31 @@
function isNeedUnitCSS(propName: string) {
return !(noUnitCSS.includes(propName)
|| propName.startsWith('borderImage')
|| propName.startsWith('flex')
|| propName.startsWith('gridRow')
|| propName.startsWith('gridColumn')
|| propName.startsWith('stroke')
|| propName.startsWith('box')
|| propName.endsWith('Opacity'));
}
/**
* width: 10 => width: 10px
*
*
*/
export function adjustStyleValue(name, value) {
let validValue = value;
if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) {
validValue = `${value}px`;
} else if (value === '' || value == null || typeof value === 'boolean') {
validValue = '';
}
return validValue;
}
/**
* DOM style
*/
@ -21,31 +49,3 @@ export function setStyles(dom, styles) {
*/
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp',
'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom'];
function isNeedUnitCSS(propName: string) {
return !(noUnitCSS.includes(propName)
|| propName.startsWith('borderImage')
|| propName.startsWith('flex')
|| propName.startsWith('gridRow')
|| propName.startsWith('gridColumn')
|| propName.startsWith('stroke')
|| propName.startsWith('box')
|| propName.endsWith('Opacity'));
}
/**
* width: 10 => width: 10px
*
*
*/
export function adjustStyleValue(name, value) {
let validValue = value;
if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) {
validValue = `${value}px`;
} else if (value === '' || value == null || typeof value === 'boolean') {
validValue = '';
}
return validValue;
}

View File

@ -14,9 +14,15 @@ const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'ba
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,',
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'surfaceScale',
'systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget', 'xChannelSelector',
'yChannelSelector', 'zoomAndPan']);
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation',
'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY',
'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']);
// 驼峰 变 “-”
function convertToLowerCase(str) {
const replacer = (match, char) => `-${char.toLowerCase()}`;
return str.replace(/([A-Z])/g, replacer);
}
/**
* dom
@ -70,12 +76,3 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN
}
}
}
// 驼峰 变 “-”
function convertToLowerCase(str) {
const replacer = (match, char) => {
return `-${char.toLowerCase()}`;
}
return str.replace(/([A-Z])/g, replacer);
};

View File

@ -105,16 +105,16 @@ export function getSelectionInfo() {
};
}
export interface selectionData {
export interface SelectionData {
focusedDom: HTMLInputElement | HTMLTextAreaElement | void;
selectionRange: {
selectionRange?: {
start: number;
end: number;
}
};
};
}
// 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失
export function resetSelectionRange(preSelectionRangeData: selectionData) {
export function resetSelectionRange(preSelectionRangeData: SelectionData) {
// 当前 focus 的元素
const currentFocusedDom = getIFrameFocusedDom();

View File

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

View File

@ -1,9 +1,9 @@
export interface IProperty {
[propName: string]: any
[propName: string]: any;
}
export interface HorizonSelect extends HTMLSelectElement {
_multiple: boolean;
_multiple?: boolean;
}
export type HorizonDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;

View File

@ -6,19 +6,15 @@ export enum PROPERTY_TYPE {
STRING, // 普通的字符串类型
SPECIAL, // 需要特殊处理的属性类型
BOOLEAN_STR, // 字符串类型的 true false
};
}
export type PropDetails = {
propName: string,
type: PROPERTY_TYPE,
attrName: string,
attrNS: string | null,
propName: string;
type: PROPERTY_TYPE;
attrName: string;
attrNS: string | null;
};
export function getPropDetails(name: string): PropDetails | null {
return propsDetailData[name] || null;
}
// 属性相关数据
// 依次为 propertyName、type、attributeName、attributeNamespace不填则使用默认值
// type 默认 STRING
@ -123,3 +119,7 @@ propertiesData.forEach(record => {
attrNS,
};
});
export function getPropDetails(name: string): PropDetails | null {
return propsDetailData[name] || null;
}

View File

@ -34,6 +34,11 @@ function isInvalidBoolean(
return false;
}
// 是事件属性
export function isEventProp(propName) {
return propName.substr(0, 2) === 'on';
}
function isValidProp(tagName, name, value) {
// 校验事件名称
if (isEventProp(name)) {
@ -79,11 +84,6 @@ export function isInvalidValue(
return false;
}
// 是事件属性
export function isEventProp(propName) {
return propName.substr(0, 2) === 'on';
}
// dev模式下校验属性是否合法
export function validateProps(type, props) {
if (!props) {

View File

@ -1,14 +1,5 @@
import {HorizonSelect, IProperty} from '../utils/Interface';
// 更新 <option>
function updateValue(options, newValues: any, isMultiple: boolean) {
if (isMultiple) {
updateMultipleValue(options, newValues);
} else {
updateSingleValue(options, newValues);
}
}
function updateMultipleValue(options, newValues) {
const newValueSet = new Set();
@ -39,6 +30,15 @@ function updateSingleValue(options, newValue) {
}
}
// 更新 <option>
function updateValue(options, newValues: any, isMultiple: boolean) {
if (isMultiple) {
updateMultipleValue(options, newValues);
} else {
updateSingleValue(options, newValues);
}
}
export function getSelectPropsWithoutValue(dom: HorizonSelect, properties: Object) {
return {
...properties,

View File

@ -1,5 +1,5 @@
import type {AnyNativeEvent, ProcessingListenerList} from './Types';
import type {VNode} from '../renderer/Types';
import type { AnyNativeEvent, ProcessingListenerList } from './Types';
import type { VNode } from '../renderer/Types';
import {
CommonEventToHorizonMap,
@ -11,20 +11,20 @@ import {
throwCaughtEventError,
runListenerAndCatchFirstError,
} from './EventError';
import {getListeners as getBeforeInputListeners} from './simulatedEvtHandler/BeforeInputEventHandler';
import {getListeners as getCompositionListeners} from './simulatedEvtHandler/CompositionEventHandler';
import {getListeners as getChangeListeners} from './simulatedEvtHandler/ChangeEventHandler';
import {getListeners as getSelectionListeners} from './simulatedEvtHandler/SelectionEventHandler';
import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler';
import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler';
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
import {
getCustomEventNameWithOn,
uniqueCharCode,
getEventTarget
} from './utils';
import {createCommonCustomEvent} from './customEvents/EventFactory';
import {getListenersFromTree} from './ListenerGetter';
import {shouldUpdateValue, updateControlledValue} from './ControlledValueUpdater';
import {asyncUpdates, runDiscreteUpdates} from '../renderer/Renderer';
import {getExactNode} from '../renderer/vnode/VNodeUtils';
import { createCommonCustomEvent } from './customEvents/EventFactory';
import { getListenersFromTree } from './ListenerGetter';
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
import { getExactNode } from '../renderer/vnode/VNodeUtils';
// 获取事件触发的普通事件监听方法队列
function getCommonListeners(
@ -62,7 +62,7 @@ function getCommonListeners(
vNode,
horizonEvtName,
horizonEvent,
isCapture ? EVENT_TYPE_CAPTURE: EVENT_TYPE_BUBBLE,
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
);
}
@ -71,7 +71,7 @@ function processListeners(processingEventsList: ProcessingListenerList): void {
processingEventsList.forEach(eventUnitList => {
let lastVNode;
eventUnitList.forEach(eventUnit => {
const {vNode, currentTarget, listener, event} = eventUnit;
const { vNode, currentTarget, listener, event } = eventUnit;
if (vNode !== lastVNode && event.isPropagationStopped()) {
return;
}
@ -121,7 +121,9 @@ function getProcessListenersFacade(
));
}
if (nativeEvtName === 'compositionend' || nativeEvtName === 'compositionstart' || nativeEvtName === 'compositionupdate') {
if (nativeEvtName === 'compositionend' ||
nativeEvtName === 'compositionstart' ||
nativeEvtName === 'compositionupdate') {
processingListenerList = processingListenerList.concat(getCompositionListeners(
nativeEvtName,
nativeEvent,
@ -180,13 +182,14 @@ export function handleEventMain(
// 有事件正在执行,同步执行事件
if (isInEventsExecution) {
return triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
return;
}
// 没有事件在执行,经过调度再执行事件
isInEventsExecution = true;
try {
return asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode));
asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode));
} finally {
isInEventsExecution = false;
if (shouldUpdateValue()) {

View File

@ -5,10 +5,10 @@ import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
export type ListenerUnit = {
vNode: null | VNode,
listener: Function,
currentTarget: EventTarget,
event: CustomBaseEvent,
vNode: null | VNode;
listener: Function;
currentTarget: EventTarget;
event: CustomBaseEvent;
};
export type ListenerUnitList = Array<ListenerUnit>;

View File

@ -35,7 +35,8 @@ export const horizonEventToNativeMap = new Map([
['onCompositionUpdate', ['compositionupdate']],
['onBeforeInput', ['compositionend', 'keypress', 'textInput']],
['onChange', ['change', 'click', 'focusout', 'input',]],
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
['onAnimationEnd', [STYLE_AMT_END]],
['onAnimationIteration', [STYLE_AMT_ITERATION]],

View File

@ -4,6 +4,51 @@
import {VNode} from '../../renderer/Types';
// 从原生事件中复制属性到自定义事件中
function extendAttribute(target, source) {
const attributes = [
// AnimationEvent
'animationName', 'elapsedTime', 'pseudoElement',
// CompositionEvent、InputEvent
'data',
// DragEvent
'dataTransfer',
// FocusEvent
'relatedTarget',
// KeyboardEvent
'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey',
'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData',
// MouseEvent
'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY',
'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget',
// PointerEvent
'pointerId', 'width', 'height', 'pressure', 'tangentialPressure',
'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary',
// TouchEvent
'touches', 'targetTouches', 'changedTouches',
// TransitionEvent
'propertyName',
// UIEvent
'view', 'detail',
// WheelEvent
'deltaX', 'deltaY', 'deltaZ', 'deltaMode',
];
const length = attributes.length;
for (let i = 0; i < length; i++) {
const attr = attributes[i];
const type = source[attr];
if (type !== 'undefined') {
if (type === 'function') {
target[attr] = function() {
return source[attr].apply(source, arguments);
};
} else {
target[attr] = source[attr];
}
}
}
}
export class CustomBaseEvent {
data: string;
@ -36,7 +81,9 @@ export class CustomBaseEvent {
this.target = target;
this.timeStamp = nativeEvt.timeStamp || Date.now();
const defaultPrevented = nativeEvt.defaultPrevented != null ? nativeEvt.defaultPrevented : nativeEvt.returnValue === false;
const defaultPrevented = nativeEvt.defaultPrevented != null ?
nativeEvt.defaultPrevented :
nativeEvt.returnValue === false;
this.defaultPrevented = defaultPrevented;
this.preventDefault = this.preventDefault.bind(this);
@ -77,43 +124,3 @@ export class CustomBaseEvent {
this.isPropagationStopped = () => true;
}
}
// 从原生事件中复制属性到自定义事件中
function extendAttribute(target, source) {
const attributes = [
// AnimationEvent
'animationName', 'elapsedTime', 'pseudoElement',
// CompositionEvent、InputEvent
'data',
// DragEvent
'dataTransfer',
// FocusEvent
'relatedTarget',
// KeyboardEvent
'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey', 'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData',
// MouseEvent
'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY', 'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget',
// PointerEvent
'pointerId', 'width', 'height', 'pressure', 'tangentialPressure', 'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary',
// TouchEvent
'touches', 'targetTouches', 'changedTouches',
// TransitionEvent
'propertyName',
// UIEvent
'view', 'detail',
// WheelEvent
'deltaX', 'deltaY', 'deltaZ', 'deltaMode',
];
attributes.forEach(attr => {
if (typeof source[attr] !== 'undefined') {
if (typeof source[attr] === 'function') {
target[attr] = function() {
return source[attr].apply(source, arguments);
};
} else {
target[attr] = source[attr];
}
}
})
}

View File

@ -9,7 +9,7 @@ const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE);
function getInputCharsByNative(
eventName: string,
nativeEvent: any,
): string | void {
): string | null {
if (eventName === 'compositionend') {
return (nativeEvent.detail && nativeEvent.detail.data) || null;
}

View File

@ -13,7 +13,7 @@ const horizonEventName = 'onSelect'
let currentElement = null;
let currentVNode = null;
let lastSelection = null;
let lastSelection: Selection | null = null;
function initTargetCache(dom, vNode) {
if (isTextInputElement(dom) || dom.contentEditable === 'true') {
@ -85,7 +85,7 @@ export function getListeners(
target: null | EventTarget,
): ProcessingListenerList {
const targetNode = vNode ? getDom(vNode) : window;
let eventUnitList = [];
let eventUnitList: ProcessingListenerList = [];
switch (name) {
case 'focusin':
initTargetCache(targetNode, vNode);

View File

@ -12,6 +12,44 @@ function getItemKey(item: any, index: number): string {
return '.' + index.toString(36);
}
function mapChildrenToArray(
children: any,
arr: Array<any>,
prefix: string,
callback?: Function,
): number | void {
const type = typeof children;
switch (type) {
// 继承原有规格undefined和boolean类型按照null处理
case 'undefined':
case 'boolean':
callMapFun(null, arr, prefix, callback);
return;
case 'number':
case 'string':
callMapFun(children, arr, prefix, callback);
return;
case 'object':
if (children === null) {
callMapFun(null, arr, prefix, callback);
return;
}
const vtype = children.vtype;
if (vtype === TYPE_COMMON_ELEMENT || vtype === TYPE_PORTAL) {
callMapFun(children, arr, prefix, callback) ;
return;
}
if (Array.isArray(children)) {
processArrayChildren(children, arr, prefix, callback);
return;
}
throw new Error(
'Object is invalid as a Horizon child. '
);
// No Default
}
}
function processArrayChildren(
children: any,
arr: Array<any>,
@ -61,44 +99,6 @@ function callMapFun(
}
}
function mapChildrenToArray(
children: any,
arr: Array<any>,
prefix: string,
callback?: Function,
): number | void {
const type = typeof children;
switch (type) {
// 继承原有规格undefined和boolean类型按照null处理
case 'undefined':
case 'boolean':
callMapFun(null, arr, prefix, callback);
return;
case 'number':
case 'string':
callMapFun(children, arr, prefix, callback);
return;
case 'object':
if (children === null) {
callMapFun(null, arr, prefix, callback);
return;
}
const vtype = children.vtype;
if (vtype === TYPE_COMMON_ELEMENT || vtype === TYPE_PORTAL) {
callMapFun(children, arr, prefix, callback) ;
return;
}
if (Array.isArray(children)) {
processArrayChildren(children, arr, prefix, callback);
return;
}
throw new Error(
'Object is invalid as a Horizon child. '
);
// No Default
}
}
// 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg
function mapChildren(
children: any,

View File

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

View File

@ -31,7 +31,7 @@ let ctxOldPreviousContext: Object = {};
// capture阶段设置
function setNamespaceCtx(vNode: VNode, dom?: Container) {
const nextContext = getNSCtx(dom, ctxNamespace, vNode.type);
const nextContext = getNSCtx(ctxNamespace, vNode.type, dom);
vNode.setContext(CTX_NAMESPACE, ctxNamespace);
ctxNamespace = nextContext;

View File

@ -8,10 +8,60 @@ import type {Update} from './UpdateHandler';
import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
import {FlagUtils, Interrupted} from './vnode/VNodeFlags';
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
import {launchUpdateFromVNode, setBuildResultError, tryRenderRoot} from './TreeBuilder';
import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder';
import {setRootThrowError} from './submit/Submit';
import {handleSuspenseChildThrowError} from './render/SuspenseComponent';
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
import {BuildErrored, setBuildResult} from './GlobalVar';
function consoleError(error: any): void {
if (isDev) {
// 只打印message为了让测试用例能pass
console['error']('The codes throw the error: ' + error.message);
} else {
console['error'](error);
}
}
function handleRootError(
error: any,
) {
// 注意:如果根节点抛出错误,不会销毁整棵树,只打印日志,抛出异常。
setRootThrowError(error);
consoleError(error);
}
function createClassErrorUpdate(
vNode: VNode,
error: any,
): Update {
const update = newUpdate();
update.type = UpdateState.Error;
const getDerivedStateFromError = vNode.type.getDerivedStateFromError;
if (typeof getDerivedStateFromError === 'function') {
update.content = () => {
consoleError(error);
return getDerivedStateFromError(error);
};
}
const inst = vNode.realNode;
if (inst !== null && typeof inst.componentDidCatch === 'function') {
update.callback = function callback() {
if (typeof getDerivedStateFromError !== 'function') {
// 打印错误
consoleError(error);
}
// @ts-ignore
this.componentDidCatch(error, {
componentStack: '',
});
};
}
return update;
}
// 处理capture和bubble阶段抛出的错误
export function handleRenderThrowError(
@ -33,7 +83,7 @@ export function handleRenderThrowError(
}
// 抛出错误无法作为suspense内容处理或无suspense来处理这次当成真的错误来处理
setBuildResultError();
setBuildResult(BuildErrored);
// 向上遍历寻找ClassComponent组件同时也是Error Boundaries组件 或者 TreeRoot
let vNode = sourceVNode.parent;
@ -76,6 +126,19 @@ export function handleRenderThrowError(
} while (vNode !== null);
}
// 新增一个update并且触发调度
function triggerUpdate(vNode, state) {
const update = newUpdate();
update.content = state;
pushUpdate(vNode, update);
const root = updateShouldUpdateOfTree(vNode);
if (root !== null) {
tryRenderFromRoot(root);
}
}
// 处理submit阶段的异常
export function handleSubmitError(vNode: VNode, error: any) {
if (vNode.tag === TreeRoot) {
@ -126,64 +189,3 @@ export function handleSubmitError(vNode: VNode, error: any) {
node = node.parent;
}
}
function createClassErrorUpdate(
vNode: VNode,
error: any,
): Update {
const update = newUpdate();
update.type = UpdateState.Error;
const getDerivedStateFromError = vNode.type.getDerivedStateFromError;
if (typeof getDerivedStateFromError === 'function') {
update.content = () => {
consoleError(error);
return getDerivedStateFromError(error);
};
}
const inst = vNode.realNode;
if (inst !== null && typeof inst.componentDidCatch === 'function') {
update.callback = function callback() {
if (typeof getDerivedStateFromError !== 'function') {
// 打印错误
consoleError(error);
}
// @ts-ignore
this.componentDidCatch(error, {
componentStack: '',
});
};
}
return update;
}
// 新增一个update并且触发调度
function triggerUpdate(vNode, state) {
const update = newUpdate();
update.content = state;
pushUpdate(vNode, update);
const root = updateShouldUpdateOfTree(vNode);
if (root !== null) {
tryRenderRoot(root);
}
}
function handleRootError(
error: any,
) {
// 注意:如果根节点抛出错误,不会销毁整棵树,只打印日志,抛出异常。
setRootThrowError(error);
consoleError(error);
}
function consoleError(error: any): void {
if (isDev) {
// 只打印message为了让测试用例能pass
console['error']('The codes throw the error: ' + error.message);
} else {
console['error'](error);
}
}

View File

@ -130,6 +130,27 @@ function handleError(root, error): void {
bubbleVNode(processing);
}
// 判断数组中节点的path的idx元素是否都相等
function isEqualByIndex(idx: number, nodes: Array<VNode>) {
let val = nodes[0].path[idx];
for (let i = 1; i < nodes.length; i++) {
let node = nodes[i];
if (val !== node.path[idx]) {
return false;
}
}
return true;
}
function getChildByIndex(vNode: VNode, idx: number) {
let node = vNode.child;
for (let i = 0; i < idx; i++) {
node = node.next;
}
return node;
}
// 从多个更新节点中,计算出开始节点。即:找到最近的共同的父辈节点
export function calcStartUpdateVNode(treeRoot: VNode) {
const toUpdateNodes = Array.from(treeRoot.toUpdateNodes);
@ -228,6 +249,8 @@ function buildVNodeTree(treeRoot: VNode) {
}
} while (true);
processing = null;
setExecuteMode(preMode);
}
@ -250,7 +273,7 @@ function renderFromRoot(treeRoot) {
}
// 尝试去渲染,已有任务就跳出
export function tryRenderRoot(treeRoot: VNode) {
export function tryRenderFromRoot(treeRoot: VNode) {
if (treeRoot.shouldUpdate && treeRoot.task === null) {
// 任务放进queue但是调度开始还是异步的
treeRoot.task = pushRenderCallback(
@ -273,7 +296,7 @@ export function launchUpdateFromVNode(vNode: VNode) {
}
// 保存待刷新的节点
treeRoot.toUpdateNodes.add(vNode);
treeRoot.toUpdateNodes?.add(vNode);
if (checkMode(BySync) && // 非批量
!checkMode(InRender)) { // 不是渲染阶段触发
@ -282,7 +305,7 @@ export function launchUpdateFromVNode(vNode: VNode) {
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
renderFromRoot(treeRoot);
} else {
tryRenderRoot(treeRoot);
tryRenderFromRoot(treeRoot);
if (!isExecuting()) {
// 同步执行
@ -291,27 +314,6 @@ export function launchUpdateFromVNode(vNode: VNode) {
}
}
// 判断数组中节点的path的idx元素是否都相等
function isEqualByIndex(idx: number, nodes: Array<VNode>) {
let val = nodes[0].path[idx];
for (let i = 1; i < nodes.length; i++) {
let node = nodes[i];
if (val !== node.path[idx]) {
return false;
}
}
return true;
}
function getChildByIndex(vNode: VNode, idx: number) {
let node = vNode.child;
for (let i = 0; i < idx; i++) {
node = node.next;
}
return node;
}
export function setBuildResultError() {
if (getBuildResult() !== BuildCompleted) {
setBuildResult(BuildErrored);

View File

@ -21,7 +21,6 @@ export type JSXElement = {
key: any;
ref: any;
props: any;
belongClassVNode: any;
};

View File

@ -2,9 +2,8 @@ import type { VNode, JSXElement } from '../Types';
// 当前vNode和element是同样的类型
// LazyComponent 会修改type的类型所以特殊处理这种类型
export const isSameType = (vNode: VNode, ele: JSXElement) => {
return vNode.type === ele.type || (vNode.isLazyComponent && vNode.lazyType === ele.type);
};
export const isSameType = (vNode: VNode, ele: JSXElement) =>
vNode.type === ele.type || (vNode.isLazyComponent && vNode.lazyType === ele.type);
export function isTextType(newChild: any) {
return typeof newChild === 'string' || typeof newChild === 'number';

View File

@ -1,7 +1,7 @@
import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
import { DomText, DomPortal, Fragment } from '../vnode/VNodeTags';
import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags';
import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator';
import {
isSameType,
@ -17,7 +17,7 @@ enum DiffCategory {
TEXT_NODE = 'TEXT_NODE',
OBJECT_NODE = 'OBJECT_NODE',
ARR_NODE = 'ARR_NODE',
};
}
// 检查是不是被 FRAGMENT 包裹
function isNoKeyFragment(child: any) {
@ -31,7 +31,7 @@ function deleteVNode(parentNode: VNode, delVNode: VNode): void {
}
// 清除多个节点
function deleteVNodes(parentVNode: VNode, startDelVNode: VNode, endVNode?: VNode): void {
function deleteVNodes(parentVNode: VNode, startDelVNode: VNode | null, endVNode?: VNode): void {
let node = startDelVNode;
while (node !== null) {
@ -67,7 +67,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean {
return false;
}
function getNodeType(newChild: any): string {
function getNodeType(newChild: any): string | null {
if (newChild === null) {
return null;
}
@ -186,9 +186,7 @@ function transLeftChildrenToMap(
travelChildren(startChild, (node) => {
leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node);
}, (node) => {
return node === rightEndVNode;
});
}, node => node === rightEndVNode);
return leftChildrenMap;
}
@ -208,6 +206,11 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
return null;
}
function setCIndex(vNode: VNode, idx: number) {
vNode.cIndex = idx;
updateVNodePath(vNode);
}
// diff数组类型的节点核心算法
function diffArrayNodes(
parentNode: VNode,
@ -277,7 +280,7 @@ function diffArrayNodes(
// 从后往前新资源的位置还没有到最末端旧的vNode也还没遍历完则可以考虑从后往前开始
if (rightIdx > leftIdx && oldNode !== null) {
const rightRemainingOldChildren = transRightChildrenToArray(oldNode);
let rightOldIndex = rightRemainingOldChildren.length - 1;
let rightOldIndex: number | null = rightRemainingOldChildren.length - 1;
// 2. 从右侧开始比对currentVNode和newChildren若不能复用则跳出循环
for (; rightIdx > leftIdx; rightIdx--) {
@ -320,7 +323,12 @@ function diffArrayNodes(
// 3. 新节点已经处理完成
if (leftIdx === rightIdx) {
if (isComparing) {
deleteVNodes(parentNode, oldNode, rightEndOldNode);
if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) {
FlagUtils.markClear(parentNode);
parentNode.clearChild = firstChild;
} else {
deleteVNodes(parentNode, oldNode, rightEndOldNode);
}
}
if (rightNewNode) {
@ -372,7 +380,7 @@ function diffArrayNodes(
if (oldNodeFromMap !== null) {
let eIndex = newNode.eIndex;
eIndexes.push(eIndex);
const last = eIndexes[result[result.length - 1]];
const last: number | undefined = eIndexes[result[result.length - 1]];
if (eIndex > last || last === undefined) { // 大的 eIndex直接放在最后
preIndex[i] = result[result.length - 1];
result.push(i);
@ -450,11 +458,6 @@ function setVNodesCIndex(startChild: VNode, startIdx: number) {
}
}
function setCIndex(vNode: VNode, idx: number) {
vNode.cIndex = idx;
updateVNodePath(vNode);
}
// 新节点是数组类型
function diffArrayNodesHandler(
parentNode: VNode,

View File

@ -1,5 +1,5 @@
import type {VNode} from '../Types';
import type {Hook} from './HookType';
import type { VNode } from '../Types';
import type { Hook } from './HookType';
let processingVNode: VNode = null;
@ -56,7 +56,9 @@ export function getNextHook(hook: Hook<any, any>, hooks: Array<Hook<any, any>>)
// processing中的hook和上一次执行中的hook需要同时往前走
// 原因1.比对hook的数量有没有变化非必要2.从上一次执行中的hook获取removeEffect
export function getCurrentHook(): Hook<any, any> {
currentHook = currentHook !== null ? getNextHook(currentHook, processingVNode.hooks) : (processingVNode.hooks[0] || null);
currentHook = currentHook !== null ?
getNextHook(currentHook, processingVNode.hooks) :
(processingVNode.hooks[0] || null);
if (lastTimeHook !== null) {
lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks);

View File

@ -1,4 +1,4 @@
import type {Hook, Reducer, Trigger, Update} from './HookType';
import type { Hook, Reducer, Trigger, Update } from './HookType';
import {
createHook,
getCurrentHook,
@ -8,12 +8,13 @@ import {
import {
launchUpdateFromVNode
} from '../TreeBuilder';
import {isSame} from '../utils/compare';
import {setStateChange} from '../render/FunctionComponent';
import {getHookStage, HookStage} from './HookStage';
import type {VNode} from '../Types';
import { isSame } from '../utils/compare';
import { setStateChange } from '../render/FunctionComponent';
import { getHookStage, HookStage } from './HookStage';
import type { VNode } from '../Types';
export function useReducerImpl<S, P, A>(reducer: (S, A) => S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger<A>] {
export function useReducerImpl<S, P, A>(reducer: (S, A) =>
S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger<A>] | void {
const stage = getHookStage();
if (stage === null) {
throwNotInFuncError();

View File

@ -1,10 +1,6 @@
import type {VNode} from '../Types';
import {createVNodeChildren} from './BaseComponent';
export function captureRender(processing: VNode): VNode | null {
return captureFragment(processing);
}
export function bubbleRender() {}
function captureFragment(processing: VNode) {
@ -12,3 +8,7 @@ function captureFragment(processing: VNode) {
processing.child = createVNodeChildren(processing, newElement);
return processing.child;
}
export function captureRender(processing: VNode): VNode | null {
return captureFragment(processing);
}

View File

@ -10,7 +10,18 @@ import {
cacheOldCtx,
} from '../components/context/CompatibleContext';
export function captureRender(processing: VNode): Array<VNode> | null {
function captureIncompleteClassComponent(processing, Component, nextProps) {
processing.tag = ClassComponent;
const hasOldContext = isOldProvider(Component);
cacheOldCtx(processing, hasOldContext);
resetDepContexts(processing);
return getIncompleteClassComponent(Component, processing, nextProps);
}
export function captureRender(processing: VNode): VNode | null {
const Component = processing.type;
const unresolvedProps = processing.props;
const resolvedProps =
@ -28,14 +39,3 @@ export function bubbleRender(processing: VNode) {
resetOldCtx(processing);
}
}
function captureIncompleteClassComponent(processing, Component, nextProps) {
processing.tag = ClassComponent;
const hasOldContext = isOldProvider(Component);
cacheOldCtx(processing, hasOldContext);
resetDepContexts(processing);
return getIncompleteClassComponent(Component, processing, nextProps);
}

View File

@ -13,10 +13,6 @@ import { captureFunctionComponent } from './FunctionComponent';
import { captureClassComponent } from './ClassComponent';
import { captureMemoComponent } from './MemoComponent';
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
return captureLazyComponent(processing, processing.type, shouldUpdate);
}
export function bubbleRender() { }
const LazyRendererMap = {
@ -26,6 +22,20 @@ const LazyRendererMap = {
[MemoComponent]: captureMemoComponent,
};
export function mergeDefaultProps(Component: any, props: object): object {
if (Component && Component.defaultProps) {
const clonedProps = { ...props };
const defaultProps = Component.defaultProps;
Object.keys(defaultProps).forEach(key => {
if (clonedProps[key] === undefined) {
clonedProps[key] = defaultProps[key];
}
});
return clonedProps;
}
return props;
}
function captureLazyComponent(
processing,
lazyComponent,
@ -43,7 +53,8 @@ function captureLazyComponent(
// 加载得到的Component存在type中
processing.type = Component;
const lazyVNodeTag = processing.tag = getLazyVNodeTag(Component);
const lazyVNodeTag = getLazyVNodeTag(Component);
processing.tag = lazyVNodeTag;
const lazyVNodeProps = mergeDefaultProps(Component, processing.props);
const lazyRender = LazyRendererMap[lazyVNodeTag];
@ -68,16 +79,6 @@ function captureLazyComponent(
}
}
export function mergeDefaultProps(Component: any, props: object): object {
if (Component && Component.defaultProps) {
const clonedProps = { ...props };
const defaultProps = Component.defaultProps;
Object.keys(defaultProps).forEach(key => {
if (clonedProps[key] === undefined) {
clonedProps[key] = defaultProps[key];
}
});
return clonedProps;
}
return props;
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
return captureLazyComponent(processing, processing.type, shouldUpdate);
}

View File

@ -10,10 +10,6 @@ import {
} from '../../external/JSXElementType';
import {Fragment} from '../vnode/VNodeTags';
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
return captureMemoComponent(processing, shouldUpdate);
}
export function bubbleRender() {}
export function captureMemoComponent(
@ -59,3 +55,7 @@ export function captureMemoComponent(
return newChild;
}
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
return captureMemoComponent(processing, shouldUpdate);
}

View File

@ -9,7 +9,7 @@ import {
Fragment,
} from '../vnode/VNodeTags';
import {pushForceUpdate} from '../UpdateHandler';
import {launchUpdateFromVNode, tryRenderRoot} from '../TreeBuilder';
import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder';
import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate';
import {getContextChangeCtx} from '../ContextSaver';
@ -19,73 +19,36 @@ export enum SuspenseChildStatus {
ShowFallback = 'showFallback',
}
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null {
if (
!processing.isCreated &&
processing.oldProps === processing.props &&
!getContextChangeCtx() &&
!shouldUpdate
) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) {
// 当显示fallback时suspense的子组件要更新
return updateFallback(processing);
}
return onlyUpdateChildVNodes(processing);
}
return captureSuspenseComponent(processing);
}
function updateFallback(processing: VNode): Array<VNode> | VNode | null {
// 创建fallback子节点
function createFallback(processing: VNode, fallbackChildren) {
const childFragment: VNode = processing.child;
let fallbackFragment;
childFragment.childShouldUpdate = false;
if (childFragment.childShouldUpdate) {
if (processing.promiseResolve) {
// promise已完成展示promise返回的新节点
return captureSuspenseComponent(processing);
if (!processing.isCreated) {
const oldFallbackFragment: VNode | null = processing.oldChild ? processing.oldChild.next : null;
if (oldFallbackFragment !== null) {
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
} else {
// promise未完成继续显示fallback不需要继续刷新子节点
const fallbackFragment: VNode = processing.child.next;
childFragment.childShouldUpdate = false;
fallbackFragment.childShouldUpdate = false;
return null;
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
FlagUtils.markAddition(fallbackFragment);
}
} else {
const children = onlyUpdateChildVNodes(processing);
if (children !== null) {
// child不需要更新跳过child处理fallback
return children[1];
} else {
return null;
}
}
}
export function bubbleRender(processing: VNode) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback
|| (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback)
) {
FlagUtils.markUpdate(processing);
// 创建
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
}
return null;
}
processing.child = childFragment;
childFragment.next = fallbackFragment;
childFragment.parent = processing;
fallbackFragment.parent = processing;
fallbackFragment.eIndex = 1;
fallbackFragment.cIndex = 1;
updateVNodePath(fallbackFragment);
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback;
export function captureSuspenseComponent(processing: VNode) {
const nextProps = processing.props;
// suspense被捕获后需要展示fallback
const showFallback = processing.suspenseDidCapture;
if (showFallback) {
processing.suspenseDidCapture = false;
const nextFallbackChildren = nextProps.fallback;
return createFallback(processing, nextFallbackChildren);
} else {
const newChildren = nextProps.children;
return createSuspenseChildren(processing, newChildren);
}
return fallbackFragment;
}
// 创建子节点
@ -120,36 +83,77 @@ function createSuspenseChildren(processing: VNode, newChildren) {
return processing.child;
}
// 创建fallback子节点
function createFallback(processing: VNode, fallbackChildren) {
const childFragment: VNode = processing.child;
let fallbackFragment;
childFragment.childShouldUpdate = false;
export function captureSuspenseComponent(processing: VNode) {
const nextProps = processing.props;
if (!processing.isCreated) {
const oldFallbackFragment: VNode | null = processing.oldChild ? processing.oldChild.next : null;
// suspense被捕获后需要展示fallback
const showFallback = processing.suspenseDidCapture;
if (oldFallbackFragment !== null) {
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
if (showFallback) {
processing.suspenseDidCapture = false;
const nextFallbackChildren = nextProps.fallback;
return createFallback(processing, nextFallbackChildren);
} else {
const newChildren = nextProps.children;
return createSuspenseChildren(processing, newChildren);
}
}
function updateFallback(processing: VNode): Array<VNode> | VNode | null {
const childFragment: VNode | null= processing.child;
if (childFragment?.childShouldUpdate) {
if (processing.promiseResolve) {
// promise已完成展示promise返回的新节点
return captureSuspenseComponent(processing);
} else {
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
FlagUtils.markAddition(fallbackFragment);
// promise未完成继续显示fallback不需要继续刷新子节点
const fallbackFragment: VNode = processing.child.next;
childFragment.childShouldUpdate = false;
fallbackFragment.childShouldUpdate = false;
return null;
}
} else {
// 创建
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
const children = onlyUpdateChildVNodes(processing);
if (children !== null) {
// child不需要更新跳过child处理fallback
return children[1];
} else {
return null;
}
}
}
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null {
if (
!processing.isCreated &&
processing.oldProps === processing.props &&
!getContextChangeCtx() &&
!shouldUpdate
) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) {
// 当显示fallback时suspense的子组件要更新
return updateFallback(processing);
}
return onlyUpdateChildVNodes(processing);
}
processing.child = childFragment;
childFragment.next = fallbackFragment;
childFragment.parent = processing;
fallbackFragment.parent = processing;
fallbackFragment.eIndex = 1;
fallbackFragment.cIndex = 1;
updateVNodePath(fallbackFragment);
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback;
return captureSuspenseComponent(processing);
}
return fallbackFragment;
export function bubbleRender(processing: VNode) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback
|| (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback)
) {
FlagUtils.markUpdate(processing);
}
return null;
}
function canCapturePromise(vNode: VNode | null): boolean {
return vNode?.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined;
}
// 处理Suspense子组件抛出的promise
@ -196,12 +200,20 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
return false;
}
function canCapturePromise(vNode: VNode): boolean {
return vNode.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode.props.fallback !== undefined;
}
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
const promiseCache = suspenseVNode.realNode;
if (promiseCache !== null) {
promiseCache.delete(promise);
}
suspenseVNode.promiseResolve = true;
const root = updateShouldUpdateOfTree(suspenseVNode);
if (root !== null) {
tryRenderFromRoot(root);
}
}
// 对于每个promise添加一个侦听器以便当它resolve时重新渲染
export function listenToPromise(suspenseVNode: VNode) {
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises;
@ -212,7 +224,8 @@ export function listenToPromise(suspenseVNode: VNode) {
let promiseCache = suspenseVNode.realNode;
if (promiseCache === null) {
// @ts-ignore
promiseCache = suspenseVNode.realNode = new PossiblyWeakSet();
promiseCache = new PossiblyWeakSet();
suspenseVNode.realNode = new PossiblyWeakSet();
}
promises.forEach(promise => {
@ -225,17 +238,3 @@ export function listenToPromise(suspenseVNode: VNode) {
});
}
}
function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
const promiseCache = suspenseVNode.realNode;
if (promiseCache !== null) {
promiseCache.delete(promise);
}
suspenseVNode.promiseResolve = true;
const root = updateShouldUpdateOfTree(suspenseVNode);
if (root !== null) {
tryRenderRoot(root);
}
}

View File

@ -6,10 +6,6 @@ import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver';
import {resetOldCtx} from '../components/context/CompatibleContext';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
export function captureRender(processing: VNode): VNode | null {
return updateTreeRoot(processing);
}
export function bubbleRender(processing: VNode) {
resetNamespaceCtx(processing);
resetOldCtx(processing);
@ -40,3 +36,7 @@ function updateTreeRoot(processing) {
processing.child = createVNodeChildren(processing, newElement);
return processing.child;
}
export function captureRender(processing: VNode): VNode | null {
return updateTreeRoot(processing);
}

View File

@ -1,7 +1,7 @@
import type { VNode } from '../../Types';
import type { Callback } from '../../UpdateHandler';
import {shallowCompare} from '../../utils/compare';
import { shallowCompare } from '../../utils/compare';
import {
pushUpdate,
newUpdate,
@ -40,7 +40,7 @@ function changeStateContent(type: UpdateState, content: object, callback: Callba
if (type === UpdateState.Update || type === UpdateState.Override) {
update.content = content;
}
if (callback !== undefined && callback !== null) {
if (callback) {
update.callback = callback;
}

View File

@ -2,10 +2,10 @@
*
*/
import type {Container} from '../../dom/DOMOperator';
import type {RefType, VNode} from '../Types';
import type { Container } from '../../dom/DOMOperator';
import type { RefType, VNode } from '../Types';
import {listenToPromise, SuspenseChildStatus} from '../render/SuspenseComponent';
import { listenToPromise, SuspenseChildStatus } from '../render/SuspenseComponent';
import {
FunctionComponent,
ForwardRef,
@ -17,8 +17,8 @@ import {
SuspenseComponent,
MemoComponent,
} from '../vnode/VNodeTags';
import {FlagUtils, ResetText} from '../vnode/VNodeFlags';
import {mergeDefaultProps} from '../render/LazyComponent';
import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags';
import { mergeDefaultProps } from '../render/LazyComponent';
import {
submitDomUpdate,
clearText,
@ -29,15 +29,20 @@ import {
unHideDom,
clearContainer,
} from '../../dom/DOMOperator';
import {callEffectRemove, callUseEffects, callUseLayoutEffectCreate, callUseLayoutEffectRemove} from './HookEffectHandler';
import {handleSubmitError} from '../ErrorHandler';
import {
callEffectRemove,
callUseEffects,
callUseLayoutEffectCreate,
callUseLayoutEffectRemove
} from './HookEffectHandler';
import { handleSubmitError } from '../ErrorHandler';
import {
travelVNodeTree,
clearVNode,
isDomVNode,
findDomParent, getSiblingDom,
} from '../vnode/VNodeUtils';
import {shouldAutoFocus} from '../../dom/utils/Common';
import { shouldAutoFocus } from '../../dom/utils/Common';
function callComponentWillUnmount(vNode: VNode, instance: any) {
try {
@ -71,7 +76,7 @@ function callBeforeSubmitLifeCycles(
const root = vNode.realNode;
clearContainer(root.outerDom);
}
// No Default
}
}
@ -160,39 +165,26 @@ function hideOrUnhideAllChildren(vNode, isHidden) {
function attachRef(vNode: VNode) {
const ref = vNode.ref;
if (ref !== null) {
const instance = vNode.realNode;
let refType = typeof ref;
if (refType === 'function') {
ref(instance);
} else if (refType === 'object') {
(<RefType>ref).current = instance;
} else {
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
vNode.belongClassVNode.realNode.refs[String(ref)] = instance;
}
}
}
handleRef(vNode, ref, vNode.realNode);
}
function detachRef(vNode: VNode, isOldRef?: boolean) {
let ref = (isOldRef ? vNode.oldRef : vNode.ref);
if (ref !== null) {
let refType = typeof ref;
handleRef(vNode, ref, null);
}
function handleRef(vNode: VNode, ref, val) {
if (ref !== null && ref !== undefined) {
const refType = typeof ref;
if (refType === 'function') {
try {
ref(null);
} catch (error) {
handleSubmitError(vNode, error);
}
ref(val);
} else if (refType === 'object') {
(<RefType>ref).current = null;
(<RefType>ref).current = val;
} else {
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
vNode.belongClassVNode.realNode.refs[String(ref)] = null;
vNode.belongClassVNode.realNode.refs[String(ref)] = val;
}
}
}
@ -233,14 +225,14 @@ function unmountVNode(vNode: VNode): void {
function unmountNestedVNodes(vNode: VNode): void {
travelVNodeTree(vNode, (node) => {
unmountVNode(node);
}, (node) => {
}, node =>
// 如果是DomPortal不需要遍历child
return node.tag === DomPortal;
});
node.tag === DomPortal
);
}
function submitAddition(vNode: VNode): void {
const {parent, parentDom} = findDomParent(vNode);
const { parent, parentDom } = findDomParent(vNode);
if (parent.flags.ResetText) {
// 在insert之前先reset
@ -257,7 +249,7 @@ function insertOrAppendPlacementNode(
beforeDom: Element | null,
parent: Element | Container,
): void {
const {tag, realNode} = node;
const { tag, realNode } = node;
if (isDomVNode(node)) {
if (beforeDom) {
@ -304,10 +296,9 @@ function unmountDomComponents(vNode: VNode): void {
} else {
unmountVNode(node);
}
}, (node) => {
}, node =>
// 如果是dom不用再遍历child
return node.tag === DomComponent || node.tag === DomText;
}, null, (node) => {
node.tag === DomComponent || node.tag === DomText, null, (node) => {
if (node.tag === DomPortal) {
// 当离开portal需要重新设置parent
currentParentIsValid = false;
@ -315,6 +306,43 @@ function unmountDomComponents(vNode: VNode): void {
});
}
function submitClear(vNode: VNode): void {
const realNode = vNode.realNode;
const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制
// 真实 dom 获取的keys只包含新增的属性
// 比如真实 dom 拿到的 keys 一般只有两个 horizon 自定义属性
// 但考虑到用户可能自定义其他属性,所以采用遍历赋值的方式
const customizeKeys = Object.keys(realNode);
const keyLength = customizeKeys.length;
for(let i = 0; i < keyLength; i++) {
const key = customizeKeys[i];
// 测试代码 mock 实例的全部可遍历属性都会被Object.keys方法读取到
// children 属性被复制意味着复制了子节点,因此要排除
if (key !== 'children') {
cloneDom[key] = realNode[key]; // 复制cloneNode未能复制的属性
}
}
const parentObj = findDomParent(vNode);
const currentParent = parentObj.parentDom;
let clearChild = vNode.clearChild as VNode; // 上次渲染的child保存在clearChild属性中
// 卸载 clearChild 和 它的兄弟节点
while(clearChild) {
// 卸载子vNode递归遍历子vNode
unmountNestedVNodes(clearChild);
clearVNode(clearChild);
clearChild = clearChild.next as VNode;
}
// 在所有子项都卸载后删除dom树中的节点
removeChildDom(currentParent, vNode.realNode);
currentParent.append(cloneDom);
vNode.realNode = cloneDom;
attachRef(vNode);
FlagUtils.removeFlag(vNode, Clear);
vNode.clearChild = null;
}
function submitDeletion(vNode: VNode): void {
// 遍历所有子节点删除dom节点detach ref 和 调用componentWillUnmount()
unmountDomComponents(vNode);
@ -361,6 +389,7 @@ export {
submitResetTextContent,
submitAddition,
submitDeletion,
submitClear,
submitUpdate,
callAfterSubmitLifeCycles,
attachRef,

View File

@ -9,9 +9,9 @@ import {
attachRef,
callAfterSubmitLifeCycles,
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
submitResetTextContent, submitUpdate, detachRef,
submitResetTextContent, submitUpdate, detachRef, submitClear,
} from './LifeCycleHandler';
import {tryRenderRoot, setProcessing} from '../TreeBuilder';
import {tryRenderFromRoot, setProcessing} from '../TreeBuilder';
import {
BySync,
InRender,
@ -35,13 +35,10 @@ let lastRoot: VNode | null = null;
export function submitToRender(treeRoot) {
treeRoot.shouldUpdate = treeRoot.childShouldUpdate;
const startVNode = getStartVNode();
// 置空task让才能加入新的render任务
treeRoot.task = null;
setProcessing(null);
const startVNode = getStartVNode();
if (FlagUtils.hasAnyFlag(startVNode)) {
// 把自己加上
@ -81,7 +78,7 @@ export function submitToRender(treeRoot) {
countLoopingUpdate(treeRoot);
// 在退出`submit` 之前始终调用此函数以确保任何已计划在此根上执行的update被执行。
tryRenderRoot(treeRoot);
tryRenderFromRoot(treeRoot);
if (rootThrowError) {
const error = rootThrowError;
@ -124,7 +121,7 @@ function submit(dirtyNodes: Array<VNode>) {
}
}
const {Addition, Update, Deletion} = node.flags;
const {Addition, Update, Deletion, Clear} = node.flags;
if (Addition && Update) {
// Addition
submitAddition(node);
@ -141,6 +138,9 @@ function submit(dirtyNodes: Array<VNode>) {
} else if (Deletion) {
submitDeletion(node);
}
if (Clear) {
submitClear(node);
}
}
} catch (error) {
throwIfTrue(node === null, 'Should be working on an effect.');

View File

@ -2,65 +2,46 @@
*
*/
export let now;
if (typeof performance === 'object' && typeof performance.now === 'function') {
const localPerformance = performance;
now = () => localPerformance.now();
} else {
const localDate = Date;
const initialTime = localDate.now();
now = () => localDate.now() - initialTime;
}
let isMessageLoopRunning = false;
let browserCallback = null;
const { port1, port2 } = new MessageChannel();
// 默认每次只运行5ms
const runTime = 5;
let deadline = 0;
export function isOverTime() {
return now() >= deadline;
return false;
}
// 1、设置deadline2、回调TaskExecutor传过来的browserCallback
const callRenderTasks = () => {
if (browserCallback == null) {
if (browserCallback === null) {
return;
}
const currentTime = now();
// 计算deadline
deadline = currentTime + runTime;
try {
// 执行callback
const hasMoreTask = browserCallback(
currentTime,
);
const hasMoreTask = browserCallback();
if (!hasMoreTask) { // 没有更多task
isMessageLoopRunning = false;
browserCallback = null;
} else {
// 还有task继续调用
port.postMessage(null);
port2.postMessage(null);
}
} catch (error) {
port.postMessage(null);
port2.postMessage(null);
throw error;
}
};
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = callRenderTasks;
port1.onmessage = callRenderTasks;
export function requestBrowserCallback(callback) {
browserCallback = callback;
if (!isMessageLoopRunning) {
isMessageLoopRunning = true;
port.postMessage(null);
port2.postMessage(null);
}
}

View File

@ -12,20 +12,6 @@ let callingQueueTask: any | null = null;
// 防止重入
let isCallingRenderQueue = false;
export function pushRenderCallback(callback: RenderCallback) {
if (renderQueue === null) {
renderQueue = [callback];
// 高优先级的异步调度
callingQueueTask = runAsync(callRenderQueue, ImmediatePriority);
} else {
// 不需要调度在syncQueue创建的时候已经调度了
renderQueue.push(callback);
}
// 返回一个空对象用于区别null
return {};
}
export function callRenderQueueImmediate() {
if (callingQueueTask !== null) {
// 取消异步调度
@ -42,27 +28,31 @@ function callRenderQueue() {
// 防止重入
isCallingRenderQueue = true;
let i = 0;
try {
for (; i < renderQueue.length; i++) {
let callback = renderQueue[i];
do {
callback = callback();
} while (callback !== null);
let callback;
while (callback = renderQueue.shift()) {
callback();
}
renderQueue = null;
} catch (error) {
// 如果有异常抛出,请将剩余的回调留在队列中
if (renderQueue !== null) {
renderQueue = renderQueue.slice(i + 1);
}
// 在下一个异步中再调用
runAsync(callRenderQueueImmediate, ImmediatePriority);
throw error;
} finally {
isCallingRenderQueue = false;
}
}
}
export function pushRenderCallback(callback: RenderCallback) {
if (renderQueue === null) {
renderQueue = [callback];
// 高优先级的异步调度
callingQueueTask = runAsync(callRenderQueue, ImmediatePriority);
} else {
// 不需要调度在syncQueue创建的时候已经调度了
renderQueue.push(callback);
}
// 返回一个空对象用于区别null
return {};
}

View File

@ -2,13 +2,13 @@
*
*/
import { Node } from '../taskExecutor/TaskQueue';
import {
requestBrowserCallback,
isOverTime,
now,
} from './BrowserAsync';
import {add, shift, first} from './TaskQueue';
import { add, shift, first, remove } from './TaskQueue';
const ImmediatePriority = 1;
const NormalPriority = 10;
@ -16,89 +16,37 @@ const NormalPriority = 10;
// 用于控制插入任务的顺序
let idCounter = 1;
let currentPriorityLevel = NormalPriority;
// 正在执行task
let isProcessing = false;
// 调度中,等待浏览器回调
let isScheduling = false;
let isWaiting = false;
function runSync(callback, priorityLevel = NormalPriority) {
const previousPriorityLevel = currentPriorityLevel;
currentPriorityLevel = priorityLevel;
try {
return callback();
} finally {
currentPriorityLevel = previousPriorityLevel;
}
}
function runAsync(callback, priorityLevel= NormalPriority ) {
let timeout;
switch (priorityLevel) {
case ImmediatePriority:
timeout = -1;
break;
case NormalPriority:
default:
timeout = 5000;
break;
}
const task = {
id: idCounter++,
callback,
priorityLevel,
expirationTime: now() + timeout,
};
add(task);
if (!isScheduling && !isProcessing) {
isScheduling = true;
requestBrowserCallback(callTasks);
}
return task;
}
function callTasks(initialTime) {
isScheduling = false;
function callTasks() {
isWaiting = false;
isProcessing = true;
let task = null;
const previousPriorityLevel = currentPriorityLevel;
let task: Node | null= null;
try {
let currentTime = initialTime;
task = first();
// 循环执行task
while (task !== null) {
if (
task.expirationTime > currentTime &&
isOverTime()
) {
// 任务的过期时间大于当前时间没达到此任务的过期时间且超过了deadline
if (isOverTime()) {
// 超过了deadline
break;
}
const callback = task.callback;
if (typeof callback === 'function') {
if (callback !== null) {
task.callback = null;
currentPriorityLevel = task.priorityLevel;
const didUserCallbackTimeout = task.expirationTime <= currentTime;
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = now();
// 执行callback返回函数重置callback
if (typeof continuationCallback === 'function') {
task.callback = continuationCallback;
} else {
if (task === first()) {
shift();
}
callback();
if (task === first()) {
shift();
} else { // 执行任务中可能插入了新任务
remove(task);
}
} else {
shift();
@ -110,26 +58,45 @@ function callTasks(initialTime) {
// 返回是否还有任务,如果有,说明是被中断了
return task !== null;
} finally {
task = null;
currentPriorityLevel = previousPriorityLevel;
isProcessing = false;
}
}
function runAsync(callback, priorityLevel= NormalPriority ) {
let increment;
switch (priorityLevel) {
case ImmediatePriority:
increment = -1;
break;
case NormalPriority:
default:
increment = 10000;
break;
}
const task = {
id: idCounter++,
callback,
order: idCounter + increment,
};
add(task);
if (!isWaiting && !isProcessing) {
isWaiting = true;
requestBrowserCallback(callTasks);
}
return task;
}
function cancelTask(task) {
task.callback = null;
}
function getCurrentPriorityLevel() {
return currentPriorityLevel;
}
export {
ImmediatePriority,
NormalPriority,
runSync,
runAsync,
cancelTask,
getCurrentPriorityLevel,
now,
};

View File

@ -3,25 +3,18 @@
*/
type Queue = Array<Node>;
type Node = {
export type Node = {
id: number;
expirationTime: number;
order: number;
};
// 任务队列
const taskQueue: Queue = [];
export function add(node: Node): void {
// 查找第一个大于等于 value 的下标,都比 value 小则返回 -1
const idx = getBiggerIdx(node);
if (idx === 0) {
taskQueue.unshift(node);
} else if (idx === -1) {
taskQueue.push(node);
} else {
taskQueue.splice(idx, 0, node);
}
function compare(a: Node, b: Node) {
// 优先先用index排序其次用id
const diff = a.order - b.order;
return diff !== 0 ? diff : a.id - b.id;
}
// 二分法查找第一个大于等于 value 的下标,都比 value 小则返回 -1时间复杂度O(logn)
@ -32,17 +25,29 @@ function getBiggerIdx(node: Node) {
while (left <= right) {
const middle = left + ((right - left) >> 1);
if (compare(taskQueue[middle], node) > 0)
if (compare(taskQueue[middle], node) > 0) {
right = middle - 1;
else
} else {
left = middle + 1;
}
}
return (left < taskQueue.length) ? left : -1;
return left < taskQueue.length ? left : -1;
}
export function add(node: Node): void {
// 查找第一个大于等于 value 的下标,都比 value 小则返回 -1
const idx = getBiggerIdx(node);
if (idx === -1) {
taskQueue.push(node);
} else {
taskQueue.splice(idx, 0, node);
}
}
export function first(): Node | null {
const val = taskQueue[0];
const val: Node | null | undefined = taskQueue[0];
return val !== undefined ? val : null;
}
@ -51,8 +56,6 @@ export function shift(): Node | null {
return val !== undefined ? val : null;
}
function compare(a: Node, b: Node) {
// 优先先用index排序其次用id
const diff = a.expirationTime - b.expirationTime;
return diff !== 0 ? diff : a.id - b.id;
export function remove(node: Node) {
taskQueue.splice(taskQueue.indexOf(node), 1);
}

View File

@ -42,9 +42,9 @@ export function shallowCompare(paramX: any, paramY: any): boolean {
return false;
}
return keysX.every((key, i) => {
return Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]]);
});
return keysX.every((key, i) =>
Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]])
);
}
return false;

View File

@ -49,19 +49,20 @@ export class VNode {
// 因为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,
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;
// one tree相关属性
isCreated: boolean = true;
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook

View File

@ -175,7 +175,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null {
// 此vNode无需更新但是子树需要
if (!processing.isCreated && processing.child !== null) {
// 更新子节点
let child = processing.child;
let child: VNode | null = processing.child;
while (child !== null) {
updateVNode(child, child.props);
updateVNodePath(child);

View File

@ -18,8 +18,10 @@ export const Interrupted = 'Interrupted';
export const ShouldCapture = 'ShouldCapture';
// For suspense
export const ForceUpdate = 'ForceUpdate';
export const Clear = 'Clear';
const FlagArr = [Addition, Update, Deletion, ResetText, Callback, DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
const FlagArr = [Addition, Update, Deletion, Clear, ResetText, Callback,
DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
@ -37,14 +39,15 @@ export class FlagUtils {
});
}
static hasAnyFlag(node: VNode) { // 有标志位
let keyFlag = false;
FlagArr.forEach(key => {
if (node.flags[key]) {
keyFlag = true;
return;
const flags = node.flags;
const arrLength = FlagArr.length;
for(let i = 0; i < arrLength; i++) {
const key = FlagArr[i];
if (flags[key]) {
return true;
}
});
return keyFlag;
}
return false;
}
static setNoFlags(node: VNode) {
@ -89,5 +92,9 @@ export class FlagUtils {
static markForceUpdate(node: VNode) {
node.flags.ForceUpdate = true;
}
static markClear(node: VNode) {
node.flags.Clear = true;
}
}

View File

@ -37,6 +37,19 @@ export function updateChildShouldUpdate(vNode: VNode) {
vNode.childShouldUpdate = false;
}
// 更新从当前节点到根节点的childShouldUpdate为true
export function setParentsChildShouldUpdate(parent: VNode | null) {
let node = parent;
while (node !== null) {
if (node.childShouldUpdate) {
break;
}
node.childShouldUpdate = true;
node = node.parent;
}
}
// 设置节点的所有父节点的childShouldUpdate
export function updateParentsChildShouldUpdate(vNode: VNode) {
let node = vNode.parent;
@ -51,16 +64,3 @@ export function updateParentsChildShouldUpdate(vNode: VNode) {
}
}
}
// 更新从当前节点到根节点的childShouldUpdate为true
export function setParentsChildShouldUpdate(parent: VNode | null) {
let node = parent;
while (node !== null) {
if (node.childShouldUpdate) {
break;
}
node.childShouldUpdate = true;
node = node.parent;
}
}