Match-id-390fa196e04fd15495885107435c85f201dfd871
This commit is contained in:
commit
863c4fe684
|
@ -10,30 +10,6 @@ import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
|
||||||
import {TreeRoot} from '../renderer/vnode/VNodeTags';
|
import {TreeRoot} from '../renderer/vnode/VNodeTags';
|
||||||
import {Callback} from '../renderer/UpdateHandler';
|
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) {
|
function createRoot(children: any, container: Container, callback?: Callback) {
|
||||||
// 清空容器
|
// 清空容器
|
||||||
let child = container.lastChild;
|
let child = container.lastChild;
|
||||||
|
@ -66,7 +42,31 @@ function createRoot(children: any, container: Container, callback?: Callback) {
|
||||||
return treeRoot;
|
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) {
|
if (domOrEle == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,12 @@ const internalKeys = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// 通过 VNode 实例获取 DOM 节点
|
// 通过 VNode 实例获取 DOM 节点
|
||||||
export function getDom(vNode: VNode): Element | Text | void {
|
export function getDom(vNode: VNode): Element | Text | null {
|
||||||
const {tag} = vNode;
|
const {tag} = vNode;
|
||||||
if (tag === DomComponent || tag === DomText) {
|
if (tag === DomComponent || tag === DomText) {
|
||||||
return vNode.realNode;
|
return vNode.realNode;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将 VNode 属性相关信息挂到 DOM 对象的特定属性上
|
// 将 VNode 属性相关信息挂到 DOM 对象的特定属性上
|
||||||
|
@ -72,7 +73,7 @@ export function getNearestVNode(dom: Node): null | VNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 vNode 上的属性相关信息
|
// 获取 vNode 上的属性相关信息
|
||||||
export function getVNodeProps(dom: Element | Text): Props {
|
export function getVNodeProps(dom: Element | Text): Props | null{
|
||||||
return dom[internalKeys.props] || null;
|
return dom[internalKeys.props] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,8 @@ export function getEventListeners(dom: EventTarget): Set<string> {
|
||||||
export function getEventToListenerMap(target: EventTarget): Map<string, EventListener> {
|
export function getEventToListenerMap(target: EventTarget): Map<string, EventListener> {
|
||||||
let eventsMap = target[internalKeys.nonDelegatedEvents];
|
let eventsMap = target[internalKeys.nonDelegatedEvents];
|
||||||
if (!eventsMap) {
|
if (!eventsMap) {
|
||||||
eventsMap = target[internalKeys.nonDelegatedEvents] = new Map();
|
eventsMap = new Map();
|
||||||
|
target[internalKeys.nonDelegatedEvents] = eventsMap;
|
||||||
}
|
}
|
||||||
return eventsMap;
|
return eventsMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ import {
|
||||||
import {
|
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 { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common';
|
||||||
import {NSS} from './utils/DomCreator';
|
import { NSS } from './utils/DomCreator';
|
||||||
import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler';
|
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
|
||||||
|
|
||||||
import {listenDelegatedEvents} from '../event/EventBinding';
|
import { listenDelegatedEvents } from '../event/EventBinding';
|
||||||
import type {VNode} from '../renderer/Types';
|
import type { VNode } from '../renderer/Types';
|
||||||
import {
|
import {
|
||||||
setInitValue,
|
setInitValue,
|
||||||
getPropsWithoutValue,
|
getPropsWithoutValue,
|
||||||
|
@ -21,10 +21,10 @@ import {
|
||||||
compareProps,
|
compareProps,
|
||||||
setDomProps, updateDomProps
|
setDomProps, updateDomProps
|
||||||
} from './DOMPropertiesHandler/DOMPropertiesHandler';
|
} from './DOMPropertiesHandler/DOMPropertiesHandler';
|
||||||
import {isNativeElement, validateProps} from './validators/ValidateProps';
|
import { isNativeElement, validateProps } from './validators/ValidateProps';
|
||||||
import {watchValueChange} from './valueHandler/ValueChangeHandler';
|
import { watchValueChange } from './valueHandler/ValueChangeHandler';
|
||||||
import {DomComponent, DomText} from '../renderer/vnode/VNodeTags';
|
import { DomComponent, DomText } from '../renderer/vnode/VNodeTags';
|
||||||
import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp';
|
import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
|
@ -37,7 +37,7 @@ export type Props = {
|
||||||
|
|
||||||
export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode });
|
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 {
|
function getChildNS(parentNS: string | null, tagName: string): string {
|
||||||
if (parentNS === NSS.svg && tagName === 'foreignObject') {
|
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 {
|
export function getNSCtx(parentNS: string, type: string, dom?: Container): string {
|
||||||
let namespace;
|
return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type);
|
||||||
if (dom) {
|
|
||||||
namespace = getChildNS(dom.namespaceURI ?? null, dom.nodeName);
|
|
||||||
} else {
|
|
||||||
namespace = getChildNS(parentNS, type);
|
|
||||||
}
|
|
||||||
return namespace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareForSubmit(): void {
|
export function prepareForSubmit(): void {
|
||||||
selectionInfo = <selectionData>getSelectionInfo();
|
selectionInfo = <SelectionData>getSelectionInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetAfterSubmit(): void {
|
export function resetAfterSubmit(): void {
|
||||||
|
@ -156,7 +150,7 @@ export function newTextDom(
|
||||||
// 提交vNode的类型为Component或者Text的更新
|
// 提交vNode的类型为Component或者Text的更新
|
||||||
export function submitDomUpdate(tag: string, vNode: VNode) {
|
export function submitDomUpdate(tag: string, vNode: VNode) {
|
||||||
const newProps = vNode.props;
|
const newProps = vNode.props;
|
||||||
const element: Element = vNode.realNode;
|
const element: Element | null = vNode.realNode;
|
||||||
|
|
||||||
if (tag === DomComponent) {
|
if (tag === DomComponent) {
|
||||||
// DomComponent类型
|
// DomComponent类型
|
||||||
|
@ -179,8 +173,10 @@ export function submitDomUpdate(tag: string, vNode: VNode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (tag === DomText) {
|
} else if (tag === DomText) {
|
||||||
// text类型
|
if (element != null) {
|
||||||
element.textContent = newProps;
|
// text类型
|
||||||
|
element.textContent = newProps;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,37 +8,7 @@ import {
|
||||||
} from '../../event/EventBinding';
|
} from '../../event/EventBinding';
|
||||||
import { isEventProp, isNativeElement } from '../validators/ValidateProps';
|
import { isEventProp, isNativeElement } from '../validators/ValidateProps';
|
||||||
|
|
||||||
// 初始化DOM属性
|
function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
|
||||||
export function setDomProps(
|
|
||||||
tagName: string,
|
|
||||||
dom: Element,
|
|
||||||
props: Object,
|
|
||||||
): void {
|
|
||||||
const isNativeTag = isNativeElement(tagName, props);
|
|
||||||
const keysOfProps = Object.keys(props);
|
|
||||||
|
|
||||||
for (let i = 0; i < keysOfProps.length; i++) {
|
|
||||||
const propName = keysOfProps[i];
|
|
||||||
const propVal = props[propName];
|
|
||||||
|
|
||||||
updateOneProp(dom, propName, propVal, isNativeTag, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新 DOM 属性
|
|
||||||
export function updateDomProps(
|
|
||||||
dom: Element,
|
|
||||||
changeList: Array<any>,
|
|
||||||
isNativeTag: boolean,
|
|
||||||
): void {
|
|
||||||
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') {
|
if (propName === 'style') {
|
||||||
setStyles(dom, propVal);
|
setStyles(dom, propVal);
|
||||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||||
|
@ -59,6 +29,36 @@ function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化DOM属性
|
||||||
|
export function setDomProps(
|
||||||
|
tagName: string,
|
||||||
|
dom: Element,
|
||||||
|
props: Object,
|
||||||
|
): void {
|
||||||
|
const isNativeTag = isNativeElement(tagName, props);
|
||||||
|
const keysOfProps = Object.keys(props);
|
||||||
|
|
||||||
|
for (let i = 0; i < keysOfProps.length; i++) {
|
||||||
|
const propName = keysOfProps[i];
|
||||||
|
const propVal = props[propName];
|
||||||
|
|
||||||
|
updateOneProp(dom, propName, isNativeTag, propVal, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DOM 属性
|
||||||
|
export function updateDomProps(
|
||||||
|
dom: Element,
|
||||||
|
changeList: Array<any>,
|
||||||
|
isNativeTag: boolean,
|
||||||
|
): void {
|
||||||
|
for (let i = 0; i < changeList.length; i++) {
|
||||||
|
const { propName, propVal } = changeList[i];
|
||||||
|
|
||||||
|
updateOneProp(dom, propName, isNativeTag, propVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 找出两个 DOM 属性的差别,生成需要更新的属性集合
|
// 找出两个 DOM 属性的差别,生成需要更新的属性集合
|
||||||
export function compareProps(
|
export function compareProps(
|
||||||
oldProps: Object,
|
oldProps: Object,
|
||||||
|
@ -111,7 +111,7 @@ export function compareProps(
|
||||||
for (let i = 0; i < keysOfNewProps.length; i++) {
|
for (let i = 0; i < keysOfNewProps.length; i++) {
|
||||||
const propName = keysOfNewProps[i];
|
const propName = keysOfNewProps[i];
|
||||||
const newPropValue = newProps[propName];
|
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)) {
|
if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) {
|
||||||
// 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理
|
// 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理
|
||||||
|
|
|
@ -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 属性
|
* 设置 DOM 节点的 style 属性
|
||||||
*/
|
*/
|
||||||
|
@ -21,31 +49,3 @@ export function setStyles(dom, styles) {
|
||||||
*/
|
*/
|
||||||
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp',
|
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp',
|
||||||
'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom'];
|
'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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,9 +14,15 @@ const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'ba
|
||||||
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,',
|
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,',
|
||||||
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
|
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
|
||||||
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
|
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
|
||||||
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'surfaceScale',
|
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation',
|
||||||
'systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget', 'xChannelSelector',
|
'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY',
|
||||||
'yChannelSelector', 'zoomAndPan']);
|
'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']);
|
||||||
|
|
||||||
|
// 驼峰 变 “-”
|
||||||
|
function convertToLowerCase(str) {
|
||||||
|
const replacer = (match, char) => `-${char.toLowerCase()}`;
|
||||||
|
return str.replace(/([A-Z])/g, replacer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给 dom 设置属性
|
* 给 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);
|
|
||||||
};
|
|
||||||
|
|
|
@ -105,16 +105,16 @@ export function getSelectionInfo() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface selectionData {
|
export interface SelectionData {
|
||||||
focusedDom: HTMLInputElement | HTMLTextAreaElement | void;
|
focusedDom: HTMLInputElement | HTMLTextAreaElement | void;
|
||||||
selectionRange: {
|
selectionRange?: {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
// 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失
|
// 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失
|
||||||
export function resetSelectionRange(preSelectionRangeData: selectionData) {
|
export function resetSelectionRange(preSelectionRangeData: SelectionData) {
|
||||||
// 当前 focus 的元素
|
// 当前 focus 的元素
|
||||||
const currentFocusedDom = getIFrameFocusedDom();
|
const currentFocusedDom = getIFrameFocusedDom();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
export interface IProperty {
|
export interface IProperty {
|
||||||
[propName: string]: any
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HorizonSelect extends HTMLSelectElement {
|
export interface HorizonSelect extends HTMLSelectElement {
|
||||||
_multiple: boolean;
|
_multiple?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HorizonDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
export type HorizonDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
||||||
|
|
|
@ -6,19 +6,15 @@ export enum PROPERTY_TYPE {
|
||||||
STRING, // 普通的字符串类型
|
STRING, // 普通的字符串类型
|
||||||
SPECIAL, // 需要特殊处理的属性类型
|
SPECIAL, // 需要特殊处理的属性类型
|
||||||
BOOLEAN_STR, // 字符串类型的 true false
|
BOOLEAN_STR, // 字符串类型的 true false
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PropDetails = {
|
export type PropDetails = {
|
||||||
propName: string,
|
propName: string;
|
||||||
type: PROPERTY_TYPE,
|
type: PROPERTY_TYPE;
|
||||||
attrName: string,
|
attrName: string;
|
||||||
attrNS: string | null,
|
attrNS: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getPropDetails(name: string): PropDetails | null {
|
|
||||||
return propsDetailData[name] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 属性相关数据
|
// 属性相关数据
|
||||||
// 依次为 propertyName、type、attributeName、attributeNamespace,不填则使用默认值
|
// 依次为 propertyName、type、attributeName、attributeNamespace,不填则使用默认值
|
||||||
// type 默认 STRING
|
// type 默认 STRING
|
||||||
|
@ -123,3 +119,7 @@ propertiesData.forEach(record => {
|
||||||
attrNS,
|
attrNS,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function getPropDetails(name: string): PropDetails | null {
|
||||||
|
return propsDetailData[name] || null;
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,11 @@ function isInvalidBoolean(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是事件属性
|
||||||
|
export function isEventProp(propName) {
|
||||||
|
return propName.substr(0, 2) === 'on';
|
||||||
|
}
|
||||||
|
|
||||||
function isValidProp(tagName, name, value) {
|
function isValidProp(tagName, name, value) {
|
||||||
// 校验事件名称
|
// 校验事件名称
|
||||||
if (isEventProp(name)) {
|
if (isEventProp(name)) {
|
||||||
|
@ -79,11 +84,6 @@ export function isInvalidValue(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是事件属性
|
|
||||||
export function isEventProp(propName) {
|
|
||||||
return propName.substr(0, 2) === 'on';
|
|
||||||
}
|
|
||||||
|
|
||||||
// dev模式下校验属性是否合法
|
// dev模式下校验属性是否合法
|
||||||
export function validateProps(type, props) {
|
export function validateProps(type, props) {
|
||||||
if (!props) {
|
if (!props) {
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
import {HorizonSelect, IProperty} from '../utils/Interface';
|
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) {
|
function updateMultipleValue(options, newValues) {
|
||||||
const newValueSet = new Set();
|
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) {
|
export function getSelectPropsWithoutValue(dom: HorizonSelect, properties: Object) {
|
||||||
return {
|
return {
|
||||||
...properties,
|
...properties,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type {AnyNativeEvent, ProcessingListenerList} from './types';
|
import type { AnyNativeEvent, ProcessingListenerList } from './types';
|
||||||
import type {VNode} from '../renderer/Types';
|
import type { VNode } from '../renderer/Types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CommonEventToHorizonMap,
|
CommonEventToHorizonMap,
|
||||||
|
@ -11,20 +11,20 @@ import {
|
||||||
throwCaughtEventError,
|
throwCaughtEventError,
|
||||||
runListenerAndCatchFirstError,
|
runListenerAndCatchFirstError,
|
||||||
} from './EventError';
|
} from './EventError';
|
||||||
import {getListeners as getBeforeInputListeners} from './simulatedEvtHandler/BeforeInputEventHandler';
|
import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler';
|
||||||
import {getListeners as getCompositionListeners} from './simulatedEvtHandler/CompositionEventHandler';
|
import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler';
|
||||||
import {getListeners as getChangeListeners} from './simulatedEvtHandler/ChangeEventHandler';
|
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
|
||||||
import {getListeners as getSelectionListeners} from './simulatedEvtHandler/SelectionEventHandler';
|
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
|
||||||
import {
|
import {
|
||||||
getCustomEventNameWithOn,
|
getCustomEventNameWithOn,
|
||||||
uniqueCharCode,
|
uniqueCharCode,
|
||||||
getEventTarget
|
getEventTarget
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import {createCommonCustomEvent} from './customEvents/EventFactory';
|
import { createCommonCustomEvent } from './customEvents/EventFactory';
|
||||||
import {getListenersFromTree} from './ListenerGetter';
|
import { getListenersFromTree } from './ListenerGetter';
|
||||||
import {shouldUpdateValue, updateControlledValue} from './ControlledValueUpdater';
|
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
|
||||||
import {asyncUpdates, runDiscreteUpdates} from '../renderer/Renderer';
|
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
|
||||||
import {getExactNode} from '../renderer/vnode/VNodeUtils';
|
import { getExactNode } from '../renderer/vnode/VNodeUtils';
|
||||||
|
|
||||||
// 获取事件触发的普通事件监听方法队列
|
// 获取事件触发的普通事件监听方法队列
|
||||||
function getCommonListeners(
|
function getCommonListeners(
|
||||||
|
@ -61,7 +61,7 @@ function getCommonListeners(
|
||||||
vNode,
|
vNode,
|
||||||
customEventName,
|
customEventName,
|
||||||
customEvent,
|
customEvent,
|
||||||
isCapture ? EVENT_TYPE_CAPTURE: EVENT_TYPE_BUBBLE,
|
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ export function processListeners(
|
||||||
processingEventsList.forEach(eventUnitList => {
|
processingEventsList.forEach(eventUnitList => {
|
||||||
let lastVNode;
|
let lastVNode;
|
||||||
eventUnitList.forEach(eventUnit => {
|
eventUnitList.forEach(eventUnit => {
|
||||||
const {vNode, currentTarget, listener, event} = eventUnit;
|
const { vNode, currentTarget, listener, event } = eventUnit;
|
||||||
if (vNode !== lastVNode && event.isPropagationStopped()) {
|
if (vNode !== lastVNode && event.isPropagationStopped()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,9 @@ function getProcessListenersFacade(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nativeEvtName === 'compositionend' || nativeEvtName === 'compositionstart' || nativeEvtName === 'compositionupdate') {
|
if (nativeEvtName === 'compositionend' ||
|
||||||
|
nativeEvtName === 'compositionstart' ||
|
||||||
|
nativeEvtName === 'compositionupdate') {
|
||||||
processingListenerList = processingListenerList.concat(getCompositionListeners(
|
processingListenerList = processingListenerList.concat(getCompositionListeners(
|
||||||
nativeEvtName,
|
nativeEvtName,
|
||||||
nativeEvent,
|
nativeEvent,
|
||||||
|
@ -151,7 +153,12 @@ function triggerHorizonEvents(
|
||||||
vNode: null | VNode,
|
vNode: null | VNode,
|
||||||
): void {
|
): void {
|
||||||
const nativeEventTarget = getEventTarget(nativeEvent);
|
const nativeEventTarget = getEventTarget(nativeEvent);
|
||||||
const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
const processingListenerList = getProcessListenersFacade(
|
||||||
|
nativeEvtName,
|
||||||
|
vNode,
|
||||||
|
nativeEvent,
|
||||||
|
nativeEventTarget,
|
||||||
|
isCapture);
|
||||||
|
|
||||||
// 处理触发的事件队列
|
// 处理触发的事件队列
|
||||||
processListeners(processingListenerList);
|
processListeners(processingListenerList);
|
||||||
|
@ -178,13 +185,14 @@ export function handleEventMain(
|
||||||
|
|
||||||
// 有事件正在执行,同步执行事件
|
// 有事件正在执行,同步执行事件
|
||||||
if (isInEventsExecution) {
|
if (isInEventsExecution) {
|
||||||
return triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
|
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 没有事件在执行,经过调度再执行事件
|
// 没有事件在执行,经过调度再执行事件
|
||||||
isInEventsExecution = true;
|
isInEventsExecution = true;
|
||||||
try {
|
try {
|
||||||
return asyncUpdates(() =>
|
asyncUpdates(() =>
|
||||||
triggerHorizonEvents(
|
triggerHorizonEvents(
|
||||||
nativeEvtName,
|
nativeEvtName,
|
||||||
isCapture,
|
isCapture,
|
||||||
|
|
|
@ -5,10 +5,10 @@ import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
|
||||||
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
|
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
|
||||||
|
|
||||||
export type ListenerUnit = {
|
export type ListenerUnit = {
|
||||||
vNode: null | VNode,
|
vNode: null | VNode;
|
||||||
listener: Function,
|
listener: Function;
|
||||||
currentTarget: EventTarget,
|
currentTarget: EventTarget;
|
||||||
event: CustomBaseEvent,
|
event: CustomBaseEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListenerUnitList = Array<ListenerUnit>;
|
export type ListenerUnitList = Array<ListenerUnit>;
|
||||||
|
|
|
@ -5,15 +5,6 @@ import {handleEventMain} from './HorizonEventMain';
|
||||||
import {runDiscreteUpdates} from '../renderer/Renderer';
|
import {runDiscreteUpdates} from '../renderer/Renderer';
|
||||||
import {getEventTarget} from './utils';
|
import {getEventTarget} from './utils';
|
||||||
|
|
||||||
// 生成委托事件的监听方法
|
|
||||||
export function createCustomEventListener(
|
|
||||||
target: EventTarget,
|
|
||||||
nativeEvtName: string,
|
|
||||||
isCapture: boolean,
|
|
||||||
): EventListener {
|
|
||||||
return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发委托事件
|
// 触发委托事件
|
||||||
function triggerDelegatedEvent(
|
function triggerDelegatedEvent(
|
||||||
nativeEvtName: string,
|
nativeEvtName: string,
|
||||||
|
@ -39,3 +30,12 @@ function triggerDelegatedEvent(
|
||||||
}
|
}
|
||||||
handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom);
|
handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成委托事件的监听方法
|
||||||
|
export function createCustomEventListener(
|
||||||
|
target: EventTarget,
|
||||||
|
nativeEvtName: string,
|
||||||
|
isCapture: boolean,
|
||||||
|
): EventListener {
|
||||||
|
return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target);
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ export const horizonEventToNativeMap = new Map([
|
||||||
['onCompositionUpdate', ['compositionupdate']],
|
['onCompositionUpdate', ['compositionupdate']],
|
||||||
['onBeforeInput', ['compositionend', 'keypress', 'textInput']],
|
['onBeforeInput', ['compositionend', 'keypress', 'textInput']],
|
||||||
['onChange', ['change', 'click', 'focusout', 'input',]],
|
['onChange', ['change', 'click', 'focusout', 'input',]],
|
||||||
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
|
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
|
||||||
|
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
|
||||||
|
|
||||||
['onAnimationEnd', [STYLE_AMT_END]],
|
['onAnimationEnd', [STYLE_AMT_END]],
|
||||||
['onAnimationIteration', [STYLE_AMT_ITERATION]],
|
['onAnimationIteration', [STYLE_AMT_ITERATION]],
|
||||||
|
|
|
@ -4,6 +4,48 @@
|
||||||
|
|
||||||
import {VNode} from '../../renderer/Types';
|
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',
|
||||||
|
];
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
export class CustomBaseEvent {
|
export class CustomBaseEvent {
|
||||||
|
|
||||||
data: string;
|
data: string;
|
||||||
|
@ -36,7 +78,9 @@ export class CustomBaseEvent {
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.timeStamp = nativeEvt.timeStamp || Date.now();
|
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.defaultPrevented = defaultPrevented;
|
||||||
|
|
||||||
this.preventDefault = this.preventDefault.bind(this);
|
this.preventDefault = this.preventDefault.bind(this);
|
||||||
|
@ -77,43 +121,3 @@ export class CustomBaseEvent {
|
||||||
this.isPropagationStopped = () => true;
|
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE);
|
||||||
function getInputCharsByNative(
|
function getInputCharsByNative(
|
||||||
eventName: string,
|
eventName: string,
|
||||||
nativeEvent: any,
|
nativeEvent: any,
|
||||||
): string | void {
|
): string | null {
|
||||||
if (eventName === 'compositionend') {
|
if (eventName === 'compositionend') {
|
||||||
return (nativeEvent.detail && nativeEvent.detail.data) || null;
|
return (nativeEvent.detail && nativeEvent.detail.data) || null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const horizonEventName = 'onSelect'
|
||||||
|
|
||||||
let currentElement = null;
|
let currentElement = null;
|
||||||
let currentVNode = null;
|
let currentVNode = null;
|
||||||
let lastSelection = null;
|
let lastSelection: Selection | null = null;
|
||||||
|
|
||||||
function initTargetCache(dom, vNode) {
|
function initTargetCache(dom, vNode) {
|
||||||
if (isTextInputElement(dom) || dom.contentEditable === 'true') {
|
if (isTextInputElement(dom) || dom.contentEditable === 'true') {
|
||||||
|
@ -85,7 +85,7 @@ export function getListeners(
|
||||||
target: null | EventTarget,
|
target: null | EventTarget,
|
||||||
): ProcessingListenerList {
|
): ProcessingListenerList {
|
||||||
const targetNode = vNode ? getDom(vNode) : window;
|
const targetNode = vNode ? getDom(vNode) : window;
|
||||||
let eventUnitList = [];
|
let eventUnitList: ProcessingListenerList = [];
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'focusin':
|
case 'focusin':
|
||||||
initTargetCache(targetNode, vNode);
|
initTargetCache(targetNode, vNode);
|
||||||
|
|
|
@ -12,6 +12,44 @@ function getItemKey(item: any, index: number): string {
|
||||||
return '.' + index.toString(36);
|
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(
|
function processArrayChildren(
|
||||||
children: any,
|
children: any,
|
||||||
arr: Array<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
|
// 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg
|
||||||
function mapChildren(
|
function mapChildren(
|
||||||
children: any,
|
children: any,
|
||||||
|
|
|
@ -41,7 +41,7 @@ function buildElement(isClone, type, setting, ...children) {
|
||||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
||||||
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
||||||
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
||||||
const props = isClone ? {...type.props} : {};
|
const props = isClone ? { ...type.props } : {};
|
||||||
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
|
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
|
||||||
|
|
||||||
if (setting != null) {
|
if (setting != null) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ let ctxOldPreviousContext: Object = {};
|
||||||
|
|
||||||
// capture阶段设置
|
// capture阶段设置
|
||||||
function setNamespaceCtx(vNode: VNode, dom?: Container) {
|
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);
|
vNode.setContext(CTX_NAMESPACE, ctxNamespace);
|
||||||
ctxNamespace = nextContext;
|
ctxNamespace = nextContext;
|
||||||
|
|
|
@ -14,6 +14,55 @@ import {handleSuspenseChildThrowError} from './render/SuspenseComponent';
|
||||||
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
|
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
|
||||||
import {BuildErrored, setBuildResult} from './GlobalVar';
|
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阶段抛出的错误
|
// 处理capture和bubble阶段抛出的错误
|
||||||
export function handleRenderThrowError(
|
export function handleRenderThrowError(
|
||||||
sourceVNode: VNode,
|
sourceVNode: VNode,
|
||||||
|
@ -77,6 +126,19 @@ export function handleRenderThrowError(
|
||||||
} while (vNode !== null);
|
} while (vNode !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增一个update,并且触发调度
|
||||||
|
function triggerUpdate(vNode, state) {
|
||||||
|
const update = newUpdate();
|
||||||
|
update.content = state;
|
||||||
|
pushUpdate(vNode, update);
|
||||||
|
|
||||||
|
const root = updateShouldUpdateOfTree(vNode);
|
||||||
|
if (root !== null) {
|
||||||
|
tryRenderRoot(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 处理submit阶段的异常
|
// 处理submit阶段的异常
|
||||||
export function handleSubmitError(vNode: VNode, error: any) {
|
export function handleSubmitError(vNode: VNode, error: any) {
|
||||||
if (vNode.tag === TreeRoot) {
|
if (vNode.tag === TreeRoot) {
|
||||||
|
@ -127,64 +189,3 @@ export function handleSubmitError(vNode: VNode, error: any) {
|
||||||
node = node.parent;
|
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) {
|
|
||||||
tryRenderFromRoot(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -129,6 +129,27 @@ function handleError(root, error): void {
|
||||||
bubbleVNode(processing);
|
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) {
|
export function calcStartUpdateVNode(treeRoot: VNode) {
|
||||||
const toUpdateNodes = Array.from(treeRoot.toUpdateNodes);
|
const toUpdateNodes = Array.from(treeRoot.toUpdateNodes);
|
||||||
|
@ -292,25 +313,10 @@ export function launchUpdateFromVNode(vNode: VNode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断数组中节点的path的idx元素是否都相等
|
export function setBuildResultError() {
|
||||||
function isEqualByIndex(idx: number, nodes: Array<VNode>) {
|
if (getBuildResult() !== BuildCompleted) {
|
||||||
let val = nodes[0].path[idx];
|
setBuildResult(BuildErrored);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== HorizonDOM使用 ==============================
|
// ============================== HorizonDOM使用 ==============================
|
||||||
|
|
|
@ -21,7 +21,6 @@ export type JSXElement = {
|
||||||
key: any;
|
key: any;
|
||||||
ref: any;
|
ref: any;
|
||||||
props: any;
|
props: any;
|
||||||
|
|
||||||
belongClassVNode: any;
|
belongClassVNode: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,8 @@ import type { VNode, JSXElement } from '../Types';
|
||||||
|
|
||||||
// 当前vNode和element是同样的类型
|
// 当前vNode和element是同样的类型
|
||||||
// LazyComponent 会修改type的类型,所以特殊处理这种类型
|
// LazyComponent 会修改type的类型,所以特殊处理这种类型
|
||||||
export const isSameType = (vNode: VNode, ele: JSXElement) => {
|
export const isSameType = (vNode: VNode, ele: JSXElement) =>
|
||||||
return vNode.type === ele.type || (vNode.isLazyComponent && vNode.lazyType === ele.type);
|
vNode.type === ele.type || (vNode.isLazyComponent && vNode.lazyType === ele.type);
|
||||||
};
|
|
||||||
|
|
||||||
export function isTextType(newChild: any) {
|
export function isTextType(newChild: any) {
|
||||||
return typeof newChild === 'string' || typeof newChild === 'number';
|
return typeof newChild === 'string' || typeof newChild === 'number';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { VNode } from '../Types';
|
import type { VNode } from '../Types';
|
||||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||||
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
|
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
|
||||||
import { DomText, DomPortal, Fragment } from '../vnode/VNodeTags';
|
import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags';
|
||||||
import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator';
|
import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator';
|
||||||
import {
|
import {
|
||||||
isSameType,
|
isSameType,
|
||||||
|
@ -17,7 +17,7 @@ enum DiffCategory {
|
||||||
TEXT_NODE = 'TEXT_NODE',
|
TEXT_NODE = 'TEXT_NODE',
|
||||||
OBJECT_NODE = 'OBJECT_NODE',
|
OBJECT_NODE = 'OBJECT_NODE',
|
||||||
ARR_NODE = 'ARR_NODE',
|
ARR_NODE = 'ARR_NODE',
|
||||||
};
|
}
|
||||||
|
|
||||||
// 检查是不是被 FRAGMENT 包裹
|
// 检查是不是被 FRAGMENT 包裹
|
||||||
function isNoKeyFragment(child: any) {
|
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;
|
let node = startDelVNode;
|
||||||
|
|
||||||
while (node !== null) {
|
while (node !== null) {
|
||||||
|
@ -67,7 +67,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodeType(newChild: any): string {
|
function getNodeType(newChild: any): string | null {
|
||||||
if (newChild === null) {
|
if (newChild === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -186,9 +186,7 @@ function transLeftChildrenToMap(
|
||||||
|
|
||||||
travelChildren(startChild, (node) => {
|
travelChildren(startChild, (node) => {
|
||||||
leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node);
|
leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node);
|
||||||
}, (node) => {
|
}, node => node === rightEndVNode);
|
||||||
return node === rightEndVNode;
|
|
||||||
});
|
|
||||||
|
|
||||||
return leftChildrenMap;
|
return leftChildrenMap;
|
||||||
}
|
}
|
||||||
|
@ -208,6 +206,11 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCIndex(vNode: VNode, idx: number) {
|
||||||
|
vNode.cIndex = idx;
|
||||||
|
updateVNodePath(vNode);
|
||||||
|
}
|
||||||
|
|
||||||
// diff数组类型的节点,核心算法
|
// diff数组类型的节点,核心算法
|
||||||
function diffArrayNodes(
|
function diffArrayNodes(
|
||||||
parentNode: VNode,
|
parentNode: VNode,
|
||||||
|
@ -277,7 +280,7 @@ function diffArrayNodes(
|
||||||
// 从后往前,新资源的位置还没有到最末端,旧的vNode也还没遍历完,则可以考虑从后往前开始
|
// 从后往前,新资源的位置还没有到最末端,旧的vNode也还没遍历完,则可以考虑从后往前开始
|
||||||
if (rightIdx > leftIdx && oldNode !== null) {
|
if (rightIdx > leftIdx && oldNode !== null) {
|
||||||
const rightRemainingOldChildren = transRightChildrenToArray(oldNode);
|
const rightRemainingOldChildren = transRightChildrenToArray(oldNode);
|
||||||
let rightOldIndex = rightRemainingOldChildren.length - 1;
|
let rightOldIndex: number | null = rightRemainingOldChildren.length - 1;
|
||||||
|
|
||||||
// 2. 从右侧开始比对currentVNode和newChildren,若不能复用则跳出循环
|
// 2. 从右侧开始比对currentVNode和newChildren,若不能复用则跳出循环
|
||||||
for (; rightIdx > leftIdx; rightIdx--) {
|
for (; rightIdx > leftIdx; rightIdx--) {
|
||||||
|
@ -320,7 +323,12 @@ function diffArrayNodes(
|
||||||
// 3. 新节点已经处理完成
|
// 3. 新节点已经处理完成
|
||||||
if (leftIdx === rightIdx) {
|
if (leftIdx === rightIdx) {
|
||||||
if (isComparing) {
|
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) {
|
if (rightNewNode) {
|
||||||
|
@ -372,7 +380,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 = eIndexes[result[result.length - 1]];
|
const last: number | undefined = 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);
|
||||||
|
@ -450,11 +458,6 @@ function setVNodesCIndex(startChild: VNode, startIdx: number) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCIndex(vNode: VNode, idx: number) {
|
|
||||||
vNode.cIndex = idx;
|
|
||||||
updateVNodePath(vNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新节点是数组类型
|
// 新节点是数组类型
|
||||||
function diffArrayNodesHandler(
|
function diffArrayNodesHandler(
|
||||||
parentNode: VNode,
|
parentNode: VNode,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type {VNode} from '../Types';
|
import type { VNode } from '../Types';
|
||||||
import type {Hook} from './HookType';
|
import type { Hook } from './HookType';
|
||||||
|
|
||||||
let processingVNode: VNode = null;
|
let processingVNode: VNode = null;
|
||||||
|
|
||||||
|
@ -56,7 +56,9 @@ export function getNextHook(hook: Hook<any, any>, hooks: Array<Hook<any, any>>)
|
||||||
// processing中的hook和上一次执行中的hook,需要同时往前走,
|
// processing中的hook和上一次执行中的hook,需要同时往前走,
|
||||||
// 原因:1.比对hook的数量有没有变化(非必要);2.从上一次执行中的hook获取removeEffect
|
// 原因:1.比对hook的数量有没有变化(非必要);2.从上一次执行中的hook获取removeEffect
|
||||||
export function getCurrentHook(): Hook<any, any> {
|
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) {
|
if (lastTimeHook !== null) {
|
||||||
lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks);
|
lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type {Hook, Reducer, Trigger, Update} from './HookType';
|
import type { Hook, Reducer, Trigger, Update } from './HookType';
|
||||||
import {
|
import {
|
||||||
createHook,
|
createHook,
|
||||||
getCurrentHook,
|
getCurrentHook,
|
||||||
|
@ -8,12 +8,13 @@ import {
|
||||||
import {
|
import {
|
||||||
launchUpdateFromVNode
|
launchUpdateFromVNode
|
||||||
} from '../TreeBuilder';
|
} from '../TreeBuilder';
|
||||||
import {isSame} from '../utils/compare';
|
import { isSame } from '../utils/compare';
|
||||||
import {setStateChange} from '../render/FunctionComponent';
|
import { setStateChange } from '../render/FunctionComponent';
|
||||||
import {getHookStage, HookStage} from './HookStage';
|
import { getHookStage, HookStage } from './HookStage';
|
||||||
import type {VNode} from '../Types';
|
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();
|
const stage = getHookStage();
|
||||||
if (stage === null) {
|
if (stage === null) {
|
||||||
throwNotInFuncError();
|
throwNotInFuncError();
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import type {VNode} from '../Types';
|
import type {VNode} from '../Types';
|
||||||
import {createVNodeChildren} from './BaseComponent';
|
import {createVNodeChildren} from './BaseComponent';
|
||||||
|
|
||||||
export function captureRender(processing: VNode): VNode | null {
|
|
||||||
return captureFragment(processing);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bubbleRender() {}
|
export function bubbleRender() {}
|
||||||
|
|
||||||
function captureFragment(processing: VNode) {
|
function captureFragment(processing: VNode) {
|
||||||
|
@ -12,3 +8,7 @@ function captureFragment(processing: VNode) {
|
||||||
processing.child = createVNodeChildren(processing, newElement);
|
processing.child = createVNodeChildren(processing, newElement);
|
||||||
return processing.child;
|
return processing.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function captureRender(processing: VNode): VNode | null {
|
||||||
|
return captureFragment(processing);
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,18 @@ import {
|
||||||
cacheOldCtx,
|
cacheOldCtx,
|
||||||
} from '../components/context/CompatibleContext';
|
} 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 Component = processing.type;
|
||||||
const unresolvedProps = processing.props;
|
const unresolvedProps = processing.props;
|
||||||
const resolvedProps =
|
const resolvedProps =
|
||||||
|
@ -28,14 +39,3 @@ export function bubbleRender(processing: VNode) {
|
||||||
resetOldCtx(processing);
|
resetOldCtx(processing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function captureIncompleteClassComponent(processing, Component, nextProps) {
|
|
||||||
processing.tag = ClassComponent;
|
|
||||||
|
|
||||||
const hasOldContext = isOldProvider(Component);
|
|
||||||
cacheOldCtx(processing, hasOldContext);
|
|
||||||
|
|
||||||
resetDepContexts(processing);
|
|
||||||
|
|
||||||
return getIncompleteClassComponent(Component, processing, nextProps);
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,10 +13,6 @@ import { captureFunctionComponent } from './FunctionComponent';
|
||||||
import { captureClassComponent } from './ClassComponent';
|
import { captureClassComponent } from './ClassComponent';
|
||||||
import { captureMemoComponent } from './MemoComponent';
|
import { captureMemoComponent } from './MemoComponent';
|
||||||
|
|
||||||
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
|
||||||
return captureLazyComponent(processing, processing.type, shouldUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bubbleRender() { }
|
export function bubbleRender() { }
|
||||||
|
|
||||||
const LazyRendererMap = {
|
const LazyRendererMap = {
|
||||||
|
@ -26,6 +22,20 @@ const LazyRendererMap = {
|
||||||
[MemoComponent]: captureMemoComponent,
|
[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(
|
function captureLazyComponent(
|
||||||
processing,
|
processing,
|
||||||
lazyComponent,
|
lazyComponent,
|
||||||
|
@ -43,7 +53,8 @@ function captureLazyComponent(
|
||||||
// 加载得到的Component存在type中
|
// 加载得到的Component存在type中
|
||||||
processing.type = Component;
|
processing.type = Component;
|
||||||
|
|
||||||
const lazyVNodeTag = processing.tag = getLazyVNodeTag(Component);
|
const lazyVNodeTag = getLazyVNodeTag(Component);
|
||||||
|
processing.tag = lazyVNodeTag;
|
||||||
const lazyVNodeProps = mergeDefaultProps(Component, processing.props);
|
const lazyVNodeProps = mergeDefaultProps(Component, processing.props);
|
||||||
|
|
||||||
const lazyRender = LazyRendererMap[lazyVNodeTag];
|
const lazyRender = LazyRendererMap[lazyVNodeTag];
|
||||||
|
@ -68,16 +79,6 @@ function captureLazyComponent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeDefaultProps(Component: any, props: object): object {
|
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
||||||
if (Component && Component.defaultProps) {
|
return captureLazyComponent(processing, processing.type, shouldUpdate);
|
||||||
const clonedProps = { ...props };
|
|
||||||
const defaultProps = Component.defaultProps;
|
|
||||||
Object.keys(defaultProps).forEach(key => {
|
|
||||||
if (clonedProps[key] === undefined) {
|
|
||||||
clonedProps[key] = defaultProps[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return clonedProps;
|
|
||||||
}
|
|
||||||
return props;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,6 @@ import {
|
||||||
} from '../../external/JSXElementType';
|
} from '../../external/JSXElementType';
|
||||||
import {Fragment} from '../vnode/VNodeTags';
|
import {Fragment} from '../vnode/VNodeTags';
|
||||||
|
|
||||||
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
|
||||||
return captureMemoComponent(processing, shouldUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bubbleRender() {}
|
export function bubbleRender() {}
|
||||||
|
|
||||||
export function captureMemoComponent(
|
export function captureMemoComponent(
|
||||||
|
@ -59,3 +55,7 @@ export function captureMemoComponent(
|
||||||
|
|
||||||
return newChild;
|
return newChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
||||||
|
return captureMemoComponent(processing, shouldUpdate);
|
||||||
|
}
|
||||||
|
|
|
@ -19,73 +19,36 @@ export enum SuspenseChildStatus {
|
||||||
ShowFallback = 'showFallback',
|
ShowFallback = 'showFallback',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null {
|
// 创建fallback子节点
|
||||||
if (
|
function createFallback(processing: VNode, fallbackChildren) {
|
||||||
!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 {
|
|
||||||
const childFragment: VNode = processing.child;
|
const childFragment: VNode = processing.child;
|
||||||
|
let fallbackFragment;
|
||||||
|
childFragment.childShouldUpdate = false;
|
||||||
|
|
||||||
if (childFragment.childShouldUpdate) {
|
if (!processing.isCreated) {
|
||||||
if (processing.promiseResolve) {
|
const oldFallbackFragment: VNode | null = processing.oldChild ? processing.oldChild.next : null;
|
||||||
// promise已完成,展示promise返回的新节点
|
|
||||||
return captureSuspenseComponent(processing);
|
if (oldFallbackFragment !== null) {
|
||||||
|
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
|
||||||
} else {
|
} else {
|
||||||
// promise未完成,继续显示fallback,不需要继续刷新子节点
|
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||||
const fallbackFragment: VNode = processing.child.next;
|
FlagUtils.markAddition(fallbackFragment);
|
||||||
childFragment.childShouldUpdate = false;
|
|
||||||
fallbackFragment.childShouldUpdate = false;
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const children = onlyUpdateChildVNodes(processing);
|
// 创建
|
||||||
|
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
return fallbackFragment;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建子节点
|
// 创建子节点
|
||||||
|
@ -120,36 +83,77 @@ function createSuspenseChildren(processing: VNode, newChildren) {
|
||||||
return processing.child;
|
return processing.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建fallback子节点
|
export function captureSuspenseComponent(processing: VNode) {
|
||||||
function createFallback(processing: VNode, fallbackChildren) {
|
const nextProps = processing.props;
|
||||||
const childFragment: VNode = processing.child;
|
|
||||||
let fallbackFragment;
|
|
||||||
childFragment.childShouldUpdate = false;
|
|
||||||
|
|
||||||
if (!processing.isCreated) {
|
// suspense被捕获后需要展示fallback
|
||||||
const oldFallbackFragment: VNode | null = processing.oldChild ? processing.oldChild.next : null;
|
const showFallback = processing.suspenseDidCapture;
|
||||||
|
|
||||||
if (oldFallbackFragment !== null) {
|
if (showFallback) {
|
||||||
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
|
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 {
|
} else {
|
||||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
// promise未完成,继续显示fallback,不需要继续刷新子节点
|
||||||
FlagUtils.markAddition(fallbackFragment);
|
const fallbackFragment: VNode = processing.child.next;
|
||||||
|
childFragment.childShouldUpdate = false;
|
||||||
|
fallbackFragment.childShouldUpdate = false;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 创建
|
const children = onlyUpdateChildVNodes(processing);
|
||||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
|
||||||
|
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;
|
return captureSuspenseComponent(processing);
|
||||||
childFragment.next = fallbackFragment;
|
}
|
||||||
childFragment.parent = processing;
|
|
||||||
fallbackFragment.parent = processing;
|
|
||||||
fallbackFragment.eIndex = 1;
|
|
||||||
fallbackFragment.cIndex = 1;
|
|
||||||
updateVNodePath(fallbackFragment);
|
|
||||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback;
|
|
||||||
|
|
||||||
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
|
// 处理Suspense子组件抛出的promise
|
||||||
|
@ -196,36 +200,8 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function canCapturePromise(vNode: VNode): boolean {
|
|
||||||
return vNode.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode.props.fallback !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
||||||
|
|
||||||
// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染
|
|
||||||
export function listenToPromise(suspenseVNode: VNode) {
|
|
||||||
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises;
|
|
||||||
if (promises !== null) {
|
|
||||||
suspenseVNode.suspensePromises = null;
|
|
||||||
|
|
||||||
// 记录已经监听的 promise
|
|
||||||
let promiseCache = suspenseVNode.realNode;
|
|
||||||
if (promiseCache === null) {
|
|
||||||
// @ts-ignore
|
|
||||||
promiseCache = suspenseVNode.realNode = new PossiblyWeakSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
promises.forEach(promise => {
|
|
||||||
const resole = resolvePromise.bind(null, suspenseVNode, promise);
|
|
||||||
if (!promiseCache.has(promise)) {
|
|
||||||
promiseCache.add(promise);
|
|
||||||
// 监听promise
|
|
||||||
promise.then(resole, resole);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
||||||
const promiseCache = suspenseVNode.realNode;
|
const promiseCache = suspenseVNode.realNode;
|
||||||
if (promiseCache !== null) {
|
if (promiseCache !== null) {
|
||||||
|
@ -238,4 +214,27 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染
|
||||||
|
export function listenToPromise(suspenseVNode: VNode) {
|
||||||
|
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises;
|
||||||
|
if (promises !== null) {
|
||||||
|
suspenseVNode.suspensePromises = null;
|
||||||
|
|
||||||
|
// 记录已经监听的 promise
|
||||||
|
let promiseCache = suspenseVNode.realNode;
|
||||||
|
if (promiseCache === null) {
|
||||||
|
// @ts-ignore
|
||||||
|
promiseCache = new PossiblyWeakSet();
|
||||||
|
suspenseVNode.realNode = new PossiblyWeakSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.forEach(promise => {
|
||||||
|
const resole = resolvePromise.bind(null, suspenseVNode, promise);
|
||||||
|
if (!promiseCache.has(promise)) {
|
||||||
|
promiseCache.add(promise);
|
||||||
|
// 监听promise
|
||||||
|
promise.then(resole, resole);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,10 +6,6 @@ import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver';
|
||||||
import {resetOldCtx} from '../components/context/CompatibleContext';
|
import {resetOldCtx} from '../components/context/CompatibleContext';
|
||||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||||
|
|
||||||
export function captureRender(processing: VNode): VNode | null {
|
|
||||||
return updateTreeRoot(processing);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bubbleRender(processing: VNode) {
|
export function bubbleRender(processing: VNode) {
|
||||||
resetNamespaceCtx(processing);
|
resetNamespaceCtx(processing);
|
||||||
resetOldCtx(processing);
|
resetOldCtx(processing);
|
||||||
|
@ -40,3 +36,7 @@ function updateTreeRoot(processing) {
|
||||||
processing.child = createVNodeChildren(processing, newElement);
|
processing.child = createVNodeChildren(processing, newElement);
|
||||||
return processing.child;
|
return processing.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function captureRender(processing: VNode): VNode | null {
|
||||||
|
return updateTreeRoot(processing);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { VNode } from '../../Types';
|
import type { VNode } from '../../Types';
|
||||||
import type { Callback } from '../../UpdateHandler';
|
import type { Callback } from '../../UpdateHandler';
|
||||||
|
|
||||||
import {shallowCompare} from '../../utils/compare';
|
import { shallowCompare } from '../../utils/compare';
|
||||||
import {
|
import {
|
||||||
pushUpdate,
|
pushUpdate,
|
||||||
newUpdate,
|
newUpdate,
|
||||||
|
@ -40,7 +40,7 @@ function changeStateContent(type: UpdateState, content: object, callback: Callba
|
||||||
if (type === UpdateState.Update || type === UpdateState.Override) {
|
if (type === UpdateState.Update || type === UpdateState.Override) {
|
||||||
update.content = content;
|
update.content = content;
|
||||||
}
|
}
|
||||||
if (callback !== undefined && callback !== null) {
|
if (callback) {
|
||||||
update.callback = callback;
|
update.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
* 该文件负责把更新应用到界面上 以及 和生命周期的相关调用
|
* 该文件负责把更新应用到界面上 以及 和生命周期的相关调用
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Container} from '../../dom/DOMOperator';
|
import type { Container } from '../../dom/DOMOperator';
|
||||||
import type {RefType, VNode} from '../Types';
|
import type { RefType, VNode } from '../Types';
|
||||||
|
|
||||||
import {listenToPromise, SuspenseChildStatus} from '../render/SuspenseComponent';
|
import { listenToPromise, SuspenseChildStatus } from '../render/SuspenseComponent';
|
||||||
import {
|
import {
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
ForwardRef,
|
ForwardRef,
|
||||||
|
@ -17,8 +17,8 @@ import {
|
||||||
SuspenseComponent,
|
SuspenseComponent,
|
||||||
MemoComponent,
|
MemoComponent,
|
||||||
} from '../vnode/VNodeTags';
|
} from '../vnode/VNodeTags';
|
||||||
import {FlagUtils, ResetText} from '../vnode/VNodeFlags';
|
import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags';
|
||||||
import {mergeDefaultProps} from '../render/LazyComponent';
|
import { mergeDefaultProps } from '../render/LazyComponent';
|
||||||
import {
|
import {
|
||||||
submitDomUpdate,
|
submitDomUpdate,
|
||||||
clearText,
|
clearText,
|
||||||
|
@ -29,15 +29,20 @@ import {
|
||||||
unHideDom,
|
unHideDom,
|
||||||
clearContainer,
|
clearContainer,
|
||||||
} from '../../dom/DOMOperator';
|
} from '../../dom/DOMOperator';
|
||||||
import {callEffectRemove, callUseEffects, callUseLayoutEffectCreate, callUseLayoutEffectRemove} from './HookEffectHandler';
|
import {
|
||||||
import {handleSubmitError} from '../ErrorHandler';
|
callEffectRemove,
|
||||||
|
callUseEffects,
|
||||||
|
callUseLayoutEffectCreate,
|
||||||
|
callUseLayoutEffectRemove
|
||||||
|
} from './HookEffectHandler';
|
||||||
|
import { handleSubmitError } from '../ErrorHandler';
|
||||||
import {
|
import {
|
||||||
travelVNodeTree,
|
travelVNodeTree,
|
||||||
clearVNode,
|
clearVNode,
|
||||||
isDomVNode,
|
isDomVNode,
|
||||||
findDomParent, getSiblingDom,
|
findDomParent, getSiblingDom,
|
||||||
} from '../vnode/VNodeUtils';
|
} from '../vnode/VNodeUtils';
|
||||||
import {shouldAutoFocus} from '../../dom/utils/Common';
|
import { shouldAutoFocus } from '../../dom/utils/Common';
|
||||||
|
|
||||||
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
||||||
try {
|
try {
|
||||||
|
@ -220,14 +225,14 @@ function unmountVNode(vNode: VNode): void {
|
||||||
function unmountNestedVNodes(vNode: VNode): void {
|
function unmountNestedVNodes(vNode: VNode): void {
|
||||||
travelVNodeTree(vNode, (node) => {
|
travelVNodeTree(vNode, (node) => {
|
||||||
unmountVNode(node);
|
unmountVNode(node);
|
||||||
}, (node) => {
|
}, node =>
|
||||||
// 如果是DomPortal,不需要遍历child
|
// 如果是DomPortal,不需要遍历child
|
||||||
return node.tag === DomPortal;
|
node.tag === DomPortal
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (parent.flags.ResetText) {
|
||||||
// 在insert之前先reset
|
// 在insert之前先reset
|
||||||
|
@ -244,7 +249,7 @@ function insertOrAppendPlacementNode(
|
||||||
beforeDom: Element | null,
|
beforeDom: Element | null,
|
||||||
parent: Element | Container,
|
parent: Element | Container,
|
||||||
): void {
|
): void {
|
||||||
const {tag, realNode} = node;
|
const { tag, realNode } = node;
|
||||||
|
|
||||||
if (isDomVNode(node)) {
|
if (isDomVNode(node)) {
|
||||||
if (beforeDom) {
|
if (beforeDom) {
|
||||||
|
@ -291,10 +296,9 @@ function unmountDomComponents(vNode: VNode): void {
|
||||||
} else {
|
} else {
|
||||||
unmountVNode(node);
|
unmountVNode(node);
|
||||||
}
|
}
|
||||||
}, (node) => {
|
}, node =>
|
||||||
// 如果是dom不用再遍历child
|
// 如果是dom不用再遍历child
|
||||||
return node.tag === DomComponent || node.tag === DomText;
|
node.tag === DomComponent || node.tag === DomText, null, (node) => {
|
||||||
}, null, (node) => {
|
|
||||||
if (node.tag === DomPortal) {
|
if (node.tag === DomPortal) {
|
||||||
// 当离开portal,需要重新设置parent
|
// 当离开portal,需要重新设置parent
|
||||||
currentParentIsValid = false;
|
currentParentIsValid = false;
|
||||||
|
@ -302,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 {
|
function submitDeletion(vNode: VNode): void {
|
||||||
// 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount()
|
// 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount()
|
||||||
unmountDomComponents(vNode);
|
unmountDomComponents(vNode);
|
||||||
|
@ -348,6 +389,7 @@ export {
|
||||||
submitResetTextContent,
|
submitResetTextContent,
|
||||||
submitAddition,
|
submitAddition,
|
||||||
submitDeletion,
|
submitDeletion,
|
||||||
|
submitClear,
|
||||||
submitUpdate,
|
submitUpdate,
|
||||||
callAfterSubmitLifeCycles,
|
callAfterSubmitLifeCycles,
|
||||||
attachRef,
|
attachRef,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
attachRef,
|
attachRef,
|
||||||
callAfterSubmitLifeCycles,
|
callAfterSubmitLifeCycles,
|
||||||
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
|
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
|
||||||
submitResetTextContent, submitUpdate, detachRef,
|
submitResetTextContent, submitUpdate, detachRef, submitClear,
|
||||||
} from './LifeCycleHandler';
|
} from './LifeCycleHandler';
|
||||||
import {tryRenderFromRoot, setProcessing} from '../TreeBuilder';
|
import {tryRenderFromRoot, setProcessing} from '../TreeBuilder';
|
||||||
import {
|
import {
|
||||||
|
@ -121,7 +121,7 @@ function submit(dirtyNodes: Array<VNode>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {Addition, Update, Deletion} = node.flags;
|
const {Addition, Update, Deletion, Clear} = node.flags;
|
||||||
if (Addition && Update) {
|
if (Addition && Update) {
|
||||||
// Addition
|
// Addition
|
||||||
submitAddition(node);
|
submitAddition(node);
|
||||||
|
@ -138,6 +138,9 @@ function submit(dirtyNodes: Array<VNode>) {
|
||||||
} else if (Deletion) {
|
} else if (Deletion) {
|
||||||
submitDeletion(node);
|
submitDeletion(node);
|
||||||
}
|
}
|
||||||
|
if (Clear) {
|
||||||
|
submitClear(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throwIfTrue(node === null, 'Should be working on an effect.');
|
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
let isMessageLoopRunning = false;
|
let isMessageLoopRunning = false;
|
||||||
let browserCallback = null;
|
let browserCallback = null;
|
||||||
|
const { port1, port2 } = new MessageChannel();
|
||||||
|
|
||||||
|
|
||||||
export function isOverTime() {
|
export function isOverTime() {
|
||||||
|
@ -33,7 +34,7 @@ const callRenderTasks = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { port1, port2 } = new MessageChannel();
|
|
||||||
port1.onmessage = callRenderTasks;
|
port1.onmessage = callRenderTasks;
|
||||||
|
|
||||||
export function requestBrowserCallback(callback) {
|
export function requestBrowserCallback(callback) {
|
||||||
|
|
|
@ -12,20 +12,6 @@ let callingQueueTask: any | null = null;
|
||||||
// 防止重入
|
// 防止重入
|
||||||
let isCallingRenderQueue = false;
|
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() {
|
export function callRenderQueueImmediate() {
|
||||||
if (callingQueueTask !== null) {
|
if (callingQueueTask !== null) {
|
||||||
// 取消异步调度
|
// 取消异步调度
|
||||||
|
@ -56,3 +42,17 @@ function callRenderQueue() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pushRenderCallback(callback: RenderCallback) {
|
||||||
|
if (renderQueue === null) {
|
||||||
|
renderQueue = [callback];
|
||||||
|
// 高优先级的异步调度
|
||||||
|
callingQueueTask = runAsync(callRenderQueue, ImmediatePriority);
|
||||||
|
} else {
|
||||||
|
// 不需要调度,在syncQueue创建的时候已经调度了
|
||||||
|
renderQueue.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回一个空对象,用于区别null
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* 调度器的核心实现
|
* 调度器的核心实现
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Node } from '../taskExecutor/TaskQueue';
|
||||||
import {
|
import {
|
||||||
requestBrowserCallback,
|
requestBrowserCallback,
|
||||||
isOverTime,
|
isOverTime,
|
||||||
|
@ -21,39 +22,11 @@ let isProcessing = false;
|
||||||
// 调度中,等待浏览器回调
|
// 调度中,等待浏览器回调
|
||||||
let isWaiting = false;
|
let isWaiting = 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 callTasks() {
|
function callTasks() {
|
||||||
isWaiting = false;
|
isWaiting = false;
|
||||||
isProcessing = true;
|
isProcessing = true;
|
||||||
|
|
||||||
let task = null;
|
let task: Node | null= null;
|
||||||
try {
|
try {
|
||||||
task = first();
|
task = first();
|
||||||
|
|
||||||
|
@ -89,6 +62,34 @@ function callTasks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
function cancelTask(task) {
|
||||||
task.callback = null;
|
task.callback = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type Queue = Array<Node>;
|
type Queue = Array<Node>;
|
||||||
type Node = {
|
export type Node = {
|
||||||
id: number;
|
id: number;
|
||||||
order: number;
|
order: number;
|
||||||
};
|
};
|
||||||
|
@ -11,15 +11,10 @@ type Node = {
|
||||||
// 任务队列
|
// 任务队列
|
||||||
const taskQueue: Queue = [];
|
const taskQueue: Queue = [];
|
||||||
|
|
||||||
export function add(node: Node): void {
|
function compare(a: Node, b: Node) {
|
||||||
// 查找第一个大于等于 value 的下标,都比 value 小则返回 -1
|
// 优先先用index排序,其次用id
|
||||||
const idx = getBiggerIdx(node);
|
const diff = a.order - b.order;
|
||||||
|
return diff !== 0 ? diff : a.id - b.id;
|
||||||
if (idx === -1) {
|
|
||||||
taskQueue.push(node);
|
|
||||||
} else {
|
|
||||||
taskQueue.splice(idx, 0, node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 二分法查找第一个大于等于 value 的下标,都比 value 小则返回 -1,时间复杂度O(logn)
|
// 二分法查找第一个大于等于 value 的下标,都比 value 小则返回 -1,时间复杂度O(logn)
|
||||||
|
@ -37,11 +32,22 @@ function getBiggerIdx(node: Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
export function first(): Node | null {
|
||||||
const val = taskQueue[0];
|
const val: Node | null | undefined = taskQueue[0];
|
||||||
return val !== undefined ? val : null;
|
return val !== undefined ? val : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +59,3 @@ export function shift(): Node | null {
|
||||||
export function remove(node: Node) {
|
export function remove(node: Node) {
|
||||||
taskQueue.splice(taskQueue.indexOf(node), 1);
|
taskQueue.splice(taskQueue.indexOf(node), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function compare(a: Node, b: Node) {
|
|
||||||
// 优先先用index排序,其次用id
|
|
||||||
const diff = a.order - b.order;
|
|
||||||
return diff !== 0 ? diff : a.id - b.id;
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,9 +42,9 @@ export function shallowCompare(paramX: any, paramY: any): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return keysX.every((key, i) => {
|
return keysX.every((key, i) =>
|
||||||
return Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]]);
|
Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -49,19 +49,20 @@ export class VNode {
|
||||||
// 因为LazyComponent会修改type属性,为了在diff中判断是否可以复用,需要增加一个lazyType
|
// 因为LazyComponent会修改type属性,为了在diff中判断是否可以复用,需要增加一个lazyType
|
||||||
lazyType: any = null;
|
lazyType: any = null;
|
||||||
flags: {
|
flags: {
|
||||||
Addition?: boolean,
|
Addition?: boolean;
|
||||||
Update?: boolean,
|
Update?: boolean;
|
||||||
Deletion?: boolean,
|
Deletion?: boolean;
|
||||||
ResetText?: boolean,
|
ResetText?: boolean;
|
||||||
Callback?: boolean,
|
Callback?: boolean;
|
||||||
DidCapture?: boolean,
|
DidCapture?: boolean;
|
||||||
Ref?: boolean,
|
Ref?: boolean;
|
||||||
Snapshot?: boolean,
|
Snapshot?: boolean;
|
||||||
Interrupted?: boolean,
|
Interrupted?: boolean;
|
||||||
ShouldCapture?: boolean,
|
ShouldCapture?: boolean;
|
||||||
ForceUpdate?: boolean,
|
ForceUpdate?: boolean;
|
||||||
|
Clear?: boolean;
|
||||||
} = {};
|
} = {};
|
||||||
|
clearChild: VNode | null = null;
|
||||||
// one tree相关属性
|
// one tree相关属性
|
||||||
isCreated: boolean = true;
|
isCreated: boolean = true;
|
||||||
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook
|
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook
|
||||||
|
|
|
@ -175,7 +175,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null {
|
||||||
// 此vNode无需更新,但是子树需要
|
// 此vNode无需更新,但是子树需要
|
||||||
if (!processing.isCreated && processing.child !== null) {
|
if (!processing.isCreated && processing.child !== null) {
|
||||||
// 更新子节点
|
// 更新子节点
|
||||||
let child = processing.child;
|
let child: VNode | null = processing.child;
|
||||||
while (child !== null) {
|
while (child !== null) {
|
||||||
updateVNode(child, child.props);
|
updateVNode(child, child.props);
|
||||||
updateVNodePath(child);
|
updateVNodePath(child);
|
||||||
|
|
|
@ -18,8 +18,10 @@ export const Interrupted = 'Interrupted';
|
||||||
export const ShouldCapture = 'ShouldCapture';
|
export const ShouldCapture = 'ShouldCapture';
|
||||||
// For suspense
|
// For suspense
|
||||||
export const ForceUpdate = 'ForceUpdate';
|
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];
|
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
|
||||||
|
|
||||||
|
@ -89,5 +91,9 @@ export class FlagUtils {
|
||||||
static markForceUpdate(node: VNode) {
|
static markForceUpdate(node: VNode) {
|
||||||
node.flags.ForceUpdate = true;
|
node.flags.ForceUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static markClear(node: VNode) {
|
||||||
|
node.flags.Clear = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,19 @@ export function updateChildShouldUpdate(vNode: VNode) {
|
||||||
vNode.childShouldUpdate = false;
|
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
|
// 设置节点的所有父节点的childShouldUpdate
|
||||||
export function updateParentsChildShouldUpdate(vNode: VNode) {
|
export function updateParentsChildShouldUpdate(vNode: VNode) {
|
||||||
let node = vNode.parent;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue