Match-id-cc46c9f90ee2c1f0d758534ce54c0583577b42ce

This commit is contained in:
* 2022-10-25 20:30:14 +08:00 committed by *
commit b0f4ffa7bf
84 changed files with 622 additions and 846 deletions

View File

@ -120,7 +120,7 @@ const Horizon = {
isMemo, isMemo,
isPortal, isPortal,
isContextProvider, isContextProvider,
isContextConsumer isContextConsumer,
}; };
export const version = __VERSION__; export const version = __VERSION__;
@ -171,7 +171,7 @@ export {
isMemo, isMemo,
isPortal, isPortal,
isContextProvider, isContextProvider,
isContextConsumer isContextConsumer,
}; };
export default Horizon; export default Horizon;

View File

@ -17,17 +17,10 @@
* dom节点赋 VNode * dom节点赋 VNode
*/ */
import type {VNode} from '../renderer/Types'; import type { VNode } from '../renderer/Types';
import type { import type { Container, Props } from './DOMOperator';
Container,
Props,
} from './DOMOperator';
import { import { DomComponent, DomText, TreeRoot } from '../renderer/vnode/VNodeTags';
DomComponent,
DomText,
TreeRoot,
} from '../renderer/vnode/VNodeTags';
const INTERNAL_VNODE = '_horizon_VNode'; const INTERNAL_VNODE = '_horizon_VNode';
const INTERNAL_PROPS = '_horizon_Props'; const INTERNAL_PROPS = '_horizon_Props';
@ -35,7 +28,7 @@ const INTERNAL_NONDELEGATEEVENTS = '_horizon_NonDelegatedEvents';
// 通过 VNode 实例获取 DOM 节点 // 通过 VNode 实例获取 DOM 节点
export function getDom(vNode: VNode): Element | Text | null { 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;
} }
@ -43,18 +36,15 @@ export function getDom(vNode: VNode): Element | Text | null {
} }
// 将 VNode 属性相关信息挂到 DOM 对象的特定属性上 // 将 VNode 属性相关信息挂到 DOM 对象的特定属性上
export function saveVNode( export function saveVNode(vNode: VNode, dom: Element | Text | Container): void {
vNode: VNode,
dom: Element | Text | Container,
): void {
dom[INTERNAL_VNODE] = vNode; dom[INTERNAL_VNODE] = vNode;
} }
// 用 DOM 节点,来找其对应的 VNode 实例 // 用 DOM 节点,来找其对应的 VNode 实例
export function getVNode(dom: Node|Container): VNode | null { export function getVNode(dom: Node | Container): VNode | null {
const vNode = dom[INTERNAL_VNODE] || (dom as Container)._treeRoot; const vNode = dom[INTERNAL_VNODE] || (dom as Container)._treeRoot;
if (vNode) { if (vNode) {
const {tag} = vNode; const { tag } = vNode;
if (tag === DomComponent || tag === DomText || tag === TreeRoot) { if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
return vNode; return vNode;
} }
@ -78,7 +68,7 @@ export function getNearestVNode(dom: Node): null | VNode {
} }
// 获取 vNode 上的属性相关信息 // 获取 vNode 上的属性相关信息
export function getVNodeProps(dom: Element | Text): Props | null{ export function getVNodeProps(dom: Element | Text): Props | null {
return dom[INTERNAL_PROPS] || null; return dom[INTERNAL_PROPS] || null;
} }

View File

@ -13,27 +13,15 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { saveVNode, updateVNodeProps } from './DOMInternalKeys';
saveVNode, import { createDom } from './utils/DomCreator';
updateVNodeProps,
} from './DOMInternalKeys';
import {
createDom,
} from './utils/DomCreator';
import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler'; import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler';
import { shouldAutoFocus } from './utils/Common'; import { shouldAutoFocus } from './utils/Common';
import { NSS } from './utils/DomCreator'; import { NSS } from './utils/DomCreator';
import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler';
import type { VNode } from '../renderer/Types'; import type { VNode } from '../renderer/Types';
import { import { setInitValue, getPropsWithoutValue, updateValue } from './valueHandler';
setInitValue, import { compareProps, setDomProps } from './DOMPropertiesHandler/DOMPropertiesHandler';
getPropsWithoutValue,
updateValue,
} from './valueHandler';
import {
compareProps,
setDomProps,
} 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';
@ -81,12 +69,7 @@ export function resetAfterSubmit(): void {
} }
// 创建 DOM 对象 // 创建 DOM 对象
export function newDom( export function newDom(tagName: string, props: Props, parentNamespace: string, vNode: VNode): Element {
tagName: string,
props: Props,
parentNamespace: string,
vNode: VNode,
): Element {
const dom: Element = createDom(tagName, parentNamespace); const dom: Element = createDom(tagName, parentNamespace);
// 将 vNode 节点挂到 DOM 对象上 // 将 vNode 节点挂到 DOM 对象上
saveVNode(vNode, dom); saveVNode(vNode, dom);
@ -119,12 +102,7 @@ export function initDomProps(dom: Element, tagName: string, rawProps: Props): bo
} }
// 准备更新之前进行一系列校验 DOM寻找属性差异等准备工作 // 准备更新之前进行一系列校验 DOM寻找属性差异等准备工作
export function getPropChangeList( export function getPropChangeList(dom: Element, type: string, lastRawProps: Props, nextRawProps: Props): Object {
dom: Element,
type: string,
lastRawProps: Props,
nextRawProps: Props,
): Object {
// 校验两个对象的不同 // 校验两个对象的不同
validateProps(type, nextRawProps); validateProps(type, nextRawProps);
@ -151,10 +129,7 @@ export function isTextChild(type: string, props: Props): boolean {
} }
} }
export function newTextDom( export function newTextDom(text: string, processing: VNode): Text {
text: string,
processing: VNode,
): Text {
const textNode: Text = document.createTextNode(text); const textNode: Text = document.createTextNode(text);
saveVNode(processing, textNode); saveVNode(processing, textNode);
return textNode; return textNode;
@ -198,26 +173,16 @@ export function clearText(dom: Element): void {
} }
// 添加child元素 // 添加child元素
export function appendChildElement( export function appendChildElement(parent: Element | Container, child: Element | Text): void {
parent: Element | Container,
child: Element | Text
): void {
parent.appendChild(child); parent.appendChild(child);
} }
// 插入dom元素 // 插入dom元素
export function insertDomBefore( export function insertDomBefore(parent: Element | Container, child: Element | Text, beforeChild: Element | Text) {
parent: Element | Container,
child: Element | Text,
beforeChild: Element | Text,
) {
parent.insertBefore(child, beforeChild); parent.insertBefore(child, beforeChild);
} }
export function removeChildDom( export function removeChildDom(parent: Element | Container, child: Element | Text) {
parent: Element | Container,
child: Element | Text
) {
parent.removeChild(child); parent.removeChild(child);
} }

View File

@ -81,11 +81,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object {
styleProp = styleProps[j]; styleProp = styleProps[j];
updatesForStyle[styleProp] = ''; updatesForStyle[styleProp] = '';
} }
} else if ( } else if (propName === 'autoFocus' || propName === 'children' || propName === 'dangerouslySetInnerHTML') {
propName === 'autoFocus' ||
propName === 'children' ||
propName === 'dangerouslySetInnerHTML'
) {
continue; continue;
} else if (isEventProp(propName)) { } else if (isEventProp(propName)) {
if (!allDelegatedHorizonEvents.has(propName)) { if (!allDelegatedHorizonEvents.has(propName)) {

View File

@ -14,14 +14,16 @@
*/ */
function isNeedUnitCSS(styleName: string) { function isNeedUnitCSS(styleName: string) {
return !(noUnitCSS.includes(styleName) return !(
|| styleName.startsWith('borderImage') noUnitCSS.includes(styleName) ||
|| styleName.startsWith('flex') styleName.startsWith('borderImage') ||
|| styleName.startsWith('gridRow') styleName.startsWith('flex') ||
|| styleName.startsWith('gridColumn') styleName.startsWith('gridRow') ||
|| styleName.startsWith('stroke') styleName.startsWith('gridColumn') ||
|| styleName.startsWith('box') styleName.startsWith('stroke') ||
|| styleName.endsWith('Opacity')); styleName.startsWith('box') ||
styleName.endsWith('Opacity')
);
} }
/** /**
@ -29,7 +31,7 @@ function isNeedUnitCSS(styleName: string) {
* *
* *
*/ */
export function adjustStyleValue(name, value) { export function adjustStyleValue(name, value) {
let validValue = value; let validValue = value;
if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) { if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) {
@ -50,7 +52,7 @@ export function setStyles(dom, styles) {
} }
const style = dom.style; const style = dom.style;
Object.keys(styles).forEach((name) => { Object.keys(styles).forEach(name => {
const styleVal = styles[name]; const styleVal = styles[name];
style[name] = adjustStyleValue(name, styleVal); style[name] = adjustStyleValue(name, styleVal);
@ -60,5 +62,19 @@ export function setStyles(dom, styles) {
/** /**
* css * css
*/ */
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp', const noUnitCSS = [
'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom']; 'animationIterationCount',
'columnCount',
'columns',
'gridArea',
'fontWeight',
'lineClamp',
'lineHeight',
'opacity',
'order',
'orphans',
'tabSize',
'widows',
'zIndex',
'zoom',
];

View File

@ -13,25 +13,77 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { getPropDetails, PROPERTY_TYPE } from '../validators/PropertiesData';
getPropDetails, import { isInvalidValue } from '../validators/ValidateProps';
PROPERTY_TYPE, import { getNamespaceCtx } from '../../renderer/ContextSaver';
} from '../validators/PropertiesData'; import { NSS } from '../utils/DomCreator';
import {isInvalidValue} from '../validators/ValidateProps'; import { getDomTag } from '../utils/Common';
import {getNamespaceCtx} from '../../renderer/ContextSaver';
import {NSS} from '../utils/DomCreator';
import {getDomTag} from '../utils/Common';
const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits', const svgHumpAttr = new Set([
'contentScriptType', 'contentStyleType', 'diffuseConstant', 'edgeMode', 'externalResourcesRequired', 'filterRes', 'allowReorder',
'filterUnits', 'glyphRef', 'gradientTransform', 'gradientUnits', 'kernelMatrix', 'kernelUnitLength', 'keyPoints', 'autoReverse',
'keySplines', 'keyTimes', 'lengthAdjust', 'limitingConeAngle', 'markerHeight', 'markerUnits', 'markerWidth', 'baseFrequency',
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,', 'baseProfile',
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits', 'calcMode',
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'clipPathUnits',
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'contentScriptType',
'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY', 'contentStyleType',
'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']); 'diffuseConstant',
'edgeMode',
'externalResourcesRequired',
'filterRes',
'filterUnits',
'glyphRef',
'gradientTransform',
'gradientUnits',
'kernelMatrix',
'kernelUnitLength',
'keyPoints',
'keySplines',
'keyTimes',
'lengthAdjust',
'limitingConeAngle',
'markerHeight',
'markerUnits',
'markerWidth',
'maskContentUnits',
'maskUnits',
'numOctaves',
'pathLength',
'patternContentUnits',
'patternTransform,',
'patternUnits',
'pointsAtX',
'pointsAtY',
'pointsAtZ',
'preserveAlpha',
'preserveAspectRatio',
'primitiveUnits',
'referrerPolicy',
'refX',
'refY',
'repeatCount',
'repeatDur',
'requiredExtensions',
'requiredFeatures',
'specularConstant',
'specularExponent',
'spreadMethod',
'startOffset',
'stdDeviation',
'stitchTiles',
'surfaceScale',
'systemLanguage',
'tableValues',
'targetX',
'targetY',
'textLength',
'viewBox',
'viewTarget',
'xChannelSelector',
'yChannelSelector',
'zoomAndPan',
]);
// 驼峰 变 “-” // 驼峰 变 “-”
function convertToLowerCase(str) { function convertToLowerCase(str) {
@ -65,19 +117,22 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN
dom.setAttribute(attrName, String(value)); dom.setAttribute(attrName, String(value));
} }
} else if (['checked', 'multiple', 'muted', 'selected'].includes(propDetails.attrName)) { } else if (['checked', 'multiple', 'muted', 'selected'].includes(propDetails.attrName)) {
if (value === null) { // 必填属性设置默认值 if (value === null) {
// 必填属性设置默认值
dom[propDetails.attrName] = false; dom[propDetails.attrName] = false;
} else { } else {
dom[propDetails.attrName] = value; dom[propDetails.attrName] = value;
} }
} else { // 处理其他普通属性 } else {
// 处理其他普通属性
if (value === null) { if (value === null) {
dom.removeAttribute(propDetails.attrName); dom.removeAttribute(propDetails.attrName);
} else { } else {
const {type, attrNS} = propDetails; // 数据类型、固有属性命名空间 const { type, attrNS } = propDetails; // 数据类型、固有属性命名空间
const attributeName = propDetails.attrName; // 固有属性名 const attributeName = propDetails.attrName; // 固有属性名
let attributeValue; let attributeValue;
if (type === PROPERTY_TYPE.BOOLEAN) { // 即可以用作标志又可以是属性值的属性 if (type === PROPERTY_TYPE.BOOLEAN) {
// 即可以用作标志又可以是属性值的属性
attributeValue = ''; attributeValue = '';
} else { } else {
attributeValue = String(value); attributeValue = String(value);

View File

@ -17,9 +17,9 @@
* *
*/ */
import {getIFrameFocusedDom, isText} from './utils/Common'; import { getIFrameFocusedDom, isText } from './utils/Common';
import {isElement} from './utils/Common'; import { isElement } from './utils/Common';
/** /**
* textarea input * textarea input
@ -76,7 +76,8 @@ function isNodeContainsByTargetNode(targetNode, node) {
if (typeof targetNode.contains === 'function') { if (typeof targetNode.contains === 'function') {
return targetNode.contains(node); // 该的节点是否为目标节点的后代节点 return targetNode.contains(node); // 该的节点是否为目标节点的后代节点
} }
if (typeof targetNode.compareDocumentPosition === 'function') { // compareDocumentPosition 数值,表示两个节点彼此做比较的位置 if (typeof targetNode.compareDocumentPosition === 'function') {
// compareDocumentPosition 数值,表示两个节点彼此做比较的位置
const CONTAINS_CODE = 16; const CONTAINS_CODE = 16;
// 返回 16 代表 第二节点在第一节点内部 // 返回 16 代表 第二节点在第一节点内部
return targetNode.compareDocumentPosition(node) === CONTAINS_CODE; return targetNode.compareDocumentPosition(node) === CONTAINS_CODE;
@ -153,9 +154,10 @@ export function resetSelectionRange(preSelectionRangeData: SelectionData) {
let ancestor = preFocusedDom.parentNode; let ancestor = preFocusedDom.parentNode;
// 查找先前的 focus 节点的先祖 // 查找先前的 focus 节点的先祖
while (ancestor) { while (ancestor) {
if (isElement(ancestor)) { // 是元素节点,就把先祖信息放到先祖数组中 if (isElement(ancestor)) {
// 是元素节点,就把先祖信息放到先祖数组中
// @ts-ignore // @ts-ignore
const {scrollLeft, scrollTop} = ancestor; const { scrollLeft, scrollTop } = ancestor;
ancestors.push({ ancestors.push({
dom: ancestor, dom: ancestor,
scrollLeft, scrollLeft,

View File

@ -13,8 +13,8 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {HorizonDom} from './Interface'; import { HorizonDom } from './Interface';
import {Props} from '../DOMOperator'; import { Props } from '../DOMOperator';
/** /**
* input textarea * input textarea
@ -35,12 +35,15 @@ export function getIFrameFocusedDom() {
while (focusedDom instanceof currentWindow.HTMLIFrameElement) { while (focusedDom instanceof currentWindow.HTMLIFrameElement) {
try { try {
// 访问 HTMLIframeElement 的 contentDocument 可能会导致浏览器抛出错误 // 访问 HTMLIframeElement 的 contentDocument 可能会导致浏览器抛出错误
if (typeof focusedDom.contentWindow.location.href === 'string') { // iframe 的内容为同源 if (typeof focusedDom.contentWindow.location.href === 'string') {
// iframe 的内容为同源
focusedDom = getFocusedDom(focusedDom.contentWindow.document); focusedDom = getFocusedDom(focusedDom.contentWindow.document);
} else { // 非同源 iframe 因为安全性原因无法获取其中的具体元素 } else {
// 非同源 iframe 因为安全性原因无法获取其中的具体元素
break; break;
} }
} catch (e) { // 非同源 iframe 因为安全性原因无法获取其中的具体元素 } catch (e) {
// 非同源 iframe 因为安全性原因无法获取其中的具体元素
break; break;
} }
} }

View File

@ -1,4 +1,3 @@
/* /*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* *
@ -21,10 +20,7 @@ export const NSS = {
}; };
// 创建DOM元素 // 创建DOM元素
export function createDom( export function createDom(tagName: string, parentNamespace: string): Element {
tagName: string,
parentNamespace: string,
): Element {
let dom: Element; let dom: Element;
const selfNamespace = NSS[tagName] || NSS.html; const selfNamespace = NSS[tagName] || NSS.html;
const ns = parentNamespace !== NSS.html ? parentNamespace : selfNamespace; const ns = parentNamespace !== NSS.html ? parentNamespace : selfNamespace;

View File

@ -13,23 +13,16 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { getPropDetails, PROPERTY_TYPE, PropDetails } from './PropertiesData';
getPropDetails, PROPERTY_TYPE, PropDetails,
} from './PropertiesData';
const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
// 是内置元素 // 是内置元素
export function isNativeElement(tagName: string, props: Record<string, any>) { export function isNativeElement(tagName: string, props: Record<string, any>) {
return !tagName.includes('-') && props.is === undefined; return !tagName.includes('-') && props.is === undefined;
} }
function isInvalidBoolean( function isInvalidBoolean(attributeName: string, value: any, propDetails: PropDetails): boolean {
attributeName: string,
value: any,
propDetails: PropDetails,
): boolean {
if (propDetails.type === PROPERTY_TYPE.SPECIAL) { if (propDetails.type === PROPERTY_TYPE.SPECIAL) {
return false; return false;
} }
@ -78,7 +71,7 @@ export function isInvalidValue(
name: string, name: string,
value: any, value: any,
propDetails: PropDetails | null, propDetails: PropDetails | null,
isNativeTag: boolean, isNativeTag: boolean
): boolean { ): boolean {
if (value == null) { if (value == null) {
return true; return true;
@ -122,11 +115,7 @@ export function validateProps(type, props) {
const propString = invalidProps.map(prop => '`' + prop + '`').join(', '); const propString = invalidProps.map(prop => '`' + prop + '`').join(', ');
if (invalidProps.length >= 1) { if (invalidProps.length >= 1) {
console.error( console.error('Invalid value for prop %s on <%s> tag.', propString, type);
'Invalid value for prop %s on <%s> tag.',
propString,
type,
);
} }
} }
} }

View File

@ -28,7 +28,7 @@ function getInitValue(dom: HTMLInputElement, props: Props) {
export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) {
// checked属于必填属性无法置 // checked属于必填属性无法置
let {checked} = props; let { checked } = props;
if (checked == null) { if (checked == null) {
checked = getInitValue(dom, props).initChecked; checked = getInitValue(dom, props).initChecked;
} }
@ -43,9 +43,10 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) {
} }
export function updateInputValue(dom: HTMLInputElement, props: Props) { export function updateInputValue(dom: HTMLInputElement, props: Props) {
const {value, checked} = props; const { value, checked } = props;
if (value != null) { // 处理 dom.value 逻辑 if (value != null) {
// 处理 dom.value 逻辑
if (dom.value !== String(value)) { if (dom.value !== String(value)) {
dom.value = String(value); dom.value = String(value);
} }
@ -56,8 +57,8 @@ export function updateInputValue(dom: HTMLInputElement, props: Props) {
// 设置input的初始值 // 设置input的初始值
export function setInitInputValue(dom: HTMLInputElement, props: Props) { export function setInitInputValue(dom: HTMLInputElement, props: Props) {
const {value, defaultValue} = props; const { value, defaultValue } = props;
const {initValue, initChecked} = getInitValue(dom, props); const { initValue, initChecked } = getInitValue(dom, props);
if (value != null || defaultValue != null) { if (value != null || defaultValue != null) {
// value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串 // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串

View File

@ -19,7 +19,7 @@ import { Props } from '../utils/Interface';
// 把 const a = 'a'; <option>gir{a}ffe</option> 转成 giraffe // 把 const a = 'a'; <option>gir{a}ffe</option> 转成 giraffe
function concatChildren(children) { function concatChildren(children) {
let content = ''; let content = '';
Children.forEach(children, function(child) { Children.forEach(children, function (child) {
content += child; content += child;
}); });

View File

@ -13,12 +13,12 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {HorizonSelect, Props} from '../utils/Interface'; import { HorizonSelect, Props } from '../utils/Interface';
function updateMultipleValue(options, newValues) { function updateMultipleValue(options, newValues) {
const newValueSet = new Set(); const newValueSet = new Set();
newValues.forEach((val) => { newValues.forEach(val => {
newValueSet.add(String(val)); newValueSet.add(String(val));
}); });
@ -61,8 +61,8 @@ export function getSelectPropsWithoutValue(dom: HorizonSelect, properties: Objec
}; };
} }
export function updateSelectValue(dom: HorizonSelect, props: Props, isInit: boolean = false) { export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = false) {
const {value, defaultValue, multiple} = props; const { value, defaultValue, multiple } = props;
const oldMultiple = dom._multiple !== undefined ? dom._multiple : dom.multiple; const oldMultiple = dom._multiple !== undefined ? dom._multiple : dom.multiple;
const newMultiple = Boolean(multiple); const newMultiple = Boolean(multiple);
@ -71,7 +71,8 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit: bool
// 设置了 value 属性 // 设置了 value 属性
if (value != null) { if (value != null) {
updateValue(dom.options, value, newMultiple); updateValue(dom.options, value, newMultiple);
} else if (oldMultiple !== newMultiple) { // 修改了 multiple 属性 } else if (oldMultiple !== newMultiple) {
// 修改了 multiple 属性
// 切换 multiple 之后,如果设置了 defaultValue 需要重新应用 // 切换 multiple 之后,如果设置了 defaultValue 需要重新应用
if (defaultValue != null) { if (defaultValue != null) {
updateValue(dom.options, defaultValue, newMultiple); updateValue(dom.options, defaultValue, newMultiple);
@ -79,7 +80,8 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit: bool
// 恢复到未选定状态 // 恢复到未选定状态
updateValue(dom.options, newMultiple ? [] : '', newMultiple); updateValue(dom.options, newMultiple ? [] : '', newMultiple);
} }
} else if (isInit && defaultValue != null) { // 设置了 defaultValue 属性 } else if (isInit && defaultValue != null) {
// 设置了 defaultValue 属性
updateValue(dom.options, defaultValue, newMultiple); updateValue(dom.options, defaultValue, newMultiple);
} }
} }

View File

@ -13,15 +13,14 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {Props} from '../utils/Interface'; import { Props } from '../utils/Interface';
// 值的优先级 value > children > defaultValue // 值的优先级 value > children > defaultValue
function getInitValue(props: Props) { function getInitValue(props: Props) {
const {value} = props; const { value } = props;
if (value == null) { if (value == null) {
const {defaultValue, children} = props; const { defaultValue, children } = props;
let initValue = defaultValue; let initValue = defaultValue;
// children content存在时会覆盖defaultValue // children content存在时会覆盖defaultValue
@ -45,7 +44,7 @@ export function getTextareaPropsWithoutValue(dom: HTMLTextAreaElement, propertie
}; };
} }
export function updateTextareaValue(dom: HTMLTextAreaElement, props: Props, isInit: boolean = false) { export function updateTextareaValue(dom: HTMLTextAreaElement, props: Props, isInit = false) {
if (isInit) { if (isInit) {
const initValue = getInitValue(props); const initValue = getInitValue(props);
if (initValue !== '') { if (initValue !== '') {
@ -63,4 +62,3 @@ export function updateTextareaValue(dom: HTMLTextAreaElement, props: Props, isIn
} }
} }
} }

View File

@ -52,7 +52,7 @@ export function watchValueChange(dom) {
const setFunc = descriptor?.set; const setFunc = descriptor?.set;
Object.defineProperty(dom, keyForValue, { Object.defineProperty(dom, keyForValue, {
...descriptor, ...descriptor,
set: function(value) { set: function (value) {
currentVal = String(value); currentVal = String(value);
setFunc?.apply(this, [value]); setFunc?.apply(this, [value]);
}, },

View File

@ -18,23 +18,11 @@
* *
*/ */
import {HorizonDom, HorizonSelect, Props} from '../utils/Interface'; import { HorizonDom, HorizonSelect, Props } from '../utils/Interface';
import { import { getInputPropsWithoutValue, setInitInputValue, updateInputValue } from './InputValueHandler';
getInputPropsWithoutValue, import { getOptionPropsWithoutValue } from './OptionValueHandler';
setInitInputValue, import { getSelectPropsWithoutValue, updateSelectValue } from './SelectValueHandler';
updateInputValue, import { getTextareaPropsWithoutValue, updateTextareaValue } from './TextareaValueHandler';
} from './InputValueHandler';
import {
getOptionPropsWithoutValue,
} from './OptionValueHandler';
import {
getSelectPropsWithoutValue,
updateSelectValue,
} from './SelectValueHandler';
import {
getTextareaPropsWithoutValue,
updateTextareaValue,
} from './TextareaValueHandler';
// 获取元素除了被代理的值以外的属性 // 获取元素除了被代理的值以外的属性
function getPropsWithoutValue(type: string, dom: HorizonDom, props: Props) { function getPropsWithoutValue(type: string, dom: HorizonDom, props: Props) {
@ -86,8 +74,4 @@ function updateValue(type: string, dom: HorizonDom, props: Props) {
} }
} }
export { export { getPropsWithoutValue, setInitValue, updateValue };
getPropsWithoutValue,
setInitValue,
updateValue,
};

View File

@ -16,10 +16,7 @@
/** /**
* *
*/ */
import { import { allDelegatedHorizonEvents, allDelegatedNativeEvents } from './EventHub';
allDelegatedHorizonEvents,
allDelegatedNativeEvents,
} from './EventHub';
import { isDocument } from '../dom/utils/Common'; import { isDocument } from '../dom/utils/Common';
import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys'; import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder'; import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder';
@ -27,11 +24,7 @@ import { handleEventMain } from './HorizonEventMain';
import { decorateNativeEvent } from './EventWrapper'; import { decorateNativeEvent } from './EventWrapper';
import { VNode } from '../renderer/vnode/VNode'; import { VNode } from '../renderer/vnode/VNode';
const listeningMarker = const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
'_horizonListening' +
Math.random()
.toString(36)
.slice(4);
// 触发委托事件 // 触发委托事件
function triggerDelegatedEvent( function triggerDelegatedEvent(
@ -71,7 +64,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
const nativeEvents = allDelegatedHorizonEvents.get(eventName); const nativeEvents = allDelegatedHorizonEvents.get(eventName);
nativeEvents.forEach(nativeEvent => { nativeEvents.forEach(nativeEvent => {
const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent;
// 事件存储在DOM节点属性避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听 // 事件存储在DOM节点属性避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听
let events = currentRoot.realNode.$EV; let events = currentRoot.realNode.$EV;

View File

@ -40,7 +40,7 @@ export class WrappedEvent {
stopPropagation: () => void; stopPropagation: () => void;
preventDefault: () => void; preventDefault: () => void;
propagationStopped = false propagationStopped = false;
isPropagationStopped = (): boolean => this.propagationStopped; isPropagationStopped = (): boolean => this.propagationStopped;
// 适配Keyboard键盘事件该函数不能由合成事件调用 // 适配Keyboard键盘事件该函数不能由合成事件调用
@ -51,9 +51,9 @@ export class WrappedEvent {
constructor(customEventName: string, nativeEvtName: string, nativeEvent: AnyNativeEvent) { constructor(customEventName: string, nativeEvtName: string, nativeEvent: AnyNativeEvent) {
for (const name in nativeEvent) { for (const name in nativeEvent) {
this[name] = nativeEvent[name]; this[name] = nativeEvent[name];
if(name === 'getModifierState') { if (name === 'getModifierState') {
const keyBoardEvent = nativeEvent as KeyboardEvent; const keyBoardEvent = nativeEvent as KeyboardEvent;
this.getModifierState = (keyArg) => keyBoardEvent.getModifierState(keyArg); this.getModifierState = keyArg => keyBoardEvent.getModifierState(keyArg);
} }
} }
// stopPropagation和preventDefault 必须通过Event实例调用 // stopPropagation和preventDefault 必须通过Event实例调用

View File

@ -15,7 +15,7 @@
import { getVNodeProps } from '../dom/DOMInternalKeys'; import { getVNodeProps } from '../dom/DOMInternalKeys';
import { getDomTag } from '../dom/utils/Common'; import { getDomTag } from '../dom/utils/Common';
import { Props } from '../dom/utils/Interface'; import { Props } from '../dom/utils/Interface';
import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler'; import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler';
import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler';
import { updateInputValue } from '../dom/valueHandler/InputValueHandler'; import { updateInputValue } from '../dom/valueHandler/InputValueHandler';
@ -87,4 +87,3 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) {
updateInputValue(inputDom, props); updateInputValue(inputDom, props);
} }
} }

View File

@ -78,11 +78,7 @@ function getChangeListeners(
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) { if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
recordChangeEventTargets(target); recordChangeEventTargets(target);
const event = decorateNativeEvent( const event = decorateNativeEvent('onChange', 'change', nativeEvt);
'onChange',
'change',
nativeEvt,
);
return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL); return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL);
} }
@ -95,7 +91,7 @@ function getCommonListeners(
vNode: null | VNode, vNode: null | VNode,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
target: null | EventTarget, target: null | EventTarget,
isCapture: boolean, isCapture: boolean
): ListenerUnitList { ): ListenerUnitList {
const horizonEvtName = transformToHorizonEvent(nativeEvtName); const horizonEvtName = transformToHorizonEvent(nativeEvtName);
@ -117,12 +113,7 @@ function getCommonListeners(
} }
const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent); const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent);
return getListenersFromTree( return getListenersFromTree(vNode, horizonEvtName, horizonEvent, isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE);
vNode,
horizonEvtName,
horizonEvent,
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
);
} }
// 按顺序执行事件队列 // 按顺序执行事件队列
@ -145,27 +136,16 @@ function triggerHorizonEvents(
nativeEvtName: string, nativeEvtName: string,
isCapture: boolean, isCapture: boolean,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
vNode: VNode | null, vNode: VNode | null
) { ) {
const target = nativeEvent.target || nativeEvent.srcElement!; const target = nativeEvent.target || nativeEvent.srcElement!;
// 触发普通委托事件 // 触发普通委托事件
let listenerList: ListenerUnitList = getCommonListeners( let listenerList: ListenerUnitList = getCommonListeners(nativeEvtName, vNode, nativeEvent, target, isCapture);
nativeEvtName,
vNode,
nativeEvent,
target,
isCapture,
);
// 触发特殊handler委托事件 // 触发特殊handler委托事件
if (!isCapture && horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) { if (!isCapture && horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) {
const changeListeners = getChangeListeners( const changeListeners = getChangeListeners(nativeEvtName, nativeEvent, vNode, target);
nativeEvtName,
nativeEvent,
vNode,
target
);
if (changeListeners.length) { if (changeListeners.length) {
listenerList = listenerList.concat(changeListeners); listenerList = listenerList.concat(changeListeners);
} }
@ -175,7 +155,6 @@ function triggerHorizonEvents(
processListeners(listenerList); processListeners(listenerList);
} }
// 其他事件正在执行中标记 // 其他事件正在执行中标记
let isInEventsExecution = false; let isInEventsExecution = false;
@ -185,7 +164,7 @@ export function handleEventMain(
isCapture: boolean, isCapture: boolean,
nativeEvent: AnyNativeEvent, nativeEvent: AnyNativeEvent,
vNode: null | VNode, vNode: null | VNode,
targetDom: EventTarget, targetDom: EventTarget
): void { ): void {
let startVNode = vNode; let startVNode = vNode;
if (startVNode !== null) { if (startVNode !== null) {

View File

@ -1,4 +1,3 @@
/* /*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* *
@ -16,12 +15,11 @@
export function isInputElement(dom?: HTMLElement): boolean { export function isInputElement(dom?: HTMLElement): boolean {
return dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement; return dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement;
} }
export function setPropertyWritable(obj, propName) { export function setPropertyWritable(obj, propName) {
const desc = Object.getOwnPropertyDescriptor(obj, propName); const desc = Object.getOwnPropertyDescriptor(obj, propName);
if (!desc || !desc.writable) { if (!desc || !desc.writable) {
Object.defineProperty(obj, propName, { writable : true }); Object.defineProperty(obj, propName, { writable: true });
} }
} }

View File

@ -56,8 +56,8 @@ function mergeDefault(sourceObj, defaultObj) {
function buildElement(isClone, type, setting, children) { function buildElement(isClone, type, setting, children) {
// setting中的值优先级最高clone情况下从 type 中取值,创建情况下直接赋值为 null // setting中的值优先级最高clone情况下从 type 中取值,创建情况下直接赋值为 null
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); const key = setting && setting.key !== undefined ? String(setting.key) : isClone ? type.key : null;
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null); const ref = setting && setting.ref !== undefined ? setting.ref : isClone ? type.ref : null;
const props = isClone ? { ...type.props } : {}; const props = isClone ? { ...type.props } : {};
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode(); let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();

View File

@ -54,7 +54,7 @@ export const helper = {
return { name: HookName.RefHook, hIndex, value: (state as Ref<any>).current }; return { name: HookName.RefHook, hIndex, value: (state as Ref<any>).current };
} else if (isEffectHook(state)) { } else if (isEffectHook(state)) {
const name = const name =
state.effectConstant == EffectConstant.LayoutEffect || (EffectConstant.LayoutEffect | EffectConstant.DepsChange) state.effectConstant == EffectConstant.LayoutEffect || EffectConstant.LayoutEffect | EffectConstant.DepsChange
? HookName.LayoutEffectHook ? HookName.LayoutEffectHook
: HookName.EffectHook; : HookName.EffectHook;
return { name, hIndex, value: (state as Effect).effect }; return { name, hIndex, value: (state as Effect).effect };

View File

@ -73,7 +73,7 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
const result = { const result = {
reducer, reducer,
getState: function() { getState: function () {
return store.$s.stateWrapper; return store.$s.stateWrapper;
}, },
subscribe: listener => { subscribe: listener => {

View File

@ -19,7 +19,7 @@ import { createContext } from '../../renderer/components/context/CreateContext';
import { createElement } from '../../external/JSXElement'; import { createElement } from '../../external/JSXElement';
import { BoundActionCreator } from './redux'; import { BoundActionCreator } from './redux';
import { ReduxAction } from './redux'; import { ReduxAction } from './redux';
import { ReduxStoreHandler } from '../store/StoreHandler' import { ReduxStoreHandler } from '../store/StoreHandler';
const DefaultContext = createContext(null); const DefaultContext = createContext(null);
type Context = typeof DefaultContext; type Context = typeof DefaultContext;
@ -44,8 +44,8 @@ export function createStoreHook(context: Context) {
} }
export function createSelectorHook(context: Context): (selector?: (any) => any) => any { export function createSelectorHook(context: Context): (selector?: (any) => any) => any {
const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
return function(selector = state => state) { return function (selector = state => state) {
const [b, fr] = useState(false); const [b, fr] = useState(false);
useEffect(() => { useEffect(() => {
@ -59,9 +59,9 @@ export function createSelectorHook(context: Context): (selector?: (any) => any)
}; };
} }
export function createDispatchHook(context: Context): ()=>BoundActionCreator { export function createDispatchHook(context: Context): () => BoundActionCreator {
const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler; const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
return function() { return function () {
return action => { return action => {
store.dispatch(action); store.dispatch(action);
}; };
@ -120,7 +120,7 @@ export function connect(
function Wrapper(props) { function Wrapper(props) {
const [f, forceReload] = useState(true); const [f, forceReload] = useState(true);
const store = (useStore() as unknown) as ReduxStoreHandler; const store = useStore() as unknown as ReduxStoreHandler;
useEffect(() => { useEffect(() => {
const unsubscribe = store.subscribe(() => forceReload(!f)); const unsubscribe = store.subscribe(() => forceReload(!f));

View File

@ -17,20 +17,22 @@ import { ReduxAction, ReduxMiddleware } from './redux';
import { ReduxStoreHandler } from '../store/StoreHandler'; import { ReduxStoreHandler } from '../store/StoreHandler';
function createThunkMiddleware(extraArgument?: any): ReduxMiddleware { function createThunkMiddleware(extraArgument?: any): ReduxMiddleware {
return (store: ReduxStoreHandler) => (next: (action: ReduxAction) => any) => ( return (store: ReduxStoreHandler) =>
action: (next: (action: ReduxAction) => any) =>
| ReduxAction (
| ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any) action:
) => { | ReduxAction
// This gets called for every action you dispatch. | ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any)
// If it's a function, call it. ) => {
if (typeof action === 'function') { // This gets called for every action you dispatch.
return action(store.dispatch, store.getState.bind(store), extraArgument); // If it's a function, call it.
} if (typeof action === 'function') {
return action(store.dispatch, store.getState.bind(store), extraArgument);
}
// Otherwise, just continue processing this action as usual // Otherwise, just continue processing this action as usual
return next(action); return next(action);
}; };
} }
export const thunk = createThunkMiddleware(); export const thunk = createThunkMiddleware();

View File

@ -13,17 +13,16 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {IObserver} from './Observer'; import { IObserver } from './Observer';
/** /**
* Observer * Observer
*
*/ */
export class HooklessObserver implements IObserver { export class HooklessObserver implements IObserver {
listeners: (() => void)[] = [];
listeners:(() => void)[] = []; useProp(key: string | symbol): void {}
useProp(key: string | symbol): void {
}
addListener(listener: () => void) { addListener(listener: () => void) {
this.listeners.push(listener); this.listeners.push(listener);
@ -46,12 +45,9 @@ export class HooklessObserver implements IObserver {
}); });
} }
triggerUpdate(vNode): void { triggerUpdate(vNode): void {}
}
allChange(): void { allChange(): void {}
}
clearByVNode(vNode): void { clearByVNode(vNode): void {}
}
} }

View File

@ -109,7 +109,7 @@ export class Observer implements IObserver {
} }
allChange(): void { allChange(): void {
let keyIt = this.keyVNodes.keys(); const keyIt = this.keyVNodes.keys();
let keyItem = keyIt.next(); let keyItem = keyIt.next();
while (!keyItem.done) { while (!keyItem.done) {
this.setProp(keyItem.value); this.setProp(keyItem.value);

View File

@ -54,7 +54,7 @@ export function createProxy(rawObj: any, hookObserver = true): any {
// 创建Proxy // 创建Proxy
let proxyObj; let proxyObj;
if (!hookObserver) { if (!hookObserver) {
proxyObj = createObjectProxy(rawObj,true); proxyObj = createObjectProxy(rawObj, true);
} else if (isArray(rawObj)) { } else if (isArray(rawObj)) {
// 数组 // 数组
proxyObj = createArrayProxy(rawObj as []); proxyObj = createArrayProxy(rawObj as []);

View File

@ -34,16 +34,16 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL
const observer = getObserver(rawObj); const observer = getObserver(rawObj);
if (key === 'watch'){ if (key === 'watch') {
return (prop, handler:(key:string, oldValue:any, newValue:any)=>void)=>{ return (prop, handler: (key: string, oldValue: any, newValue: any) => void) => {
if(!observer.watchers[prop]){ if (!observer.watchers[prop]) {
observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[]; observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
} }
observer.watchers[prop].push(handler); observer.watchers[prop].push(handler);
return ()=>{ return () => {
observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler); observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
} };
} };
} }
if (key === 'addListener') { if (key === 'addListener') {
@ -81,7 +81,7 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo
const ret = Reflect.set(rawObj, key, newValue, receiver); const ret = Reflect.set(rawObj, key, newValue, receiver);
if (!isSame(newValue, oldValue)) { if (!isSame(newValue, oldValue)) {
if(observer.watchers?.[key]){ if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => { observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue); cb(key, oldValue, newValue);
}); });

View File

@ -23,7 +23,7 @@ export function readonlyProxy<T extends object>(target: T): ProxyHandler<T> {
if (isObject(result)) { if (isObject(result)) {
return readonlyProxy(result); return readonlyProxy(result);
} }
} catch(err) { } catch (err) {
// 不处理 // 不处理
} }
return result; return result;

View File

@ -14,7 +14,6 @@
*/ */
export interface IObserver { export interface IObserver {
useProp: (key: string | symbol) => void; useProp: (key: string | symbol) => void;
addListener: (listener: () => void) => void; addListener: (listener: () => void) => void;

View File

@ -18,16 +18,16 @@
*/ */
import type { PromiseType, VNode } from './Types'; import type { PromiseType, VNode } from './Types';
import type {Update} from './UpdateHandler'; import type { Update } from './UpdateHandler';
import {ClassComponent, TreeRoot} from './vnode/VNodeTags'; import { ClassComponent, TreeRoot } from './vnode/VNodeTags';
import {FlagUtils, Interrupted, DidCapture, InitFlag} from './vnode/VNodeFlags'; import { FlagUtils, Interrupted, DidCapture, InitFlag } from './vnode/VNodeFlags';
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler'; import { newUpdate, UpdateState, pushUpdate } from './UpdateHandler';
import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder'; import { launchUpdateFromVNode, tryRenderFromRoot } from './TreeBuilder';
import {setRootThrowError} from './submit/Submit'; import { setRootThrowError } from './submit/Submit';
import {handleSuspenseChildThrowError} from './render/SuspenseComponent'; import { handleSuspenseChildThrowError } from './render/SuspenseComponent';
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate'; import { updateShouldUpdateOfTree } from './vnode/VNodeShouldUpdate';
import {BuildErrored, setBuildResult} from './GlobalVar'; import { BuildErrored, setBuildResult } from './GlobalVar';
function consoleError(error: any): void { function consoleError(error: any): void {
if (isTest) { if (isTest) {
@ -38,18 +38,13 @@ function consoleError(error: any): void {
} }
} }
function handleRootError( function handleRootError(error: any) {
error: any,
) {
// 注意:如果根节点抛出错误,不会销毁整棵树,只打印日志,抛出异常。 // 注意:如果根节点抛出错误,不会销毁整棵树,只打印日志,抛出异常。
setRootThrowError(error); setRootThrowError(error);
consoleError(error); consoleError(error);
} }
function createClassErrorUpdate( function createClassErrorUpdate(vNode: VNode, error: any): Update {
vNode: VNode,
error: any,
): Update {
const update = newUpdate(); const update = newUpdate();
update.type = UpdateState.Error; update.type = UpdateState.Error;
@ -78,13 +73,10 @@ function createClassErrorUpdate(
return update; return update;
} }
function isPromise(error: any): error is PromiseType<any> { function isPromise(error: any): error is PromiseType<any> {
return error !== null && typeof error === 'object' && typeof error.then === 'function' return error !== null && typeof error === 'object' && typeof error.then === 'function';
} }
// 处理capture和bubble阶段抛出的错误 // 处理capture和bubble阶段抛出的错误
export function handleRenderThrowError( export function handleRenderThrowError(sourceVNode: VNode, error: any) {
sourceVNode: VNode,
error: any,
) {
// vNode抛出了异常标记Interrupted中断 // vNode抛出了异常标记Interrupted中断
FlagUtils.markInterrupted(sourceVNode); FlagUtils.markInterrupted(sourceVNode);
// dirtyNodes 不再有效 // dirtyNodes 不再有效
@ -117,10 +109,8 @@ export function handleRenderThrowError(
const instance = vNode.realNode; const instance = vNode.realNode;
if ( if (
(vNode.flags & DidCapture) === InitFlag && (vNode.flags & DidCapture) === InitFlag &&
( (typeof ctor.getDerivedStateFromError === 'function' ||
typeof ctor.getDerivedStateFromError === 'function' || (instance !== null && typeof instance.componentDidCatch === 'function'))
(instance !== null && typeof instance.componentDidCatch === 'function')
)
) { ) {
FlagUtils.markShouldCapture(vNode); FlagUtils.markShouldCapture(vNode);
@ -155,7 +145,6 @@ function triggerUpdate(vNode, state) {
} }
} }
// 处理submit阶段的异常 // 处理submit阶段的异常
export function handleSubmitError(vNode: VNode, error: any) { export function handleSubmitError(vNode: VNode, error: any) {
if (vNode.tag === TreeRoot) { if (vNode.tag === TreeRoot) {
@ -169,20 +158,19 @@ export function handleSubmitError(vNode: VNode, error: any) {
if (node.tag === TreeRoot) { if (node.tag === TreeRoot) {
handleRootError(error); handleRootError(error);
return; return;
} else if (node.tag === ClassComponent) { // 只有 class 组件才可以成为错误边界组件 } else if (node.tag === ClassComponent) {
// 只有 class 组件才可以成为错误边界组件
const ctor = node.type; const ctor = node.type;
const instance = node.realNode; const instance = node.realNode;
if ( if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function') {
typeof ctor.getDerivedStateFromError === 'function' ||
typeof instance.componentDidCatch === 'function'
) {
const getDerivedStateFromError = node.type.getDerivedStateFromError; const getDerivedStateFromError = node.type.getDerivedStateFromError;
if (typeof getDerivedStateFromError === 'function') { if (typeof getDerivedStateFromError === 'function') {
// 打印错误 // 打印错误
consoleError(error); consoleError(error);
const retState = getDerivedStateFromError(error); const retState = getDerivedStateFromError(error);
if (retState) { // 有返回值 if (retState) {
// 有返回值
// 触发更新 // 触发更新
triggerUpdate(node, retState); triggerUpdate(node, retState);
} }
@ -190,7 +178,8 @@ export function handleSubmitError(vNode: VNode, error: any) {
// 处理componentDidCatch // 处理componentDidCatch
if (instance !== null && typeof instance.componentDidCatch === 'function') { if (instance !== null && typeof instance.componentDidCatch === 'function') {
if (typeof getDerivedStateFromError !== 'function') { // 没有getDerivedStateFromError if (typeof getDerivedStateFromError !== 'function') {
// 没有getDerivedStateFromError
// 打印错误 // 打印错误
consoleError(error); consoleError(error);
} }

View File

@ -1,4 +1,3 @@
/* /*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* *
@ -42,7 +41,7 @@ export function isExecuting() {
} }
export function copyExecuteMode() { export function copyExecuteMode() {
return {...executeMode}; return { ...executeMode };
} }
export function setExecuteMode(mode: typeof executeMode) { export function setExecuteMode(mode: typeof executeMode) {

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from './Types'; import type { VNode } from './Types';
// 当前处理的classVNode用于设置inst.refs // 当前处理的classVNode用于设置inst.refs
let processingClassVNode: VNode | null = null; let processingClassVNode: VNode | null = null;

View File

@ -16,30 +16,15 @@
import type { VNode } from './Types'; import type { VNode } from './Types';
import type { Update } from './UpdateHandler'; import type { Update } from './UpdateHandler';
import { import { asyncUpdates, syncUpdates, runDiscreteUpdates, launchUpdateFromVNode } from './TreeBuilder';
asyncUpdates,
syncUpdates,
runDiscreteUpdates,
launchUpdateFromVNode,
} from './TreeBuilder';
import { runAsyncEffects } from './submit/HookEffectHandler'; import { runAsyncEffects } from './submit/HookEffectHandler';
import { Callback, newUpdate, pushUpdate } from './UpdateHandler'; import { Callback, newUpdate, pushUpdate } from './UpdateHandler';
export { createVNode, createTreeRootVNode } from './vnode/VNodeCreator'; export { createVNode, createTreeRootVNode } from './vnode/VNodeCreator';
export { createPortal } from './components/CreatePortal'; export { createPortal } from './components/CreatePortal';
export { export { asyncUpdates, syncUpdates, runDiscreteUpdates, runAsyncEffects };
asyncUpdates,
syncUpdates,
runDiscreteUpdates,
runAsyncEffects,
};
export function startUpdate( export function startUpdate(element: any, treeRoot: VNode, callback?: Callback) {
element: any,
treeRoot: VNode,
callback?: Callback,
) {
const update: Update = newUpdate(); const update: Update = newUpdate();
update.content = { element }; update.content = { element };
@ -58,4 +43,3 @@ export function getFirstCustomDom(treeRoot?: VNode | null): Element | Text | nul
} }
return null; return null;
} }

View File

@ -45,12 +45,7 @@ import {
isExecuting, isExecuting,
setExecuteMode, setExecuteMode,
} from './ExecuteMode'; } from './ExecuteMode';
import { import { resetContext, resetNamespaceCtx, setContext, setNamespaceCtx } from './ContextSaver';
resetContext,
resetNamespaceCtx,
setContext,
setNamespaceCtx,
} from './ContextSaver';
import { import {
updateChildShouldUpdate, updateChildShouldUpdate,
updateParentsChildShouldUpdate, updateParentsChildShouldUpdate,
@ -110,7 +105,8 @@ function bubbleVNode(vNode: VNode): void {
do { do {
const parent = node.parent; const parent = node.parent;
if ((node.flags & Interrupted) === InitFlag) { // vNode没有抛出异常 if ((node.flags & Interrupted) === InitFlag) {
// vNode没有抛出异常
componentRenders[node.tag].bubbleRender(node); componentRenders[node.tag].bubbleRender(node);
// 设置node的childShouldUpdate属性 // 设置node的childShouldUpdate属性
@ -133,7 +129,8 @@ function bubbleVNode(vNode: VNode): void {
} }
const siblingVNode = node.next; const siblingVNode = node.next;
if (siblingVNode !== null) { // 有兄弟vNode if (siblingVNode !== null) {
// 有兄弟vNode
processing = siblingVNode; processing = siblingVNode;
return; return;
} }
@ -244,7 +241,8 @@ function buildVNodeTree(treeRoot: VNode) {
// 清空toUpdateNodes // 清空toUpdateNodes
treeRoot.toUpdateNodes.clear(); treeRoot.toUpdateNodes.clear();
if (startVNode.tag !== TreeRoot) { // 不是根节点 if (startVNode.tag !== TreeRoot) {
// 不是根节点
// 设置namespace用于createElement // 设置namespace用于createElement
let parent = startVNode.parent; let parent = startVNode.parent;
while (parent !== null) { while (parent !== null) {
@ -290,7 +288,8 @@ function buildVNodeTree(treeRoot: VNode) {
handleError(treeRoot, thrownValue); handleError(treeRoot, thrownValue);
} }
} }
if (startVNode.tag !== TreeRoot) { // 不是根节点 if (startVNode.tag !== TreeRoot) {
// 不是根节点
// 恢复父节点的context // 恢复父节点的context
resetTreeContext(startVNode); resetTreeContext(startVNode);
} }
@ -308,7 +307,7 @@ function recoverTreeContext(vNode: VNode) {
if (parent.tag === ContextProvider) { if (parent.tag === ContextProvider) {
contextProviders.unshift(parent); contextProviders.unshift(parent);
} }
if(parent.tag === DomPortal){ if (parent.tag === DomPortal) {
pushCurrentRoot(parent); pushCurrentRoot(parent);
} }
parent = parent.parent; parent = parent.parent;
@ -326,7 +325,7 @@ function resetTreeContext(vNode: VNode) {
if (parent.tag === ContextProvider) { if (parent.tag === ContextProvider) {
resetContext(parent); resetContext(parent);
} }
if(parent.tag === DomPortal){ if (parent.tag === DomPortal) {
popCurrentRoot(); popCurrentRoot();
} }
parent = parent.parent; parent = parent.parent;
@ -365,9 +364,7 @@ function renderFromRoot(treeRoot) {
export function tryRenderFromRoot(treeRoot: VNode) { export function tryRenderFromRoot(treeRoot: VNode) {
if (treeRoot.shouldUpdate && treeRoot.task === null) { if (treeRoot.shouldUpdate && treeRoot.task === null) {
// 任务放进queue但是调度开始还是异步的 // 任务放进queue但是调度开始还是异步的
treeRoot.task = pushRenderCallback( treeRoot.task = pushRenderCallback(renderFromRoot.bind(null, treeRoot));
renderFromRoot.bind(null, treeRoot),
);
} }
} }
@ -387,8 +384,11 @@ export function launchUpdateFromVNode(vNode: VNode) {
// 保存待刷新的节点 // 保存待刷新的节点
treeRoot.toUpdateNodes?.add(vNode); treeRoot.toUpdateNodes?.add(vNode);
if (checkMode(BySync) && // 非批量 if (
!checkMode(InRender)) { // 不是渲染阶段触发 checkMode(BySync) && // 非批量
!checkMode(InRender)
) {
// 不是渲染阶段触发
// 业务直接调用Horizon.render的时候会进入这个分支同步渲染。 // 业务直接调用Horizon.render的时候会进入这个分支同步渲染。
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。 // 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。

View File

@ -53,13 +53,7 @@ export function pushUpdate(vNode: VNode, update: Update) {
} }
// 根据update获取新的state // 根据update获取新的state
function calcState( function calcState(vNode: VNode, update: Update, inst: any, oldState: any, props: any): any {
vNode: VNode,
update: Update,
inst: any,
oldState: any,
props: any,
): any {
switch (update.type) { switch (update.type) {
case UpdateState.Override: case UpdateState.Override:
const content = update.content; const content = update.content;
@ -73,9 +67,7 @@ function calcState(
case UpdateState.Update: case UpdateState.Update:
const updateContent = update.content; const updateContent = update.content;
const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent; const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent;
return (newState === null || newState === undefined) return newState === null || newState === undefined ? oldState : { ...oldState, ...newState };
? oldState
: { ...oldState, ...newState };
default: default:
return oldState; return oldState;
} }
@ -118,7 +110,6 @@ export function processUpdates(vNode: VNode, inst: any, props: any): void {
calcUpdates(vNode, props, inst, toProcessUpdates); calcUpdates(vNode, props, inst, toProcessUpdates);
} }
} }
} }
export function pushForceUpdate(vNode: VNode) { export function pushForceUpdate(vNode: VNode) {

View File

@ -13,14 +13,10 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {TYPE_PORTAL} from '../../external/JSXElementType'; import { TYPE_PORTAL } from '../../external/JSXElementType';
import type {PortalType} from '../Types'; import type { PortalType } from '../Types';
export function createPortal( export function createPortal(children: any, realNode: any, key = ''): PortalType {
children: any,
realNode: any,
key: string = '',
): PortalType {
return { return {
vtype: TYPE_PORTAL, vtype: TYPE_PORTAL,
key: key == '' ? '' : '' + key, key: key == '' ? '' : '' + key,

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {RefType} from '../Types'; import type { RefType } from '../Types';
export function createRef(): RefType { export function createRef(): RefType {
return { return {

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {TYPE_FORWARD_REF} from '../../external/JSXElementType'; import { TYPE_FORWARD_REF } from '../../external/JSXElementType';
export function forwardRef(render: Function) { export function forwardRef(render: Function) {
return { return {

View File

@ -13,9 +13,9 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {PromiseType} from '../Types'; import type { PromiseType } from '../Types';
import {TYPE_LAZY} from '../../external/JSXElementType'; import { TYPE_LAZY } from '../../external/JSXElementType';
enum LayStatus { enum LayStatus {
UnProcessed = 'UnProcessed', UnProcessed = 'UnProcessed',
@ -26,7 +26,7 @@ enum LayStatus {
type LazyContent<T> = { type LazyContent<T> = {
_status: string; _status: string;
_value: () => PromiseType<{default: T}> | PromiseType<T> | T | any; _value: () => PromiseType<{ default: T }> | PromiseType<T> | T | any;
}; };
export type LazyComponent<T, P> = { export type LazyComponent<T, P> = {
@ -59,7 +59,7 @@ function lazyLoader<T>(lazyContent: LazyContent<T>): any {
lazyContent._status = LayStatus.Rejected; lazyContent._status = LayStatus.Rejected;
lazyContent._value = error; lazyContent._value = error;
} }
}, }
); );
} }
if (lazyContent._status === LayStatus.Fulfilled) { if (lazyContent._status === LayStatus.Fulfilled) {
@ -69,7 +69,7 @@ function lazyLoader<T>(lazyContent: LazyContent<T>): any {
} }
} }
export function lazy<T>(promiseCtor: () => PromiseType<{default: T}>): LazyComponent<T, LazyContent<T>> { export function lazy<T>(promiseCtor: () => PromiseType<{ default: T }>): LazyComponent<T, LazyContent<T>> {
return { return {
vtype: TYPE_LAZY, vtype: TYPE_LAZY,
_content: { _content: {

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {TYPE_MEMO} from '../../external/JSXElementType'; import { TYPE_MEMO } from '../../external/JSXElementType';
export function memo<Props>(type, compare?: (oldProps: Props, newProps: Props) => boolean) { export function memo<Props>(type, compare?: (oldProps: Props, newProps: Props) => boolean) {
return { return {

View File

@ -13,9 +13,9 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode, ContextType} from '../../Types'; import type { VNode, ContextType } from '../../Types';
import {getHookStage} from '../../hooks/HookStage'; import { getHookStage } from '../../hooks/HookStage';
import {throwNotInFuncError} from '../../hooks/BaseHook'; import { throwNotInFuncError } from '../../hooks/BaseHook';
// 重置依赖 // 重置依赖
export function resetDepContexts(vNode: VNode): void { export function resetDepContexts(vNode: VNode): void {
@ -35,7 +35,7 @@ function collectDeps<T>(vNode: VNode, context: ContextType<T>) {
} }
} }
export function getNewContext<T>(vNode: VNode, ctx: ContextType<T>, isUseContext: boolean = false): T { export function getNewContext<T>(vNode: VNode, ctx: ContextType<T>, isUseContext = false): T {
// 如果来自于useContext则需要在函数组件中调用 // 如果来自于useContext则需要在函数组件中调用
if (isUseContext && getHookStage() === null) { if (isUseContext && getHookStage() === null) {
throwNotInFuncError(); throwNotInFuncError();

View File

@ -13,8 +13,8 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {ContextType} from '../../Types'; import type { ContextType } from '../../Types';
import {TYPE_PROVIDER, TYPE_CONTEXT} from '../../../external/JSXElementType'; import { TYPE_PROVIDER, TYPE_CONTEXT } from '../../../external/JSXElementType';
export function createContext<T>(val: T): ContextType<T> { export function createContext<T>(val: T): ContextType<T> {
const context: ContextType<T> = { const context: ContextType<T> = {

View File

@ -18,7 +18,7 @@ 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) =>
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';

View File

@ -24,13 +24,7 @@ import {
createPortalVNode, createPortalVNode,
createDomTextVNode, createDomTextVNode,
} from '../vnode/VNodeCreator'; } from '../vnode/VNodeCreator';
import { import { isSameType, getIteratorFn, isTextType, isIteratorType, isObjectType } from './DiffTools';
isSameType,
getIteratorFn,
isTextType,
isIteratorType,
isObjectType,
} from './DiffTools';
import { travelChildren } from '../vnode/VNodeUtils'; import { travelChildren } from '../vnode/VNodeUtils';
import { markVNodePath } from '../utils/vNodePath'; import { markVNodePath } from '../utils/vNodePath';
@ -120,10 +114,12 @@ function getNodeType(newChild: any): string | null {
function setVNodeAdditionFlag(newNode: VNode, lastPosition: number): number { function setVNodeAdditionFlag(newNode: VNode, lastPosition: number): number {
let position = lastPosition; let position = lastPosition;
if (newNode.isCreated || newNode.eIndex < lastPosition) { // 位置 小于 上一个复用的位置 if (newNode.isCreated || newNode.eIndex < lastPosition) {
// 位置 小于 上一个复用的位置
// 标记为新增 // 标记为新增
FlagUtils.setAddition(newNode); FlagUtils.setAddition(newNode);
} else { // 复用 } else {
// 复用
position = newNode.eIndex; position = newNode.eIndex;
} }
@ -206,15 +202,16 @@ function transRightChildrenToArray(child) {
return rightChildrenArray; return rightChildrenArray;
} }
function transLeftChildrenToMap( function transLeftChildrenToMap(startChild: VNode, rightEndVNode: VNode | null): Map<string | number, VNode> {
startChild: VNode,
rightEndVNode: VNode | null,
): Map<string | number, VNode> {
const leftChildrenMap: Map<string | number, VNode> = new Map(); const leftChildrenMap: Map<string | number, VNode> = new Map();
travelChildren(startChild, node => { travelChildren(
leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node); startChild,
}, node => node === rightEndVNode); node => {
leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node);
},
node => node === rightEndVNode
);
return leftChildrenMap; return leftChildrenMap;
} }
@ -235,11 +232,7 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
} }
// diff数组类型的节点核心算法 // diff数组类型的节点核心算法
function diffArrayNodesHandler( function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array<any>): VNode | null {
parentNode: VNode,
firstChild: VNode | null,
newChildren: Array<any>,
): VNode | null {
let resultingFirstChild: VNode | null = null; let resultingFirstChild: VNode | null = null;
let prevNewNode: VNode | null = null; let prevNewNode: VNode | null = null;
@ -366,13 +359,14 @@ function diffArrayNodesHandler(
// 4. 新节点还有一部分,但是老节点已经没有了 // 4. 新节点还有一部分,但是老节点已经没有了
if (oldNode === null) { if (oldNode === null) {
let isDirectAdd = false; let isDirectAdd = false;
// TODO: 是否可以扩大至非dom类型节点 // TODO: 是否可以扩大至非dom类型节点
// 如果dom节点在上次添加前没有节点说明本次添加时可以直接添加到最后不需要通过 getSiblingDom 函数找到 before 节点 // 如果dom节点在上次添加前没有节点说明本次添加时可以直接添加到最后不需要通过 getSiblingDom 函数找到 before 节点
if (parentNode.tag === DomComponent && if (
parentNode.tag === DomComponent &&
parentNode.oldProps?.children?.length === 0 && parentNode.oldProps?.children?.length === 0 &&
rightIdx - leftIdx === newChildren.length) { rightIdx - leftIdx === newChildren.length
) {
isDirectAdd = true; isDirectAdd = true;
} }
const isAddition = parentNode.tag === DomPortal || !parentNode.isCreated; const isAddition = parentNode.tag === DomPortal || !parentNode.isCreated;
@ -424,7 +418,8 @@ function diffArrayNodesHandler(
const eIndex = newNode.eIndex; const eIndex = newNode.eIndex;
eIndexes.push(eIndex); eIndexes.push(eIndex);
last = eIndexes[result[result.length - 1]]; last = eIndexes[result[result.length - 1]];
if (eIndex > last || last === undefined) { // 大的 eIndex直接放在最后 if (eIndex > last || last === undefined) {
// 大的 eIndex直接放在最后
preIndex[i] = result[result.length - 1]; preIndex[i] = result[result.length - 1];
result.push(i); result.push(i);
} else { } else {
@ -500,7 +495,7 @@ function setVNodesCIndex(startChild: VNode | null, startIdx: number) {
function diffIteratorNodesHandler( function diffIteratorNodesHandler(
parentNode: VNode, parentNode: VNode,
firstChild: VNode | null, firstChild: VNode | null,
newChildrenIterable: Iterable<any>, newChildrenIterable: Iterable<any>
): VNode | null { ): VNode | null {
const iteratorFn = getIteratorFn(newChildrenIterable); const iteratorFn = getIteratorFn(newChildrenIterable);
const iteratorObj = iteratorFn.call(newChildrenIterable); const iteratorObj = iteratorFn.call(newChildrenIterable);
@ -517,12 +512,7 @@ function diffIteratorNodesHandler(
} }
// 新节点是字符串类型 // 新节点是字符串类型
function diffStringNodeHandler( function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode, isComparing: boolean) {
parentNode: VNode,
newChild: any,
firstChildVNode: VNode,
isComparing: boolean
) {
let newTextNode: VNode | null = null; let newTextNode: VNode | null = null;
// 第一个vNode是Text则复用 // 第一个vNode是Text则复用

View File

@ -14,7 +14,7 @@
*/ */
import type { Hook } from './HookType'; import type { Hook } from './HookType';
import {getProcessingVNode} from '../GlobalVar'; import { getProcessingVNode } from '../GlobalVar';
// lastTimeHook是上一次执行func时产生的hooks中与currentHook对应的hook // lastTimeHook是上一次执行func时产生的hooks中与currentHook对应的hook
let lastTimeHook: Hook<any, any> | null = null; let lastTimeHook: Hook<any, any> | null = null;
@ -35,9 +35,7 @@ export function setCurrentHook(hook: Hook<any, any> | null) {
} }
export function throwNotInFuncError() { export function throwNotInFuncError() {
throw Error( throw Error('Hooks should be used inside function component.');
'Hooks should be used inside function component.',
);
} }
// 新建一个hook并放到vNode.hooks中 // 新建一个hook并放到vNode.hooks中
@ -63,9 +61,8 @@ export function getNextHook(hook: Hook<any, any>, hooks: Array<Hook<any, any>>)
// 原因1.比对hook的数量有没有变化非必要2.从上一次执行中的hook获取removeEffect // 原因1.比对hook的数量有没有变化非必要2.从上一次执行中的hook获取removeEffect
export function getCurrentHook(): Hook<any, any> { export function getCurrentHook(): Hook<any, any> {
const processingVNode = getProcessingVNode(); const processingVNode = getProcessingVNode();
currentHook = currentHook !== null ? currentHook =
getNextHook(currentHook, processingVNode.hooks) : currentHook !== null ? getNextHook(currentHook, processingVNode.hooks) : processingVNode.hooks[0] || null;
(processingVNode.hooks[0] || null);
if (lastTimeHook !== null) { if (lastTimeHook !== null) {
lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks); lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks);

View File

@ -13,39 +13,32 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {ContextType} from '../Types'; import type { ContextType } from '../Types';
import {useRefImpl} from './UseRefHook'; import { useRefImpl } from './UseRefHook';
import {useEffectImpl, useLayoutEffectImpl} from './UseEffectHook'; import { useEffectImpl, useLayoutEffectImpl } from './UseEffectHook';
import {useCallbackImpl} from './UseCallbackHook'; import { useCallbackImpl } from './UseCallbackHook';
import {useMemoImpl} from './UseMemoHook'; import { useMemoImpl } from './UseMemoHook';
import {useImperativeHandleImpl} from './UseImperativeHook'; import { useImperativeHandleImpl } from './UseImperativeHook';
import {useReducerImpl} from './UseReducerHook'; import { useReducerImpl } from './UseReducerHook';
import {useStateImpl} from './UseStateHook'; import { useStateImpl } from './UseStateHook';
import {getNewContext} from '../components/context/Context'; import { getNewContext } from '../components/context/Context';
import {getProcessingVNode} from '../GlobalVar'; import { getProcessingVNode } from '../GlobalVar';
import {Ref, Trigger} from './HookType'; import { Ref, Trigger } from './HookType';
type BasicStateAction<S> = ((S) => S) | S; type BasicStateAction<S> = ((S) => S) | S;
type Dispatch<A> = (A) => void; type Dispatch<A> = (A) => void;
export function useContext<T>(Context: ContextType<T>): T {
export function useContext<T>(
Context: ContextType<T>,
): T {
const processingVNode = getProcessingVNode(); const processingVNode = getProcessingVNode();
return getNewContext(processingVNode!, Context, true); return getNewContext(processingVNode!, Context, true);
} }
export function useState<S>(initialState: (() => S) | S,): [S, Dispatch<BasicStateAction<S>>] { export function useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>] {
return useStateImpl(initialState); return useStateImpl(initialState);
} }
export function useReducer<S, I, A>( export function useReducer<S, I, A>(reducer: (S, A) => S, initialArg: I, init?: (I) => S): [S, Trigger<A>] | void {
reducer: (S, A) => S,
initialArg: I,
init?: (I) => S,
): [S, Trigger<A>] | void {
return useReducerImpl(reducer, initialArg, init); return useReducerImpl(reducer, initialArg, init);
} }
@ -53,38 +46,26 @@ export function useRef<T>(initialValue: T): Ref<T> {
return useRefImpl(initialValue); return useRefImpl(initialValue);
} }
export function useEffect( export function useEffect(create: () => (() => void) | void, deps?: Array<any> | null): void {
create: () => (() => void) | void,
deps?: Array<any> | null,
): void {
return useEffectImpl(create, deps); return useEffectImpl(create, deps);
} }
export function useLayoutEffect( export function useLayoutEffect(create: () => (() => void) | void, deps?: Array<any> | null): void {
create: () => (() => void) | void,
deps?: Array<any> | null,
): void {
return useLayoutEffectImpl(create, deps); return useLayoutEffectImpl(create, deps);
} }
export function useCallback<T>( export function useCallback<T>(callback: T, deps?: Array<any> | null): T {
callback: T,
deps?: Array<any> | null,
): T {
return useCallbackImpl(callback, deps); return useCallbackImpl(callback, deps);
} }
export function useMemo<T>( export function useMemo<T>(create: () => T, deps?: Array<any> | null): T {
create: () => T,
deps?: Array<any> | null,
): T {
return useMemoImpl(create, deps); return useMemoImpl(create, deps);
} }
export function useImperativeHandle<T>( export function useImperativeHandle<T>(
ref: {current: T | null} | ((inst: T | null) => any) | null | void, ref: { current: T | null } | ((inst: T | null) => any) | null | void,
create: () => T, create: () => T,
deps?: Array<any> | null, deps?: Array<any> | null
): void { ): void {
return useImperativeHandleImpl(ref, create, deps); return useImperativeHandleImpl(ref, create, deps);
} }

View File

@ -13,21 +13,17 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import { import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook';
getLastTimeHook, import { HookStage, setHookStage } from './HookStage';
setLastTimeHook,
setCurrentHook, getNextHook
} from './BaseHook';
import {HookStage, setHookStage} from './HookStage';
// hook对外入口 // hook对外入口
export function runFunctionWithHooks<Props extends Record<string, any>, Arg>( export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
funcComp: (props: Props, arg: Arg) => any, funcComp: (props: Props, arg: Arg) => any,
props: Props, props: Props,
arg: Arg, arg: Arg,
processing: VNode, processing: VNode
) { ) {
// 重置全局变量 // 重置全局变量
resetGlobalVariable(); resetGlobalVariable();
@ -67,4 +63,3 @@ function resetGlobalVariable() {
setLastTimeHook(null); setLastTimeHook(null);
setCurrentHook(null); setCurrentHook(null);
} }

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {EffectConstant} from './EffectConstant'; import { EffectConstant } from './EffectConstant';
type ValueOf<T> = T[keyof T]; type ValueOf<T> = T[keyof T];
export interface Hook<S, A> { export interface Hook<S, A> {
state: Reducer<S, A> | Effect | Memo<S> | CallBack<S> | Ref<S>; state: Reducer<S, A> | Effect | Memo<S> | CallBack<S> | Ref<S>;

View File

@ -13,13 +13,9 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { createHook, getCurrentHook, throwNotInFuncError } from './BaseHook';
createHook, import { getHookStage, HookStage } from './HookStage';
getCurrentHook, import { isArrayEqual } from '../utils/compare';
throwNotInFuncError
} from './BaseHook';
import {getHookStage, HookStage} from './HookStage';
import {isArrayEqual} from '../utils/compare';
export function useCallbackImpl<F>(func: F, dependencies?: Array<any> | null): F { export function useCallbackImpl<F>(func: F, dependencies?: Array<any> | null): F {
const stage = getHookStage(); const stage = getHookStage();
@ -31,7 +27,7 @@ export function useCallbackImpl<F>(func: F, dependencies?: Array<any> | null): F
const deps = dependencies !== undefined ? dependencies : null; const deps = dependencies !== undefined ? dependencies : null;
if (stage === HookStage.Init) { if (stage === HookStage.Init) {
hook = createHook(); hook = createHook();
hook.state = {func, dependencies: deps}; hook.state = { func, dependencies: deps };
} else if (stage === HookStage.Update) { } else if (stage === HookStage.Update) {
hook = getCurrentHook(); hook = getCurrentHook();
@ -40,7 +36,7 @@ export function useCallbackImpl<F>(func: F, dependencies?: Array<any> | null): F
if (lastState !== null && deps !== null && isArrayEqual(deps, lastState.dependencies)) { if (lastState !== null && deps !== null && isArrayEqual(deps, lastState.dependencies)) {
return lastState.func; return lastState.func;
} }
hook.state = {func, dependencies: deps}; hook.state = { func, dependencies: deps };
} }
return func; return func;

View File

@ -13,20 +13,15 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { createHook, getCurrentHook, getLastTimeHook, throwNotInFuncError } from './BaseHook';
createHook, import { FlagUtils } from '../vnode/VNodeFlags';
getCurrentHook, import { EffectConstant } from './EffectConstant';
getLastTimeHook, import type { Effect, EffectList } from './HookType';
throwNotInFuncError import { getHookStage, HookStage } from './HookStage';
} from './BaseHook'; import { isArrayEqual } from '../utils/compare';
import {FlagUtils} from '../vnode/VNodeFlags'; import { getProcessingVNode } from '../GlobalVar';
import {EffectConstant} from './EffectConstant';
import type {Effect, EffectList} from './HookType';
import {getHookStage, HookStage} from './HookStage';
import {isArrayEqual} from '../utils/compare';
import {getProcessingVNode} from '../GlobalVar';
export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null,): void { export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void {
// 异步触发的effect // 异步触发的effect
useEffect(effectFunc, deps, EffectConstant.Effect); useEffect(effectFunc, deps, EffectConstant.Effect);
} }
@ -36,11 +31,7 @@ export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?
useEffect(effectFunc, deps, EffectConstant.LayoutEffect); useEffect(effectFunc, deps, EffectConstant.LayoutEffect);
} }
function useEffect( function useEffect(effectFunc: () => (() => void) | void, deps: Array<any> | void | null, effectType: number): void {
effectFunc: () => (() => void) | void,
deps: Array<any> | void | null,
effectType: number
): void {
const stage = getHookStage(); const stage = getHookStage();
if (stage === null) { if (stage === null) {
throwNotInFuncError(); throwNotInFuncError();

View File

@ -13,15 +13,15 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {useLayoutEffectImpl} from './UseEffectHook'; import { useLayoutEffectImpl } from './UseEffectHook';
import {getHookStage} from './HookStage'; import { getHookStage } from './HookStage';
import {throwNotInFuncError} from './BaseHook'; import { throwNotInFuncError } from './BaseHook';
import type {Ref} from './HookType'; import type { Ref } from './HookType';
export function useImperativeHandleImpl<R>( export function useImperativeHandleImpl<R>(
ref: { current: R | null } | ((any) => any) | null | void, ref: { current: R | null } | ((any) => any) | null | void,
func: () => R, func: () => R,
dependencies?: Array<any> | null, dependencies?: Array<any> | null
): void { ): void {
const stage = getHookStage(); const stage = getHookStage();
if (stage === null) { if (stage === null) {
@ -36,10 +36,7 @@ function isNotNull(object: any): boolean {
return object !== null && object !== undefined; return object !== null && object !== undefined;
} }
function effectFunc<R>( function effectFunc<R>(func: () => R, ref: Ref<R> | ((any) => any) | null): (() => void) | void {
func: () => R,
ref: Ref<R> | ((any) => any) | null,
): (() => void) | void {
if (typeof ref === 'function') { if (typeof ref === 'function') {
const value = func(); const value = func();
ref(value); ref(value);

View File

@ -13,15 +13,11 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import { import { createHook, getCurrentHook, throwNotInFuncError } from './BaseHook';
createHook, import { getHookStage, HookStage } from './HookStage';
getCurrentHook, import { isArrayEqual } from '../utils/compare';
throwNotInFuncError
} from './BaseHook';
import {getHookStage, HookStage} from './HookStage';
import {isArrayEqual} from '../utils/compare';
export function useMemoImpl<V>(fun: () => V, deps?: Array<any> | null,): V { export function useMemoImpl<V>(fun: () => V, deps?: Array<any> | null): V {
const stage = getHookStage(); const stage = getHookStage();
if (stage === null) { if (stage === null) {
throwNotInFuncError(); throwNotInFuncError();
@ -45,6 +41,6 @@ export function useMemoImpl<V>(fun: () => V, deps?: Array<any> | null,): V {
result = fun(); result = fun();
} }
hook.state = {result, dependencies: nextDeps}; hook.state = { result, dependencies: nextDeps };
return hook.state.result; return hook.state.result;
} }

View File

@ -137,7 +137,7 @@ function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer):
// 计算stateValue值 // 计算stateValue值
function calculateNewState<S, A>(currentHookUpdates: Array<Update<S, A>>, currentHook, reducer: (S, A) => S) { function calculateNewState<S, A>(currentHookUpdates: Array<Update<S, A>>, currentHook, reducer: (S, A) => S) {
let reducerObj = currentHook.state; const reducerObj = currentHook.state;
let state = reducerObj.stateValue; let state = reducerObj.stateValue;
// 循环遍历更新数组,计算新的状态值 // 循环遍历更新数组,计算新的状态值

View File

@ -13,9 +13,9 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import {createHook, getCurrentHook, throwNotInFuncError} from './BaseHook'; import { createHook, getCurrentHook, throwNotInFuncError } from './BaseHook';
import {getHookStage, HookStage} from './HookStage'; import { getHookStage, HookStage } from './HookStage';
import type {Ref} from './HookType'; import type { Ref } from './HookType';
export function useRefImpl<V>(value: V): Ref<V> { export function useRefImpl<V>(value: V): Ref<V> {
const stage = getHookStage(); const stage = getHookStage();
@ -26,7 +26,7 @@ export function useRefImpl<V>(value: V): Ref<V> {
let hook; let hook;
if (stage === HookStage.Init) { if (stage === HookStage.Init) {
hook = createHook(); hook = createHook();
hook.state = {current: value}; hook.state = { current: value };
} else if (stage === HookStage.Update) { } else if (stage === HookStage.Update) {
hook = getCurrentHook(); hook = getCurrentHook();
} }

View File

@ -13,8 +13,8 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {Trigger} from './HookType'; import type { Trigger } from './HookType';
import {useReducerImpl} from './UseReducerHook'; import { useReducerImpl } from './UseReducerHook';
function defaultReducer<S>(state: S, action: ((S) => S) | S): S { function defaultReducer<S>(state: S, action: ((S) => S) | S): S {
// @ts-ignore // @ts-ignore

View File

@ -30,9 +30,7 @@ import {
} from './class/ClassLifeCycleProcessor'; } from './class/ClassLifeCycleProcessor';
import { FlagUtils, DidCapture } from '../vnode/VNodeFlags'; import { FlagUtils, DidCapture } from '../vnode/VNodeFlags';
import { markRef } from './BaseComponent'; import { markRef } from './BaseComponent';
import { import { processUpdates } from '../UpdateHandler';
processUpdates,
} from '../UpdateHandler';
import { setProcessingClassVNode } from '../GlobalVar'; import { setProcessingClassVNode } from '../GlobalVar';
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
@ -41,9 +39,7 @@ const emptyContextObj = {};
// 获取当前节点的context // 获取当前节点的context
export function getCurrentContext(clazz, processing: VNode) { export function getCurrentContext(clazz, processing: VNode) {
const context = clazz.contextType; const context = clazz.contextType;
return typeof context === 'object' && context !== null return typeof context === 'object' && context !== null ? getNewContext(processing, context) : emptyContextObj;
? getNewContext(processing, context)
: emptyContextObj;
} }
// 挂载实例 // 挂载实例
@ -84,9 +80,7 @@ function createChildren(clazz: any, processing: VNode) {
const isCatchError = (processing.flags & DidCapture) === DidCapture; const isCatchError = (processing.flags & DidCapture) === DidCapture;
// 按照已有规格如果捕获了错误却没有定义getDerivedStateFromError函数返回的child为null // 按照已有规格如果捕获了错误却没有定义getDerivedStateFromError函数返回的child为null
const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function' const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function' ? null : inst.render();
? null
: inst.render();
processing.child = createChildrenByDiff(processing, processing.child, newElements, !processing.isCreated); processing.child = createChildrenByDiff(processing, processing.child, newElements, !processing.isCreated);
return processing.child; return processing.child;
@ -136,7 +130,8 @@ export function captureRender(processing: VNode): VNode | null {
// 挂载新组件,一定会更新 // 挂载新组件,一定会更新
mountInstance(ctor, processing, nextProps); mountInstance(ctor, processing, nextProps);
shouldUpdate = true; shouldUpdate = true;
} else { // 更新 } else {
// 更新
const newContext = getCurrentContext(ctor, processing); const newContext = getCurrentContext(ctor, processing);
// 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props // 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props
@ -150,9 +145,7 @@ export function captureRender(processing: VNode): VNode | null {
processUpdates(processing, inst, nextProps); processUpdates(processing, inst, nextProps);
// 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新 // 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新
shouldUpdate = oldProps !== processing.props || shouldUpdate = oldProps !== processing.props || inst.state !== processing.state || processing.isForceUpdate;
inst.state !== processing.state ||
processing.isForceUpdate;
if (shouldUpdate) { if (shouldUpdate) {
// derivedStateFromProps会修改nextState因此需要调用 // derivedStateFromProps会修改nextState因此需要调用

View File

@ -13,9 +13,9 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode, ContextType} from '../Types'; import type { VNode, ContextType } from '../Types';
import {resetDepContexts, getNewContext} from '../components/context/Context'; import { resetDepContexts, getNewContext } from '../components/context/Context';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
function captureContextConsumer(processing: VNode) { function captureContextConsumer(processing: VNode) {
@ -36,4 +36,3 @@ export function captureRender(processing: VNode): VNode | null {
} }
export function bubbleRender() {} export function bubbleRender() {}

View File

@ -18,10 +18,7 @@ import type { VNode, ContextType, ProviderType } from '../Types';
import { isSame } from '../utils/compare'; import { isSame } from '../utils/compare';
import { ClassComponent, ContextProvider } from '../vnode/VNodeTags'; import { ClassComponent, ContextProvider } from '../vnode/VNodeTags';
import { pushForceUpdate } from '../UpdateHandler'; import { pushForceUpdate } from '../UpdateHandler';
import { import { resetContext, setContext } from '../ContextSaver';
resetContext,
setContext,
} from '../ContextSaver';
import { travelVNodeTree } from '../vnode/VNodeUtils'; import { travelVNodeTree } from '../vnode/VNodeUtils';
import { launchUpdateFromVNode } from '../TreeBuilder'; import { launchUpdateFromVNode } from '../TreeBuilder';
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
@ -62,15 +59,20 @@ function handleContextChange(processing: VNode, context: ContextType<any>): void
let isMatch = false; let isMatch = false;
// 从vNode开始遍历 // 从vNode开始遍历
travelVNodeTree(vNode, node => { travelVNodeTree(
const depContexts = node.depContexts; vNode,
if (depContexts && depContexts.length) { node => {
isMatch = matchDependencies(depContexts, context, node) ?? isMatch; const depContexts = node.depContexts;
} if (depContexts && depContexts.length) {
}, node => isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
// 如果这是匹配的provider则不要更深入地扫描 }
node.tag === ContextProvider && node.type === processing.type },
, processing, null); node =>
// 如果这是匹配的provider则不要更深入地扫描
node.tag === ContextProvider && node.type === processing.type,
processing,
null
);
// 找到了依赖context的子节点触发一次更新 // 找到了依赖context的子节点触发一次更新
if (isMatch) { if (isMatch) {
@ -117,4 +119,3 @@ export function captureRender(processing: VNode): VNode | null {
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
resetContext(processing); resetContext(processing);
} }

View File

@ -16,28 +16,15 @@
import type { VNode } from '../Types'; import type { VNode } from '../Types';
import type { Props } from '../../dom/DOMOperator'; import type { Props } from '../../dom/DOMOperator';
import { import { getNamespaceCtx, setNamespaceCtx, resetNamespaceCtx } from '../ContextSaver';
getNamespaceCtx, import { appendChildElement, newDom, initDomProps, getPropChangeList, isTextChild } from '../../dom/DOMOperator';
setNamespaceCtx,
resetNamespaceCtx,
} from '../ContextSaver';
import {
appendChildElement,
newDom,
initDomProps, getPropChangeList,
isTextChild,
} from '../../dom/DOMOperator';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import { markRef } from './BaseComponent'; import { markRef } from './BaseComponent';
import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags';
import { travelVNodeTree } from '../vnode/VNodeUtils'; import { travelVNodeTree } from '../vnode/VNodeUtils';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
function updateDom( function updateDom(processing: VNode, type: any, newProps: Props) {
processing: VNode,
type: any,
newProps: Props,
) {
// 如果oldProps !== newProps意味着存在更新并且需要处理其相关的副作用 // 如果oldProps !== newProps意味着存在更新并且需要处理其相关的副作用
const oldProps = processing.oldProps; const oldProps = processing.oldProps;
if (oldProps === newProps) { if (oldProps === newProps) {
@ -47,12 +34,7 @@ function updateDom(
const dom: Element = processing.realNode; const dom: Element = processing.realNode;
const changeList = getPropChangeList( const changeList = getPropChangeList(dom, type, oldProps, newProps);
dom,
type,
oldProps,
newProps,
);
// 输入类型的直接标记更新 // 输入类型的直接标记更新
if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') { if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') {
@ -74,11 +56,7 @@ export function bubbleRender(processing: VNode) {
const newProps = processing.props; const newProps = processing.props;
if (!processing.isCreated && processing.realNode != null) { if (!processing.isCreated && processing.realNode != null) {
// 更新dom属性 // 更新dom属性
updateDom( updateDom(processing, type, newProps);
processing,
type,
newProps,
);
if (processing.oldRef !== processing.ref) { if (processing.oldRef !== processing.ref) {
FlagUtils.markRef(processing); FlagUtils.markRef(processing);
@ -87,25 +65,25 @@ export function bubbleRender(processing: VNode) {
const parentNamespace = getNamespaceCtx(); const parentNamespace = getNamespaceCtx();
// 创建dom // 创建dom
const dom = newDom( const dom = newDom(type, newProps, parentNamespace, processing);
type,
newProps,
parentNamespace,
processing,
);
// 把dom类型的子节点append到parent dom中 // 把dom类型的子节点append到parent dom中
const vNode = processing.child; const vNode = processing.child;
if (vNode !== null) { if (vNode !== null) {
// 向下递归它的子节点,查找所有终端节点。 // 向下递归它的子节点,查找所有终端节点。
travelVNodeTree(vNode, node => { travelVNodeTree(
if (node.tag === DomComponent || node.tag === DomText) { vNode,
appendChildElement(dom, node.realNode); node => {
} if (node.tag === DomComponent || node.tag === DomText) {
}, node => appendChildElement(dom, node.realNode);
// 已经append到父节点或者是DomPortal都不需要处理child了 }
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal },
, processing, null); node =>
// 已经append到父节点或者是DomPortal都不需要处理child了
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal,
processing,
null
);
} }
processing.realNode = dom; processing.realNode = dom;

View File

@ -13,11 +13,11 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import {throwIfTrue} from '../utils/throwIfTrue'; import { throwIfTrue } from '../utils/throwIfTrue';
import {newTextDom} from '../../dom/DOMOperator'; import { newTextDom } from '../../dom/DOMOperator';
import {FlagUtils} from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
export function captureRender(): VNode | null { export function captureRender(): VNode | null {
return null; return null;
@ -26,25 +26,24 @@ export function captureRender(): VNode | null {
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
const newText = processing.props; const newText = processing.props;
if (!processing.isCreated && processing.realNode != null) { // 更新 if (!processing.isCreated && processing.realNode != null) {
// 更新
const oldText = processing.oldProps; const oldText = processing.oldProps;
// 如果文本不同,将其标记为更新 // 如果文本不同,将其标记为更新
if (oldText !== newText) { if (oldText !== newText) {
FlagUtils.markUpdate(processing); FlagUtils.markUpdate(processing);
} }
} else { // 初始化 } else {
// 初始化
if (typeof newText !== 'string') { if (typeof newText !== 'string') {
// 如果存在bug可能出现这种情况 // 如果存在bug可能出现这种情况
throwIfTrue( throwIfTrue(
processing.realNode === null, processing.realNode === null,
'We must have new text for new mounted node. This error is likely ' + 'We must have new text for new mounted node. This error is likely ' +
'caused by a bug in Horizon. Please file an issue.', 'caused by a bug in Horizon. Please file an issue.'
); );
} }
// 获得对应节点 // 获得对应节点
processing.realNode = newTextDom( processing.realNode = newTextDom(newText, processing);
newText,
processing,
);
} }
} }

View File

@ -13,12 +13,11 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import {captureRender as funCaptureRender} from './FunctionComponent'; import { captureRender as funCaptureRender } from './FunctionComponent';
export function captureRender(processing: VNode): VNode | null { export function captureRender(processing: VNode): VNode | null {
return funCaptureRender(processing); return funCaptureRender(processing);
} }
export function bubbleRender() {} export function bubbleRender() {}

View File

@ -13,7 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
export function bubbleRender() {} export function bubbleRender() {}

View File

@ -26,14 +26,11 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator';
// 在useState, useReducer的时候会触发state变化 // 在useState, useReducer的时候会触发state变化
let stateChange = false; let stateChange = false;
export function bubbleRender() { export function bubbleRender() {}
}
// 判断children是否可以复用 // 判断children是否可以复用
function checkIfCanReuseChildren(processing: VNode) { function checkIfCanReuseChildren(processing: VNode) {
return !processing.isCreated && return !processing.isCreated && processing.oldProps === processing.props && !processing.isDepContextChange;
processing.oldProps === processing.props &&
!processing.isDepContextChange;
} }
export function setStateChange(isUpdate) { export function setStateChange(isUpdate) {
@ -44,11 +41,7 @@ export function isStateChange() {
return stateChange; return stateChange;
} }
export function captureFunctionComponent( export function captureFunctionComponent(processing: VNode, funcComp: any, nextProps: any) {
processing: VNode,
funcComp: any,
nextProps: any,
) {
// 函数组件内已完成异步动作 // 函数组件内已完成异步动作
if (processing.isSuspended) { if (processing.isSuspended) {
// 由于首次被打断,应仍为首次渲染 // 由于首次被打断,应仍为首次渲染
@ -67,7 +60,7 @@ export function captureFunctionComponent(
processing.tag === ForwardRef ? funcComp.render : funcComp, processing.tag === ForwardRef ? funcComp.render : funcComp,
nextProps, nextProps,
processing.tag === ForwardRef ? processing.ref : undefined, processing.tag === ForwardRef ? processing.ref : undefined,
processing, processing
); );
// 这里需要判断是否可以复用因为函数组件比起其他组件多了context、stateChange、或者store改变了 三个因素 // 这里需要判断是否可以复用因为函数组件比起其他组件多了context、stateChange、或者store改变了 三个因素
@ -86,15 +79,7 @@ export function captureFunctionComponent(
export function captureRender(processing: VNode): VNode | null { 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 = processing.isLazyComponent ? mergeDefaultProps(Component, unresolvedProps) : unresolvedProps;
processing.isLazyComponent
? mergeDefaultProps(Component, unresolvedProps)
: unresolvedProps;
return captureFunctionComponent( return captureFunctionComponent(processing, Component, resolvedProps);
processing,
Component,
resolvedProps,
);
} }

View File

@ -17,18 +17,13 @@ import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import { getLazyVNodeTag } from '../vnode/VNodeCreator'; import { getLazyVNodeTag } from '../vnode/VNodeCreator';
import { import { ClassComponent, ForwardRef, FunctionComponent, MemoComponent } from '../vnode/VNodeTags';
ClassComponent,
ForwardRef,
FunctionComponent,
MemoComponent,
} from '../vnode/VNodeTags';
import { throwIfTrue } from '../utils/throwIfTrue'; import { throwIfTrue } from '../utils/throwIfTrue';
import { captureFunctionComponent } from './FunctionComponent'; import { captureFunctionComponent } from './FunctionComponent';
import { captureRender as captureClassComponent } from './ClassComponent'; import { captureRender as captureClassComponent } from './ClassComponent';
import { captureMemoComponent } from './MemoComponent'; import { captureMemoComponent } from './MemoComponent';
export function bubbleRender() { } export function bubbleRender() {}
const LazyRendererMap = { const LazyRendererMap = {
[FunctionComponent]: captureFunctionComponent, [FunctionComponent]: captureFunctionComponent,
@ -51,11 +46,7 @@ export function mergeDefaultProps(Component: any, props: object): object {
return props; return props;
} }
function captureLazyComponent( function captureLazyComponent(processing, lazyComponent, shouldUpdate) {
processing,
lazyComponent,
shouldUpdate,
) {
if (!processing.isCreated) { if (!processing.isCreated) {
// 每次加载lazy都当作mount来处理 // 每次加载lazy都当作mount来处理
processing.isCreated = true; processing.isCreated = true;
@ -87,9 +78,9 @@ function captureLazyComponent(
throwIfTrue( throwIfTrue(
true, true,
'Element type is invalid. Received a promise that resolves to: %s. ' + 'Element type is invalid. Received a promise that resolves to: %s. ' +
'Lazy element type must resolve to a class or function.%s', 'Lazy element type must resolve to a class or function.%s',
Component, Component,
'', ''
); );
return null; return null;
} }

View File

@ -17,12 +17,7 @@ import type { VNode, PromiseType } from '../Types';
import { FlagUtils, Interrupted } from '../vnode/VNodeFlags'; import { FlagUtils, Interrupted } from '../vnode/VNodeFlags';
import { onlyUpdateChildVNodes, updateVNode, createFragmentVNode } from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes, updateVNode, createFragmentVNode } from '../vnode/VNodeCreator';
import { import { ClassComponent, ForwardRef, FunctionComponent, SuspenseComponent } from '../vnode/VNodeTags';
ClassComponent,
ForwardRef,
FunctionComponent,
SuspenseComponent,
} from '../vnode/VNodeTags';
import { pushForceUpdate } from '../UpdateHandler'; import { pushForceUpdate } from '../UpdateHandler';
import { launchUpdateFromVNode, tryRenderFromRoot } from '../TreeBuilder'; import { launchUpdateFromVNode, tryRenderFromRoot } from '../TreeBuilder';
import { updateShouldUpdateOfTree } from '../vnode/VNodeShouldUpdate'; import { updateShouldUpdateOfTree } from '../vnode/VNodeShouldUpdate';
@ -141,11 +136,7 @@ function updateFallback(processing: VNode): Array<VNode> | VNode | null {
} }
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null { export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null {
if ( if (!processing.isCreated && processing.oldProps === processing.props && !shouldUpdate) {
!processing.isCreated &&
processing.oldProps === processing.props &&
!shouldUpdate
) {
if (processing.suspenseState.childStatus === SuspenseChildStatus.ShowFallback) { if (processing.suspenseState.childStatus === SuspenseChildStatus.ShowFallback) {
// 当显示fallback时suspense的子组件要更新 // 当显示fallback时suspense的子组件要更新
return updateFallback(processing); return updateFallback(processing);
@ -158,8 +149,9 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array<V
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
const { childStatus, oldChildStatus } = processing.suspenseState; const { childStatus, oldChildStatus } = processing.suspenseState;
if (childStatus === SuspenseChildStatus.ShowFallback if (
|| (!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback) childStatus === SuspenseChildStatus.ShowFallback ||
(!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback)
) { ) {
FlagUtils.markUpdate(processing); FlagUtils.markUpdate(processing);
} }
@ -183,7 +175,6 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
} }
vNode.suspenseState.promiseSet.add(promise); vNode.suspenseState.promiseSet.add(promise);
// 移除生命周期flag 和 中断flag // 移除生命周期flag 和 中断flag
FlagUtils.removeLifecycleEffectFlags(processing); FlagUtils.removeLifecycleEffectFlags(processing);
FlagUtils.removeFlag(processing, Interrupted); FlagUtils.removeFlag(processing, Interrupted);

View File

@ -13,11 +13,11 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import {throwIfTrue} from '../utils/throwIfTrue'; import { throwIfTrue } from '../utils/throwIfTrue';
import {processUpdates} from '../UpdateHandler'; import { processUpdates } from '../UpdateHandler';
import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver'; import { resetNamespaceCtx, setNamespaceCtx } from '../ContextSaver';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
@ -31,8 +31,8 @@ function updateTreeRoot(processing) {
throwIfTrue( throwIfTrue(
processing.isCreated || updates === null, processing.isCreated || updates === null,
'If the root does not have an updates, we should have already ' + 'If the root does not have an updates, we should have already ' +
'bailed out. This error is likely caused by a bug. Please ' + 'bailed out. This error is likely caused by a bug. Please ' +
'file an issue.', 'file an issue.'
); );
const newProps = processing.props; const newProps = processing.props;

View File

@ -17,12 +17,7 @@ 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, newUpdate, UpdateState, processUpdates } from '../../UpdateHandler';
pushUpdate,
newUpdate,
UpdateState,
processUpdates,
} from '../../UpdateHandler';
import { launchUpdateFromVNode } from '../../TreeBuilder'; import { launchUpdateFromVNode } from '../../TreeBuilder';
import { FlagUtils } from '../../vnode/VNodeFlags'; import { FlagUtils } from '../../vnode/VNodeFlags';
import { getCurrentContext } from '../ClassComponent'; import { getCurrentContext } from '../ClassComponent';
@ -31,7 +26,7 @@ import { PureComponent } from '../../components/BaseClassComponent';
export function callDerivedStateFromProps( export function callDerivedStateFromProps(
processing: VNode, processing: VNode,
getDerivedStateFromProps: (props: object, state: object) => object, getDerivedStateFromProps: (props: object, state: object) => object,
nextProps: object, nextProps: object
) { ) {
if (getDerivedStateFromProps) { if (getDerivedStateFromProps) {
const oldState = processing.state; const oldState = processing.state;
@ -40,9 +35,7 @@ export function callDerivedStateFromProps(
const newState = getDerivedStateFromProps(nextProps, oldState); const newState = getDerivedStateFromProps(nextProps, oldState);
// 组件未返回state,需要返回旧的preState // 组件未返回state,需要返回旧的preState
processing.state = newState === null || newState === undefined processing.state = newState === null || newState === undefined ? oldState : { ...oldState, ...newState };
? oldState
: { ...oldState, ...newState };
} }
} }
@ -68,7 +61,7 @@ export function callShouldComponentUpdate(
oldProps: object, oldProps: object,
newProps: object, newProps: object,
newState: object, newState: object,
newContext: object, newContext: object
) { ) {
const inst = processing.realNode; const inst = processing.realNode;
@ -169,4 +162,3 @@ export function markComponentDidUpdate(processing: VNode) {
FlagUtils.markUpdate(processing); FlagUtils.markUpdate(processing);
} }
} }

View File

@ -44,9 +44,7 @@ import {
SuspenseComponent, SuspenseComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
export { export { BaseComponentRender };
BaseComponentRender,
};
export default { export default {
[ClassComponent]: ClassComponentRender, [ClassComponent]: ClassComponentRender,

View File

@ -17,21 +17,16 @@
* useEffect useLayoutEffect的执行逻辑 * useEffect useLayoutEffect的执行逻辑
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import type { import type { Effect as HookEffect, EffectList } from '../hooks/HookType';
Effect as HookEffect, import { runAsync } from '../taskExecutor/TaskExecutor';
EffectList, import { copyExecuteMode, InRender, setExecuteMode, changeMode } from '../ExecuteMode';
} from '../hooks/HookType'; import { EffectConstant } from '../hooks/EffectConstant';
import {runAsync} from '../taskExecutor/TaskExecutor';
import {
copyExecuteMode, InRender, setExecuteMode,changeMode
} from '../ExecuteMode';
import {EffectConstant} from '../hooks/EffectConstant';
let hookEffects: Array<HookEffect | VNode> = []; let hookEffects: Array<HookEffect | VNode> = [];
let hookRemoveEffects: Array<HookEffect | VNode> = []; let hookRemoveEffects: Array<HookEffect | VNode> = [];
// 是否正在异步调度effects // 是否正在异步调度effects
let isScheduling: boolean = false; let isScheduling = false;
export function setSchedulingEffects(value) { export function setSchedulingEffects(value) {
isScheduling = value; isScheduling = value;
@ -44,7 +39,7 @@ export function callUseEffects(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;
if (effectList !== null) { if (effectList !== null) {
effectList.forEach(effect => { effectList.forEach(effect => {
const {effectConstant} = effect; const { effectConstant } = effect;
if ( if (
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect && (effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect (effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
@ -69,7 +64,7 @@ export function runAsyncEffects() {
// 调用effect destroy // 调用effect destroy
const removeEffects = hookRemoveEffects; const removeEffects = hookRemoveEffects;
hookRemoveEffects = []; hookRemoveEffects = [];
removeEffects.forEach((effect) => { removeEffects.forEach(effect => {
const destroy = effect.removeEffect; const destroy = effect.removeEffect;
effect.removeEffect = undefined; effect.removeEffect = undefined;
@ -85,7 +80,7 @@ export function runAsyncEffects() {
// 调用effect create // 调用effect create
const createEffects = hookEffects; const createEffects = hookEffects;
hookEffects = []; hookEffects = [];
createEffects.forEach((effect) => { createEffects.forEach(effect => {
try { try {
const create = effect.effect; const create = effect.effect;
@ -103,17 +98,19 @@ export function callEffectRemove(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;
if (effectList !== null) { if (effectList !== null) {
effectList.forEach(effect => { effectList.forEach(effect => {
const {removeEffect, effectConstant} = effect; const { removeEffect, effectConstant } = effect;
if (removeEffect !== undefined) { if (removeEffect !== undefined) {
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect就异步调用 if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) {
// 如果是useEffect就异步调用
hookRemoveEffects.push(effect); hookRemoveEffects.push(effect);
if (!isScheduling) { if (!isScheduling) {
isScheduling = true; isScheduling = true;
runAsync(runAsyncEffects); runAsync(runAsyncEffects);
} }
} else { // 是useLayoutEffect直接执行 } else {
// 是useLayoutEffect直接执行
removeEffect(); removeEffect();
} }
} }

View File

@ -50,12 +50,7 @@ import {
callUseLayoutEffectRemove, callUseLayoutEffectRemove,
} from './HookEffectHandler'; } from './HookEffectHandler';
import { handleSubmitError } from '../ErrorHandler'; import { handleSubmitError } from '../ErrorHandler';
import { import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils';
travelVNodeTree,
clearVNode,
isDomVNode,
getSiblingDom,
} 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) {
@ -67,13 +62,10 @@ function callComponentWillUnmount(vNode: VNode, instance: any) {
} }
// 调用界面变化前的生命周期 // 调用界面变化前的生命周期
function callBeforeSubmitLifeCycles( function callBeforeSubmitLifeCycles(vNode: VNode): void {
vNode: VNode, if (vNode.tag === ClassComponent && !vNode.isCreated) {
): void { // 调用instance.getSnapshotBeforeUpdate
if (vNode.tag === ClassComponent && !vNode.isCreated) { // 调用instance.getSnapshotBeforeUpdate const prevProps = vNode.isLazyComponent ? mergeDefaultProps(vNode.type, vNode.oldProps) : vNode.oldProps;
const prevProps = vNode.isLazyComponent
? mergeDefaultProps(vNode.type, vNode.oldProps)
: vNode.oldProps;
const prevState = vNode.oldState; const prevState = vNode.oldState;
const instance = vNode.realNode; const instance = vNode.realNode;
@ -98,9 +90,7 @@ function callStateCallback(vNode: VNode, obj: any): void {
} }
// 调用界面变化后的生命周期 // 调用界面变化后的生命周期
function callAfterSubmitLifeCycles( function callAfterSubmitLifeCycles(vNode: VNode): void {
vNode: VNode,
): void {
switch (vNode.tag) { switch (vNode.tag) {
case FunctionComponent: case FunctionComponent:
case ForwardRef: { case ForwardRef: {
@ -116,17 +106,10 @@ function callAfterSubmitLifeCycles(
if (vNode.isCreated) { if (vNode.isCreated) {
instance.componentDidMount(); instance.componentDidMount();
} else { } else {
const prevProps = const prevProps = vNode.isLazyComponent ? mergeDefaultProps(vNode.type, vNode.oldProps) : vNode.oldProps;
vNode.isLazyComponent
? mergeDefaultProps(vNode.type, vNode.oldProps)
: vNode.oldProps;
const prevState = vNode.oldState; const prevState = vNode.oldState;
instance.componentDidUpdate( instance.componentDidUpdate(prevProps, prevState, instance.__snapshotResult);
prevProps,
prevState,
instance.__snapshotResult,
);
} }
} }
@ -152,17 +135,23 @@ function callAfterSubmitLifeCycles(
} }
function hideOrUnhideAllChildren(vNode, isHidden) { function hideOrUnhideAllChildren(vNode, isHidden) {
travelVNodeTree(vNode, (node: VNode) => { travelVNodeTree(
const instance = node.realNode; vNode,
(node: VNode) => {
const instance = node.realNode;
if (node.tag === DomComponent || node.tag === DomText) { if (node.tag === DomComponent || node.tag === DomText) {
if (isHidden) { if (isHidden) {
hideDom(node.tag, instance); hideDom(node.tag, instance);
} else { } else {
unHideDom(node.tag, instance, node.props); unHideDom(node.tag, instance, node.props);
}
} }
} },
}, null, vNode, null); null,
vNode,
null
);
} }
function attachRef(vNode: VNode) { function attachRef(vNode: VNode) {
@ -172,7 +161,7 @@ function attachRef(vNode: VNode) {
} }
function detachRef(vNode: VNode, isOldRef?: boolean) { function detachRef(vNode: VNode, isOldRef?: boolean) {
let ref = (isOldRef ? vNode.oldRef : vNode.ref); const ref = isOldRef ? vNode.oldRef : vNode.ref;
handleRef(vNode, ref, null); handleRef(vNode, ref, null);
} }
@ -233,12 +222,17 @@ function unmountVNode(vNode: VNode): void {
// 卸载vNode递归遍历子vNode // 卸载vNode递归遍历子vNode
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
node.tag === DomPortal node.tag === DomPortal,
, vNode, null); vNode,
null
);
} }
function submitAddition(vNode: VNode): void { function submitAddition(vNode: VNode): void {
@ -269,11 +263,7 @@ function submitAddition(vNode: VNode): void {
insertOrAppendPlacementNode(vNode, before, parentDom); insertOrAppendPlacementNode(vNode, before, parentDom);
} }
function insertOrAppendPlacementNode( function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void {
node: VNode,
beforeDom: Element | null,
parent: Element | Container,
): void {
const { tag, realNode } = node; const { tag, realNode } = node;
if (isDomVNode(node)) { if (isDomVNode(node)) {
@ -305,42 +295,48 @@ function unmountDomComponents(vNode: VNode): void {
// 这两个变量要一起更新 // 这两个变量要一起更新
let currentParent; let currentParent;
travelVNodeTree(vNode, (node) => { travelVNodeTree(
if (!currentParentIsValid) { vNode,
let parent = node.parent; node => {
let tag; if (!currentParentIsValid) {
while (parent !== null) { let parent = node.parent;
tag = parent.tag; let tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) { while (parent !== null) {
currentParent = parent.realNode; tag = parent.tag;
break; if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
currentParent = parent.realNode;
break;
}
parent = parent.parent;
} }
parent = parent.parent; currentParentIsValid = true;
} }
currentParentIsValid = true;
}
if (node.tag === DomComponent || node.tag === DomText) { if (node.tag === DomComponent || node.tag === DomText) {
// 卸载vNode递归遍历子vNode // 卸载vNode递归遍历子vNode
unmountNestedVNodes(node); unmountNestedVNodes(node);
// 在所有子项都卸载后删除dom树中的节点 // 在所有子项都卸载后删除dom树中的节点
removeChildDom(currentParent, node.realNode); removeChildDom(currentParent, node.realNode);
} else if (node.tag === DomPortal) { } else if (node.tag === DomPortal) {
if (node.child !== null) { if (node.child !== null) {
currentParent = node.realNode; currentParent = node.realNode;
}
} else {
unmountVNode(node);
}
},
node =>
// 如果是dom不用再遍历child
node.tag === DomComponent || node.tag === DomText,
vNode,
node => {
if (node.tag === DomPortal) {
// 当离开portal需要重新设置parent
currentParentIsValid = false;
} }
} else {
unmountVNode(node);
} }
}, node => );
// 如果是dom不用再遍历child
node.tag === DomComponent || node.tag === DomText, vNode, (node) => {
if (node.tag === DomPortal) {
// 当离开portal需要重新设置parent
currentParentIsValid = false;
}
});
} }
function submitClear(vNode: VNode): void { function submitClear(vNode: VNode): void {

View File

@ -13,35 +13,32 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import {FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback} from '../vnode/VNodeFlags'; import { FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback } from '../vnode/VNodeFlags';
import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator'; import { prepareForSubmit, resetAfterSubmit } from '../../dom/DOMOperator';
import {handleSubmitError} from '../ErrorHandler'; import { handleSubmitError } from '../ErrorHandler';
import { import {
attachRef, attachRef,
callAfterSubmitLifeCycles, callAfterSubmitLifeCycles,
callBeforeSubmitLifeCycles, submitDeletion, submitAddition, callBeforeSubmitLifeCycles,
submitResetTextContent, submitUpdate, detachRef, submitClear, submitDeletion,
submitAddition,
submitResetTextContent,
submitUpdate,
detachRef,
submitClear,
} from './LifeCycleHandler'; } from './LifeCycleHandler';
import {tryRenderFromRoot} from '../TreeBuilder'; import { tryRenderFromRoot } from '../TreeBuilder';
import { import { InRender, copyExecuteMode, setExecuteMode, changeMode } from '../ExecuteMode';
InRender, import { isSchedulingEffects, setSchedulingEffects } from './HookEffectHandler';
copyExecuteMode, import { getStartVNode } from '../GlobalVar';
setExecuteMode,
changeMode,
} from '../ExecuteMode';
import {
isSchedulingEffects,
setSchedulingEffects,
} from './HookEffectHandler';
import {getStartVNode} from '../GlobalVar';
let rootThrowError = null; let rootThrowError = null;
// 防止死循环调用update // 防止死循环调用update
const LOOPING_UPDATE_LIMIT = 50; const LOOPING_UPDATE_LIMIT = 50;
let loopingUpdateCount: number = 0; let loopingUpdateCount = 0;
let lastRoot: VNode | null = null; let lastRoot: VNode | null = null;
export function submitToRender(treeRoot) { export function submitToRender(treeRoot) {
@ -104,7 +101,7 @@ export function submitToRender(treeRoot) {
function beforeSubmit(dirtyNodes: Array<VNode>) { function beforeSubmit(dirtyNodes: Array<VNode>) {
let node; let node;
const nodesLength = dirtyNodes.length; const nodesLength = dirtyNodes.length;
for(let i = 0; i < nodesLength; i++) { for (let i = 0; i < nodesLength; i++) {
node = dirtyNodes[i]; node = dirtyNodes[i];
try { try {
if ((node.flags & Snapshot) === Snapshot) { if ((node.flags & Snapshot) === Snapshot) {
@ -123,7 +120,7 @@ function submit(dirtyNodes: Array<VNode>) {
let isUpdate; let isUpdate;
let isDeletion; let isDeletion;
let isClear; let isClear;
for(let i = 0; i < nodesLength; i++) { for (let i = 0; i < nodesLength; i++) {
node = dirtyNodes[i]; node = dirtyNodes[i];
try { try {
if ((node.flags & ResetText) === ResetText) { if ((node.flags & ResetText) === ResetText) {
@ -170,7 +167,7 @@ function submit(dirtyNodes: Array<VNode>) {
function afterSubmit(dirtyNodes: Array<VNode>) { function afterSubmit(dirtyNodes: Array<VNode>) {
let node; let node;
const nodesLength = dirtyNodes.length; const nodesLength = dirtyNodes.length;
for(let i = 0; i < nodesLength; i++) { for (let i = 0; i < nodesLength; i++) {
node = dirtyNodes[i]; node = dirtyNodes[i];
try { try {
if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) { if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) {

View File

@ -21,7 +21,6 @@ let isMessageLoopRunning = false;
let browserCallback = null; let browserCallback = null;
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
export function isOverTime() { export function isOverTime() {
return false; return false;
} }
@ -36,7 +35,8 @@ const callRenderTasks = () => {
// 执行callback // 执行callback
const hasMoreTask = browserCallback(); const hasMoreTask = browserCallback();
if (!hasMoreTask) { // 没有更多task if (!hasMoreTask) {
// 没有更多task
isMessageLoopRunning = false; isMessageLoopRunning = false;
browserCallback = null; browserCallback = null;
} else { } else {
@ -49,7 +49,6 @@ const callRenderTasks = () => {
} }
}; };
port1.onmessage = callRenderTasks; port1.onmessage = callRenderTasks;
export function requestBrowserCallback(callback) { export function requestBrowserCallback(callback) {

View File

@ -17,7 +17,7 @@
* TaskExecutor的异步任务renderQueue来执行同步的渲染callback * TaskExecutor的异步任务renderQueue来执行同步的渲染callback
*/ */
import {runAsync, cancelTask, ImmediatePriority} from './TaskExecutor'; import { runAsync, cancelTask, ImmediatePriority } from './TaskExecutor';
type RenderCallback = () => RenderCallback | null; type RenderCallback = () => RenderCallback | null;
@ -45,7 +45,7 @@ function callRenderQueue() {
try { try {
let callback; let callback;
while (callback = renderQueue.shift()) { while ((callback = renderQueue.shift())) {
callback(); callback();
} }

View File

@ -18,10 +18,7 @@
*/ */
import { Node } from '../taskExecutor/TaskQueue'; import { Node } from '../taskExecutor/TaskQueue';
import { import { requestBrowserCallback, isOverTime } from './BrowserAsync';
requestBrowserCallback,
isOverTime,
} from './BrowserAsync';
import { add, shift, first, remove } from './TaskQueue'; import { add, shift, first, remove } from './TaskQueue';
@ -41,7 +38,7 @@ function callTasks() {
isWaiting = false; isWaiting = false;
isProcessing = true; isProcessing = true;
let task: Node | null= null; let task: Node | null = null;
try { try {
task = first(); task = first();
@ -60,7 +57,8 @@ function callTasks() {
if (task === first()) { if (task === first()) {
shift(); shift();
} else { // 执行任务中可能插入了新任务 } else {
// 执行任务中可能插入了新任务
remove(task); remove(task);
} }
} else { } else {
@ -77,7 +75,7 @@ function callTasks() {
} }
} }
function runAsync(callback, priorityLevel= NormalPriority) { function runAsync(callback, priorityLevel = NormalPriority) {
let increment; let increment;
switch (priorityLevel) { switch (priorityLevel) {
case ImmediatePriority: case ImmediatePriority:
@ -109,9 +107,4 @@ function cancelTask(task) {
task.callback = null; task.callback = null;
} }
export { export { ImmediatePriority, NormalPriority, runAsync, cancelTask };
ImmediatePriority,
NormalPriority,
runAsync,
cancelTask,
};

View File

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

View File

@ -20,7 +20,7 @@ export function throwIfTrue(condition: boolean, errTemplate: string, ...errExpre
// 将%s 替换成对应的变量 // 将%s 替换成对应的变量
const msg = errTemplate.split('%s').reduce((prevSentence: string, part: string, idx: number) => { const msg = errTemplate.split('%s').reduce((prevSentence: string, part: string, idx: number) => {
// %s对应的变量 // %s对应的变量
const expression = idx < errExpressions.length ? errExpressions[idx] : '' ; const expression = idx < errExpressions.length ? errExpressions[idx] : '';
return prevSentence + part + expression; return prevSentence + part + expression;
}, ''); }, '');
throw Error(msg); throw Error(msg);

View File

@ -32,10 +32,10 @@ import {
Profiler, Profiler,
MemoComponent, MemoComponent,
} from './VNodeTags'; } from './VNodeTags';
import type {VNodeTag} from './VNodeTags'; import type { VNodeTag } from './VNodeTags';
import type {RefType, ContextType, SuspenseState, Source} from '../Types'; import type { RefType, ContextType, SuspenseState, Source } from '../Types';
import type {Hook} from '../hooks/HookType'; import type { Hook } from '../hooks/HookType';
import {InitFlag} from './VNodeFlags'; import { InitFlag } from './VNodeFlags';
export class VNode { export class VNode {
tag: VNodeTag; tag: VNodeTag;

View File

@ -19,22 +19,21 @@
import type { VNode } from './VNode'; import type { VNode } from './VNode';
export const InitFlag = /** */ 0;
export const InitFlag = /** */ 0;
// vNode节点的flags // vNode节点的flags
export const DirectAddition = /** */ 1 << 0; // 在本次更新前入股父dom没有子节点说明本次可以直接添加至父节点不需要通过 getSiblingDom 找到 before 节点 export const DirectAddition = /** */ 1 << 0; // 在本次更新前入股父dom没有子节点说明本次可以直接添加至父节点不需要通过 getSiblingDom 找到 before 节点
export const Addition = /** */ 1 << 1; export const Addition = /** */ 1 << 1;
export const Update = /** */ 1 << 2; export const Update = /** */ 1 << 2;
export const Deletion = /** */ 1 << 3; export const Deletion = /** */ 1 << 3;
export const ResetText =/** */ 1 << 4; export const ResetText = /** */ 1 << 4;
export const Callback = /** */ 1 << 5; export const Callback = /** */ 1 << 5;
export const DidCapture =/** */ 1 << 6; export const DidCapture = /** */ 1 << 6;
export const Ref = /** */ 1 << 7; export const Ref = /** */ 1 << 7;
export const Snapshot = /** */ 1 << 8; export const Snapshot = /** */ 1 << 8;
export const Interrupted = /** */ 1 << 9; // 被中断了抛出错误的vNode以及它的父vNode export const Interrupted = /** */ 1 << 9; // 被中断了抛出错误的vNode以及它的父vNode
export const ShouldCapture =/** */ 1 << 11; export const ShouldCapture = /** */ 1 << 11;
export const ForceUpdate = /** */ 1 << 12; // For suspense export const ForceUpdate = /** */ 1 << 12; // For suspense
export const Clear = /** */ 1 << 13; export const Clear = /** */ 1 << 13;
const LifecycleEffectArr = Update | Callback | Ref | Snapshot; const LifecycleEffectArr = Update | Callback | Ref | Snapshot;
export class FlagUtils { export class FlagUtils {
@ -44,7 +43,8 @@ export class FlagUtils {
static removeLifecycleEffectFlags(node) { static removeLifecycleEffectFlags(node) {
node.flags &= ~LifecycleEffectArr; node.flags &= ~LifecycleEffectArr;
} }
static hasAnyFlag(node: VNode) { // 有标志位 static hasAnyFlag(node: VNode) {
// 有标志位
return node.flags !== InitFlag; return node.flags !== InitFlag;
} }
@ -97,4 +97,3 @@ export class FlagUtils {
node.flags |= Clear; node.flags |= Clear;
} }
} }

View File

@ -14,8 +14,8 @@
*/ */
// 从当前节点向上遍历更新shouldUpdate和childShouldUpdate // 从当前节点向上遍历更新shouldUpdate和childShouldUpdate
import {VNode} from './VNode'; import { VNode } from './VNode';
import {TreeRoot} from './VNodeTags'; import { TreeRoot } from './VNodeTags';
export function updateShouldUpdateOfTree(vNode: VNode): VNode | null { export function updateShouldUpdateOfTree(vNode: VNode): VNode | null {
vNode.shouldUpdate = true; vNode.shouldUpdate = true;
@ -70,7 +70,8 @@ export function updateParentsChildShouldUpdate(vNode: VNode) {
let node = vNode.parent; let node = vNode.parent;
const isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate; const isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate;
if (isShouldUpdate) { // 开始节点是shouldUpdate或childShouldUpdate if (isShouldUpdate) {
// 开始节点是shouldUpdate或childShouldUpdate
// 更新从当前节点到根节点的childShouldUpdate为true // 更新从当前节点到根节点的childShouldUpdate为true
setParentsChildShouldUpdate(node); setParentsChildShouldUpdate(node);
} else { } else {

View File

@ -17,11 +17,11 @@
* vNode的 * vNode的
*/ */
import type {VNode} from '../Types'; import type { VNode } from '../Types';
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags'; import { DomComponent, DomPortal, DomText, TreeRoot } from './VNodeTags';
import {isComment} from '../../dom/utils/Common'; import { isComment } from '../../dom/utils/Common';
import {getNearestVNode} from '../../dom/DOMInternalKeys'; import { getNearestVNode } from '../../dom/DOMInternalKeys';
import { Addition, InitFlag } from './VNodeFlags'; import { Addition, InitFlag } from './VNodeFlags';
export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) { export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) {
@ -125,20 +125,22 @@ export function isDomVNode(node: VNode) {
// 是容器类型的vNode // 是容器类型的vNode
function isDomContainer(vNode: VNode): boolean { function isDomContainer(vNode: VNode): boolean {
return ( return vNode.tag === DomComponent || vNode.tag === TreeRoot || vNode.tag === DomPortal;
vNode.tag === DomComponent ||
vNode.tag === TreeRoot ||
vNode.tag === DomPortal
);
} }
export function findDomVNode(vNode: VNode): VNode | null { export function findDomVNode(vNode: VNode): VNode | null {
return travelVNodeTree(vNode, (node) => { return travelVNodeTree(
if (node.tag === DomComponent || node.tag === DomText) { vNode,
return node; node => {
} if (node.tag === DomComponent || node.tag === DomText) {
return null; return node;
}, null, vNode, null); }
return null;
},
null,
vNode,
null
);
} }
export function findDOMByClassInst(inst) { export function findDOMByClassInst(inst) {

View File

@ -4,7 +4,8 @@
"libs/*" "libs/*"
], ],
"scripts": { "scripts": {
"lint": "eslint . --ext .ts", "lint": "eslint . --ext .ts --fix",
"prettier": "prettier -w libs/**/*.ts",
"build": "rollup --config ./scripts/rollup/rollup.config.js", "build": "rollup --config ./scripts/rollup/rollup.config.js",
"build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js", "build:watch": "rollup --watch --config ./scripts/rollup/rollup.config.js",
"build:3rdLib": "node ./scripts/gen3rdLib.js build:3rdLib", "build:3rdLib": "node ./scripts/gen3rdLib.js build:3rdLib",