Match-id-6bdd55632499029ec478f21c7bb9af4d0000a13c
This commit is contained in:
commit
3833d0501a
|
@ -1,3 +1,6 @@
|
||||||
|
## 0.0.24 (2022-10-25)
|
||||||
|
- **core**: fix 修改IE上报Symbol错误的问题
|
||||||
|
|
||||||
## 0.0.23 (2022-09-23)
|
## 0.0.23 (2022-09-23)
|
||||||
- **core**: #86 兼容ReactIs API
|
- **core**: #86 兼容ReactIs API
|
||||||
-
|
-
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"horizon"
|
"horizon"
|
||||||
],
|
],
|
||||||
"version": "0.0.23",
|
"version": "0.0.24",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
"bugs": "",
|
"bugs": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|
|
@ -18,16 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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';
|
||||||
|
@ -43,10 +36,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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',
|
||||||
|
];
|
||||||
|
|
|
@ -13,25 +13,79 @@
|
||||||
* 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,
|
|
||||||
PROPERTY_TYPE,
|
|
||||||
} from '../validators/PropertiesData';
|
|
||||||
import { isInvalidValue } from '../validators/ValidateProps';
|
import { isInvalidValue } from '../validators/ValidateProps';
|
||||||
import { getNamespaceCtx } from '../../renderer/ContextSaver';
|
import { getNamespaceCtx } from '../../renderer/ContextSaver';
|
||||||
import { NSS } from '../utils/DomCreator';
|
import { NSS } from '../utils/DomCreator';
|
||||||
import { getDomTag } from '../utils/Common';
|
import { getDomTag } from '../utils/Common';
|
||||||
|
|
||||||
const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits',
|
// 不需要装换的svg属性集合
|
||||||
'contentScriptType', 'contentStyleType', 'diffuseConstant', 'edgeMode', 'externalResourcesRequired', 'filterRes',
|
const svgHumpAttr = new Set();
|
||||||
'filterUnits', 'glyphRef', 'gradientTransform', 'gradientUnits', 'kernelMatrix', 'kernelUnitLength', 'keyPoints',
|
[
|
||||||
'keySplines', 'keyTimes', 'lengthAdjust', 'limitingConeAngle', 'markerHeight', 'markerUnits', 'markerWidth',
|
'allowReorder',
|
||||||
'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,',
|
'autoReverse',
|
||||||
'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
|
'baseFrequency',
|
||||||
'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
|
'baseProfile',
|
||||||
'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation',
|
'calcMode',
|
||||||
'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY',
|
'clipPathUnits',
|
||||||
'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']);
|
'contentScriptType',
|
||||||
|
'contentStyleType',
|
||||||
|
'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',
|
||||||
|
].forEach((name) => svgHumpAttr.add(name));
|
||||||
|
|
||||||
// 驼峰 变 “-”
|
// 驼峰 变 “-”
|
||||||
function convertToLowerCase(str) {
|
function convertToLowerCase(str) {
|
||||||
|
@ -65,19 +119,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);
|
||||||
|
|
|
@ -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,7 +154,8 @@ 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({
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ 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,7 +61,7 @@ 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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,22 +19,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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键盘事件该函数不能由合成事件调用
|
||||||
|
@ -53,7 +53,7 @@ export class WrappedEvent {
|
||||||
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实例调用
|
||||||
|
|
|
@ -87,4 +87,3 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) {
|
||||||
updateInputValue(inputDom, props);
|
updateInputValue(inputDom, props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
*
|
*
|
||||||
|
@ -16,7 +15,6 @@
|
||||||
|
|
||||||
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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
||||||
*
|
|
||||||
* openGauss is licensed under Mulan PSL v2.
|
|
||||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
* You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
*
|
|
||||||
* http://license.coscl.org.cn/MulanPSL2
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
||||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
||||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the Mulan PSL v2 for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function isObject(obj) {
|
|
||||||
const type = typeof obj;
|
|
||||||
return obj != null && (type === 'object' || type === 'function');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSet(obj) {
|
|
||||||
return obj != null && (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWeakSet(obj) {
|
|
||||||
return obj != null && (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isMap(obj) {
|
|
||||||
return obj != null && (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWeakMap(obj) {
|
|
||||||
return obj != null && (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isArray(obj) {
|
|
||||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isCollection(obj) {
|
|
||||||
return isSet(obj) || isWeakSet(obj) || isMap(obj) || isWeakMap(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isString(obj) {
|
|
||||||
return typeof obj === 'string';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isValidIntegerKey(key) {
|
|
||||||
return isString(key) && key !== 'NaN' && key[0] !== '-' && String(parseInt(key, 10)) === key;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const noop = () => {};
|
|
||||||
|
|
||||||
export function isSame(x, y) {
|
|
||||||
if (!(typeof Object.is === 'function')) {
|
|
||||||
if (x === y) {
|
|
||||||
// +0 != -0
|
|
||||||
return x !== 0 || 1 / x === 1 / y;
|
|
||||||
} else {
|
|
||||||
// NaN == NaN
|
|
||||||
return x !== x && y !== y;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Object.is(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openGauss is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function isObject(obj: any): boolean {
|
||||||
|
const type = typeof obj;
|
||||||
|
return (obj !== null || obj !== undefined) && (type === 'object' || type === 'function');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSet(obj: any): boolean {
|
||||||
|
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Set]' || obj.constructor === Set);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWeakSet(obj: any): boolean {
|
||||||
|
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakSet]' || obj.constructor === WeakSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMap(obj: any): boolean {
|
||||||
|
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object Map]' || obj.constructor === Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWeakMap(obj: any): boolean {
|
||||||
|
return (obj !== null || obj !== undefined) && (Object.prototype.toString.call(obj) === '[object WeakMap]' || obj.constructor === WeakMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isArray(obj: any): boolean {
|
||||||
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCollection(obj: any): boolean {
|
||||||
|
return isSet(obj) || isWeakSet(obj) || isMap(obj) || isWeakMap(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isString(obj: any): boolean {
|
||||||
|
return typeof obj === 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
// key是有效的正整数字的字符串
|
||||||
|
export function isValidIntegerKey(key: any): boolean {
|
||||||
|
return isString(key) && key !== 'NaN' && key[0] !== '-' && String(parseInt(key, 10)) === key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPromise(obj: any): boolean {
|
||||||
|
return isObject(obj) && typeof obj.then === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSame(x, y) {
|
||||||
|
if (!(typeof Object.is === 'function')) {
|
||||||
|
if (x === y) {
|
||||||
|
// +0 != -0
|
||||||
|
return x !== 0 || 1 / x === 1 / y;
|
||||||
|
} else {
|
||||||
|
// NaN == NaN
|
||||||
|
return x !== x && y !== y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Object.is(x, y);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,8 +13,4 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The two constants must be the same as those in horizon.
|
|
||||||
export const FunctionComponent = 'FunctionComponent';
|
|
||||||
export const ClassComponent = 'ClassComponent';
|
|
||||||
|
|
||||||
export const OBSERVER_KEY = '_horizonObserver';
|
export const OBSERVER_KEY = '_horizonObserver';
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
|
|
||||||
import { createStore as createStoreX } from '../store/StoreHandler';
|
import { createStore as createStoreX } from '../store/StoreHandler';
|
||||||
|
|
||||||
import { ReduxStoreHandler } from '../store/StoreHandler';
|
|
||||||
|
|
||||||
export { thunk } from './reduxThunk';
|
export { thunk } from './reduxThunk';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -29,6 +27,14 @@ export {
|
||||||
createDispatchHook,
|
createDispatchHook,
|
||||||
} from './reduxReact';
|
} from './reduxReact';
|
||||||
|
|
||||||
|
export type ReduxStoreHandler = {
|
||||||
|
reducer: (state: any, action: { type: string }) => any;
|
||||||
|
dispatch: (action: { type: string }) => void;
|
||||||
|
getState: () => any;
|
||||||
|
subscribe: (listener: () => void) => () => void;
|
||||||
|
replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export type ReduxAction = {
|
export type ReduxAction = {
|
||||||
type: string;
|
type: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
|
@ -17,9 +17,7 @@
|
||||||
import { useState, useContext, useEffect, useRef } from '../../renderer/hooks/HookExternal';
|
import { useState, useContext, useEffect, useRef } from '../../renderer/hooks/HookExternal';
|
||||||
import { createContext } from '../../renderer/components/context/CreateContext';
|
import { createContext } from '../../renderer/components/context/CreateContext';
|
||||||
import { createElement } from '../../external/JSXElement';
|
import { createElement } from '../../external/JSXElement';
|
||||||
import { BoundActionCreator } from './redux';
|
import type { ReduxStoreHandler, ReduxAction, BoundActionCreator } from './redux';
|
||||||
import { ReduxAction } from './redux';
|
|
||||||
import { ReduxStoreHandler } from '../store/StoreHandler'
|
|
||||||
|
|
||||||
const DefaultContext = createContext(null);
|
const DefaultContext = createContext(null);
|
||||||
type Context = typeof DefaultContext;
|
type Context = typeof DefaultContext;
|
||||||
|
@ -44,7 +42,7 @@ 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);
|
||||||
|
|
||||||
|
@ -60,7 +58,7 @@ 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 +118,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));
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReduxAction, ReduxMiddleware } from './redux';
|
import { ReduxStoreHandler, ReduxAction, ReduxMiddleware } from './redux';
|
||||||
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) =>
|
||||||
|
(next: (action: ReduxAction) => any) =>
|
||||||
|
(
|
||||||
action:
|
action:
|
||||||
| ReduxAction
|
| ReduxAction
|
||||||
| ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any)
|
| ((dispatch: (action: ReduxAction) => void, store: ReduxStoreHandler, extraArgument?: any) => any)
|
||||||
|
|
|
@ -13,20 +13,15 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: implement vNode type
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -49,12 +44,9 @@ export class HooklessObserver implements IObserver {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerUpdate(vNode): void {
|
triggerUpdate(vNode): void {}
|
||||||
}
|
|
||||||
|
|
||||||
allChange(): void {
|
allChange(): void {}
|
||||||
}
|
|
||||||
|
|
||||||
clearByVNode(vNode): void {
|
clearByVNode(vNode): void {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,14 +13,10 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个对象(对象、数组、集合)对应一个Observer
|
|
||||||
*/
|
|
||||||
|
|
||||||
//@ts-ignore
|
|
||||||
import { launchUpdateFromVNode } from '../../renderer/TreeBuilder';
|
import { launchUpdateFromVNode } from '../../renderer/TreeBuilder';
|
||||||
import { getProcessingVNode } from '../../renderer/GlobalVar';
|
import { getProcessingVNode } from '../../renderer/GlobalVar';
|
||||||
import { VNode } from '../../renderer/vnode/VNode';
|
import { VNode } from '../../renderer/vnode/VNode';
|
||||||
|
|
||||||
export interface IObserver {
|
export interface IObserver {
|
||||||
useProp: (key: string) => void;
|
useProp: (key: string) => void;
|
||||||
|
|
||||||
|
@ -39,6 +35,9 @@ export interface IObserver {
|
||||||
clearByVNode: (vNode: any) => void;
|
clearByVNode: (vNode: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个对象(对象、数组、集合)对应一个Observer
|
||||||
|
*/
|
||||||
export class Observer implements IObserver {
|
export class Observer implements IObserver {
|
||||||
vNodeKeys = new WeakMap();
|
vNodeKeys = new WeakMap();
|
||||||
|
|
||||||
|
@ -48,16 +47,18 @@ export class Observer implements IObserver {
|
||||||
|
|
||||||
watchers = {} as { [key: string]: ((key: string, oldValue: any, newValue: any) => void)[] };
|
watchers = {} as { [key: string]: ((key: string, oldValue: any, newValue: any) => void)[] };
|
||||||
|
|
||||||
|
// 对象的属性被使用时调用
|
||||||
useProp(key: string | symbol): void {
|
useProp(key: string | symbol): void {
|
||||||
const processingVNode = getProcessingVNode();
|
const processingVNode = getProcessingVNode();
|
||||||
if (processingVNode === null || !processingVNode.observers) {
|
if (processingVNode === null || !processingVNode.observers) {
|
||||||
|
// 异常场景
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vNode -> Observers
|
// vNode -> Observers
|
||||||
processingVNode.observers.add(this);
|
processingVNode.observers.add(this);
|
||||||
|
|
||||||
// key -> vNodes
|
// key -> vNodes,记录这个prop被哪些VNode使用了
|
||||||
let vNodes = this.keyVNodes.get(key);
|
let vNodes = this.keyVNodes.get(key);
|
||||||
if (!vNodes) {
|
if (!vNodes) {
|
||||||
vNodes = new Set();
|
vNodes = new Set();
|
||||||
|
@ -65,7 +66,7 @@ export class Observer implements IObserver {
|
||||||
}
|
}
|
||||||
vNodes.add(processingVNode);
|
vNodes.add(processingVNode);
|
||||||
|
|
||||||
// vNode -> keys
|
// vNode -> keys,记录这个VNode使用了哪些props
|
||||||
let keys = this.vNodeKeys.get(processingVNode);
|
let keys = this.vNodeKeys.get(processingVNode);
|
||||||
if (!keys) {
|
if (!keys) {
|
||||||
keys = new Set();
|
keys = new Set();
|
||||||
|
@ -74,6 +75,32 @@ export class Observer implements IObserver {
|
||||||
keys.add(key);
|
keys.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对象的属性被赋值时调用
|
||||||
|
setProp(key: string | symbol): void {
|
||||||
|
const vNodes = this.keyVNodes.get(key);
|
||||||
|
vNodes?.forEach((vNode: VNode) => {
|
||||||
|
if (vNode.isStoreChange) {
|
||||||
|
// VNode已经被触发过,不再重复触发
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vNode.isStoreChange = true;
|
||||||
|
|
||||||
|
// 触发vNode更新
|
||||||
|
this.triggerUpdate(vNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.triggerChangeListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerUpdate(vNode: VNode): void {
|
||||||
|
if (!vNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发VNode更新
|
||||||
|
launchUpdateFromVNode(vNode);
|
||||||
|
}
|
||||||
|
|
||||||
addListener(listener: () => void): void {
|
addListener(listener: () => void): void {
|
||||||
this.listeners.push(listener);
|
this.listeners.push(listener);
|
||||||
}
|
}
|
||||||
|
@ -82,34 +109,13 @@ export class Observer implements IObserver {
|
||||||
this.listeners = this.listeners.filter(item => item != listener);
|
this.listeners = this.listeners.filter(item => item != listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
setProp(key: string | symbol): void {
|
|
||||||
const vNodes = this.keyVNodes.get(key);
|
|
||||||
vNodes?.forEach((vNode: VNode) => {
|
|
||||||
if (vNode.isStoreChange) {
|
|
||||||
// update already triggered
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vNode.isStoreChange = true;
|
|
||||||
|
|
||||||
// 触发vNode更新
|
|
||||||
this.triggerUpdate(vNode);
|
|
||||||
});
|
|
||||||
this.triggerChangeListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerChangeListeners(): void {
|
triggerChangeListeners(): void {
|
||||||
this.listeners.forEach(listener => listener());
|
this.listeners.forEach(listener => listener());
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerUpdate(vNode: VNode): void {
|
// 触发所有使用的props的VNode更新
|
||||||
if (!vNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
launchUpdateFromVNode(vNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -117,6 +123,7 @@ export class Observer implements IObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除keyVNodes中保存的这个VNode的关系数据
|
||||||
clearByVNode(vNode: VNode): void {
|
clearByVNode(vNode: VNode): void {
|
||||||
const keys = this.vNodeKeys.get(vNode);
|
const keys = this.vNodeKeys.get(vNode);
|
||||||
if (keys) {
|
if (keys) {
|
||||||
|
|
|
@ -22,16 +22,18 @@ import { createCollectionProxy } from './handlers/CollectionProxyHandler';
|
||||||
import { IObserver } from '../types';
|
import { IObserver } from '../types';
|
||||||
import { OBSERVER_KEY } from '../Constants';
|
import { OBSERVER_KEY } from '../Constants';
|
||||||
|
|
||||||
|
// 保存rawObj -> Proxy
|
||||||
const proxyMap = new WeakMap();
|
const proxyMap = new WeakMap();
|
||||||
|
|
||||||
export const hookObserverMap = new WeakMap();
|
export const hookObserverMap = new WeakMap();
|
||||||
|
|
||||||
export function createProxy(rawObj: any, hookObserver = true): any {
|
export function createProxy(rawObj: any, isHookObserver = true): any {
|
||||||
// 不是对象(是原始数据类型)不用代理
|
// 不是对象(是原始数据类型)不用代理
|
||||||
if (!isObject(rawObj)) {
|
if (!isObject(rawObj)) {
|
||||||
return rawObj;
|
return rawObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已代理过
|
||||||
const existProxy = proxyMap.get(rawObj);
|
const existProxy = proxyMap.get(rawObj);
|
||||||
if (existProxy) {
|
if (existProxy) {
|
||||||
return existProxy;
|
return existProxy;
|
||||||
|
@ -45,15 +47,15 @@ export function createProxy(rawObj: any, hookObserver = true): any {
|
||||||
// 创建Observer
|
// 创建Observer
|
||||||
let observer: IObserver = getObserver(rawObj);
|
let observer: IObserver = getObserver(rawObj);
|
||||||
if (!observer) {
|
if (!observer) {
|
||||||
observer = hookObserver ? new Observer() : new HooklessObserver();
|
observer = isHookObserver ? new Observer() : new HooklessObserver();
|
||||||
rawObj[OBSERVER_KEY] = observer;
|
rawObj[OBSERVER_KEY] = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
hookObserverMap.set(rawObj, hookObserver);
|
hookObserverMap.set(rawObj, isHookObserver);
|
||||||
|
|
||||||
// 创建Proxy
|
// 创建Proxy
|
||||||
let proxyObj;
|
let proxyObj;
|
||||||
if (!hookObserver) {
|
if (!isHookObserver) {
|
||||||
proxyObj = createObjectProxy(rawObj, true);
|
proxyObj = createObjectProxy(rawObj, true);
|
||||||
} else if (isArray(rawObj)) {
|
} else if (isArray(rawObj)) {
|
||||||
// 数组
|
// 数组
|
||||||
|
|
|
@ -44,6 +44,7 @@ function get(rawObj: any[], key: string, receiver: any) {
|
||||||
if (isValidIntegerKey(key) || key === 'length') {
|
if (isValidIntegerKey(key) || key === 'length') {
|
||||||
return objectGet(rawObj, key, receiver);
|
return objectGet(rawObj, key, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Reflect.get(rawObj, key, receiver);
|
return Reflect.get(rawObj, key, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,16 +59,19 @@ function set(rawObj: any[], key: string, value: any, receiver: any) {
|
||||||
const observer = getObserver(rawObj);
|
const observer = getObserver(rawObj);
|
||||||
|
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 触发属性变化
|
||||||
observer.setProp(key);
|
observer.setProp(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldLength !== newLength) {
|
if (oldLength !== newLength) {
|
||||||
|
// 触发数组的大小变化
|
||||||
observer.setProp('length');
|
observer.setProp('length');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ const handler = {
|
||||||
forEach,
|
forEach,
|
||||||
keys,
|
keys,
|
||||||
values,
|
values,
|
||||||
[Symbol.iterator]: forOf,
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']: forOf,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createCollectionProxy(rawObj: Object, hookObserver = true): Object {
|
export function createCollectionProxy(rawObj: Object, hookObserver = true): Object {
|
||||||
|
@ -214,7 +215,8 @@ function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: b
|
||||||
|
|
||||||
return { value: newVal, done };
|
return { value: newVal, done };
|
||||||
},
|
},
|
||||||
[Symbol.iterator]() {
|
// 判断Symbol类型,兼容IE
|
||||||
|
[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']() {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,8 +42,8 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL
|
||||||
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') {
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
|
|
||||||
import { isObject } from '../CommonUtils';
|
import { isObject } from '../CommonUtils';
|
||||||
|
|
||||||
export function readonlyProxy<T extends object>(target: T): ProxyHandler<T> {
|
export function readonlyProxy<T extends object>(rawObj: T): ProxyHandler<T> {
|
||||||
return new Proxy(target, {
|
return new Proxy(rawObj, {
|
||||||
get(target, property, receiver) {
|
get(rawObj, property, receiver) {
|
||||||
const result = Reflect.get(target, property, receiver);
|
const result = Reflect.get(rawObj, property, receiver);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isObject(result)) {
|
if (isObject(result)) {
|
||||||
return readonlyProxy(result);
|
return readonlyProxy(result);
|
||||||
|
|
|
@ -19,37 +19,35 @@ import { getProcessingVNode } from '../../renderer/GlobalVar';
|
||||||
import { createProxy } from '../proxy/ProxyHandler';
|
import { createProxy } from '../proxy/ProxyHandler';
|
||||||
import readonlyProxy from '../proxy/readonlyProxy';
|
import readonlyProxy from '../proxy/readonlyProxy';
|
||||||
import { Observer } from '../proxy/Observer';
|
import { Observer } from '../proxy/Observer';
|
||||||
import { FunctionComponent, ClassComponent } from '../Constants';
|
import { FunctionComponent, ClassComponent } from '../../renderer/vnode/VNodeTags';
|
||||||
import { VNode } from '../../renderer/Types';
|
import { VNode } from '../../renderer/Types';
|
||||||
|
import { isPromise } from '../CommonUtils';
|
||||||
|
|
||||||
const storeMap = new Map<string, StoreHandler<any, any, any>>();
|
const storeMap = new Map<string, StoreHandler<any, any, any>>();
|
||||||
|
|
||||||
function isPromise(obj: any): boolean {
|
|
||||||
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
|
|
||||||
}
|
|
||||||
|
|
||||||
type StoreConfig<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
|
type StoreConfig<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
|
||||||
|
id?: string;
|
||||||
state?: S;
|
state?: S;
|
||||||
actions?: A;
|
actions?: A;
|
||||||
id?: string;
|
|
||||||
computed?: C;
|
computed?: C;
|
||||||
|
options?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReduxStoreHandler = {
|
type UserActions<S extends object> = { [K: string]: ActionFunction<S> };
|
||||||
reducer: (state: any, action: { type: string }) => any;
|
type ActionFunction<S extends object> = (this: StoreHandler<S, any, any>, state: S, ...args: any[]) => any;
|
||||||
dispatch: (action: { type: string }) => void;
|
type StoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: Action<A[K], S> };
|
||||||
getState: () => any;
|
type Action<T extends ActionFunction<any>, S extends object> = (
|
||||||
subscribe: (listener: () => void) => () => void;
|
this: StoreHandler<S, any, any>,
|
||||||
replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void;
|
...args: RemoveFirstFromTuple<Parameters<T>>
|
||||||
};
|
) => ReturnType<T>;
|
||||||
|
|
||||||
type StoreHandler<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
|
type StoreHandler<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = {
|
||||||
$subscribe: (listener: () => void) => void;
|
|
||||||
$unsubscribe: (listener: () => void) => void;
|
|
||||||
$s: S;
|
$s: S;
|
||||||
$queue: QueuedStoreActions<S, A>;
|
|
||||||
$a: StoreActions<S, A>;
|
$a: StoreActions<S, A>;
|
||||||
$c: UserComputedValues<S>;
|
$c: UserComputedValues<S>;
|
||||||
|
$queue: QueuedStoreActions<S, A>;
|
||||||
|
$subscribe: (listener: () => void) => void;
|
||||||
|
$unsubscribe: (listener: () => void) => void;
|
||||||
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
|
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
|
||||||
|
|
||||||
type PlannedAction<S extends object, F extends ActionFunction<S>> = {
|
type PlannedAction<S extends object, F extends ActionFunction<S>> = {
|
||||||
|
@ -63,43 +61,35 @@ type RemoveFirstFromTuple<T extends any[]> = T['length'] extends 0
|
||||||
? I
|
? I
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
type UserActions<S extends object> = { [K: string]: ActionFunction<S> };
|
|
||||||
type UserComputedValues<S extends object> = { [K: string]: ComputedFunction<S> };
|
type UserComputedValues<S extends object> = { [K: string]: ComputedFunction<S> };
|
||||||
|
|
||||||
type ActionFunction<S extends object> = (this: StoreHandler<S, any, any>, state: S, ...args: any[]) => any;
|
|
||||||
type ComputedFunction<S extends object> = (state: S) => any;
|
type ComputedFunction<S extends object> = (state: S) => any;
|
||||||
type Action<T extends ActionFunction<any>, S extends object> = (
|
|
||||||
this: StoreHandler<S, any, any>,
|
|
||||||
...args: RemoveFirstFromTuple<Parameters<T>>
|
|
||||||
) => ReturnType<T>;
|
|
||||||
type AsyncAction<T extends ActionFunction<any>, S extends object> = (
|
type AsyncAction<T extends ActionFunction<any>, S extends object> = (
|
||||||
this: StoreHandler<S, any, any>,
|
this: StoreHandler<S, any, any>,
|
||||||
...args: RemoveFirstFromTuple<Parameters<T>>
|
...args: RemoveFirstFromTuple<Parameters<T>>
|
||||||
) => Promise<ReturnType<T>>;
|
) => Promise<ReturnType<T>>;
|
||||||
|
|
||||||
type StoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: Action<A[K], S> };
|
|
||||||
type QueuedStoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: AsyncAction<A[K], S> };
|
type QueuedStoreActions<S extends object, A extends UserActions<S>> = { [K in keyof A]: AsyncAction<A[K], S> };
|
||||||
type ComputedValues<S extends object, C extends UserComputedValues<S>> = { [K in keyof C]: ReturnType<C[K]> };
|
type ComputedValues<S extends object, C extends UserComputedValues<S>> = { [K in keyof C]: ReturnType<C[K]> };
|
||||||
type PostponedAction = (state: object, ...args: any[]) => Promise<any>;
|
|
||||||
type PostponedActions = { [key: string]: PostponedAction };
|
|
||||||
|
|
||||||
export function createStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
|
export function createStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
|
||||||
config: StoreConfig<S, A, C>
|
storeConfig: StoreConfig<S, A, C>
|
||||||
): () => StoreHandler<S, A, C> {
|
): () => StoreHandler<S, A, C> {
|
||||||
//create a local shalow copy to ensure consistency (if user would change the config object after store creation)
|
|
||||||
config = {
|
|
||||||
id: config.id,
|
|
||||||
options: config.options,
|
|
||||||
state: config.state,
|
|
||||||
actions: config.actions ? { ...config.actions } : undefined,
|
|
||||||
computed: config.computed ? { ...config.computed } : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 校验
|
// 校验
|
||||||
if (Object.prototype.toString.call(config) !== '[object Object]') {
|
if (Object.prototype.toString.call(storeConfig) !== '[object Object]') {
|
||||||
throw new Error('store obj must be pure object');
|
throw new Error('store obj must be pure object');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建本地浅拷贝以确保一致性(避免用户在创建存储后更改配置对象)
|
||||||
|
const config = {
|
||||||
|
id: storeConfig.id,
|
||||||
|
state: storeConfig.state,
|
||||||
|
actions: storeConfig.actions ? { ...storeConfig.actions } : undefined,
|
||||||
|
computed: storeConfig.computed ? { ...storeConfig.computed } : undefined,
|
||||||
|
options: storeConfig.options
|
||||||
|
};
|
||||||
|
|
||||||
const proxyObj = createProxy(config.state, !config.options?.reduxAdapter);
|
const proxyObj = createProxy(config.state, !config.options?.reduxAdapter);
|
||||||
|
|
||||||
proxyObj.$pending = false;
|
proxyObj.$pending = false;
|
||||||
|
@ -116,14 +106,14 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
const $a: Partial<StoreActions<S, A>> = {};
|
const $a: Partial<StoreActions<S, A>> = {};
|
||||||
const $queue: Partial<StoreActions<S, A>> = {};
|
const $queue: Partial<StoreActions<S, A>> = {};
|
||||||
const $c: Partial<ComputedValues<S, C>> = {};
|
const $c: Partial<ComputedValues<S, C>> = {};
|
||||||
const handler = {
|
const storeHandler = {
|
||||||
|
$s: proxyObj,
|
||||||
|
$a: $a as StoreActions<S, A>,
|
||||||
|
$c: $c as ComputedValues<S, C>,
|
||||||
|
$queue: $queue as QueuedStoreActions<S, A>,
|
||||||
|
$config: config,
|
||||||
$subscribe,
|
$subscribe,
|
||||||
$unsubscribe,
|
$unsubscribe,
|
||||||
$a: $a as StoreActions<S, A>,
|
|
||||||
$s: proxyObj,
|
|
||||||
$c: $c as ComputedValues<S, C>,
|
|
||||||
$config: config,
|
|
||||||
$queue: $queue as QueuedStoreActions<S, A>,
|
|
||||||
} as unknown as StoreHandler<S, A, C>;
|
} as unknown as StoreHandler<S, A, C>;
|
||||||
|
|
||||||
function tryNextAction() {
|
function tryNextAction() {
|
||||||
|
@ -134,7 +124,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
|
|
||||||
const nextAction = plannedActions.shift()!;
|
const nextAction = plannedActions.shift()!;
|
||||||
const result = config.actions
|
const result = config.actions
|
||||||
? config.actions[nextAction.action].bind(handler, proxyObj)(...nextAction.payload)
|
? config.actions[nextAction.action].bind(storeHandler, proxyObj)(...nextAction.payload)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (isPromise(result)) {
|
if (isPromise(result)) {
|
||||||
|
@ -155,7 +145,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (!proxyObj.$pending) {
|
if (!proxyObj.$pending) {
|
||||||
proxyObj.$pending = true;
|
proxyObj.$pending = true;
|
||||||
const result = config.actions![action].bind(handler, proxyObj)(...payload);
|
const result = config.actions![action].bind(storeHandler, proxyObj)(...payload);
|
||||||
|
|
||||||
if (isPromise(result)) {
|
if (isPromise(result)) {
|
||||||
result.then(value => {
|
result.then(value => {
|
||||||
|
@ -176,45 +166,50 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 让store.$a[action]可以访问到action方法
|
||||||
($a as any)[action] = function Wrapped(...payload) {
|
($a as any)[action] = function Wrapped(...payload) {
|
||||||
return config.actions![action].bind(handler, proxyObj)(...payload);
|
return config.actions![action].bind(storeHandler, proxyObj)(...payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
// direct store access
|
// 让store[action]可以访问到action方法
|
||||||
Object.defineProperty(handler, action, {
|
Object.defineProperty(storeHandler, action, {
|
||||||
writable: false,
|
writable: false,
|
||||||
value: (...payload) => {
|
value: (...payload) => {
|
||||||
return config.actions![action].bind(handler, proxyObj)(...payload);
|
return config.actions![action].bind(storeHandler, proxyObj)(...payload);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.computed) {
|
if (config.computed) {
|
||||||
Object.keys(config.computed).forEach(key => {
|
Object.keys(config.computed).forEach(computeKey => {
|
||||||
($c as any)[key] = config.computed![key].bind(handler, readonlyProxy(proxyObj));
|
// 让store.$c[computeKey]可以访问到computed方法
|
||||||
|
($c as any)[computeKey] = config.computed![computeKey].bind(storeHandler, readonlyProxy(proxyObj));
|
||||||
|
|
||||||
// direct store access
|
// 让store[computeKey]可以访问到computed的值
|
||||||
Object.defineProperty(handler, key, {
|
Object.defineProperty(storeHandler, computeKey, {
|
||||||
get: $c[key] as () => any,
|
get: $c[computeKey] as () => any,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// direct state access
|
// 让store[key]可以访问到state的值
|
||||||
if (config.state) {
|
if (config.state) {
|
||||||
Object.keys(config.state).forEach(key => {
|
Object.keys(config.state).forEach(key => {
|
||||||
Object.defineProperty(handler, key, {
|
Object.defineProperty(storeHandler, key, {
|
||||||
get: () => proxyObj[key],
|
get: () => {
|
||||||
|
// 从Proxy对象获取值,会触发代理
|
||||||
|
return proxyObj[key];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.id) {
|
if (config.id) {
|
||||||
storeMap.set(config.id, handler);
|
storeMap.set(config.id, storeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
return createStoreHook(handler);
|
return createStoreHook(storeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearVNodeObservers(vNode) {
|
export function clearVNodeObservers(vNode) {
|
||||||
|
@ -240,8 +235,7 @@ function hookStore() {
|
||||||
|
|
||||||
if (processingVNode.tag === FunctionComponent) {
|
if (processingVNode.tag === FunctionComponent) {
|
||||||
// from FunctionComponent
|
// from FunctionComponent
|
||||||
const vNodeRef = useRef(null) as unknown as { current: VNode };
|
const vNodeRef = useRef(processingVNode);
|
||||||
vNodeRef.current = processingVNode;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -31,66 +30,3 @@ export interface IObserver {
|
||||||
|
|
||||||
clearByVNode: (vNode: any) => void;
|
clearByVNode: (vNode: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoveFirstFromTuple<T extends any[]> =
|
|
||||||
T['length'] extends 0 ? [] :
|
|
||||||
(((...b: T) => void) extends (a, ...b: infer I) => void ? I : [])
|
|
||||||
|
|
||||||
|
|
||||||
type UserActions<S extends object> = { [K:string]: ActionFunction<S> };
|
|
||||||
type UserComputedValues<S extends object> = { [K:string]: ComputedFunction<S> };
|
|
||||||
|
|
||||||
type ActionFunction<S extends object> = (state: S, ...args: any[]) => any;
|
|
||||||
type ComputedFunction<S extends object> = (state: S) => any;
|
|
||||||
type Action<T extends UserActions<?>> = (...args:RemoveFirstFromTuple<Parameters<T>>)=>ReturnType<T>
|
|
||||||
type AsyncAction<T extends UserActions<?>> = (...args:RemoveFirstFromTuple<Parameters<T>>)=>Promise<ReturnType<T>>
|
|
||||||
|
|
||||||
type StoreActions<S extends object,A extends UserActions<S>> = { [K in keyof A]: Action<A[K]> };
|
|
||||||
type QueuedStoreActions<S extends object,A extends UserActions<S>> = { [K in keyof A]: AsyncAction<A[K]> };
|
|
||||||
type ComputedValues<S extends object,C extends UserComputedValues<S>> = { [K in keyof C]: ReturnType<C[K]> };
|
|
||||||
type PostponedAction = (state: object, ...args: any[]) => Promise<any>;
|
|
||||||
type PostponedActions = { [key:string]: PostponedAction }
|
|
||||||
|
|
||||||
export type StoreHandler<S extends object,A extends UserActions<S>,C extends UserComputedValues<S>> =
|
|
||||||
{$subscribe: ((listener: () => void) => void),
|
|
||||||
$unsubscribe: ((listener: () => void) => void),
|
|
||||||
$state: S,
|
|
||||||
$config: StoreConfig<S,A,C>,
|
|
||||||
$queue: QueuedStoreActions<S,A>,
|
|
||||||
$actions: StoreActions<S,A>,
|
|
||||||
$computed: ComputedValues<S,C>,
|
|
||||||
reduxHandler?:ReduxStoreHandler}
|
|
||||||
&
|
|
||||||
{[K in keyof S]: S[K]}
|
|
||||||
&
|
|
||||||
{[K in keyof A]: Action<A[K]>}
|
|
||||||
&
|
|
||||||
{[K in keyof C]: ReturnType<C[K]>}
|
|
||||||
|
|
||||||
export type StoreConfig<S extends object,A extends UserActions<S>,C extends UserComputedValues<S>> = {
|
|
||||||
state?: S,
|
|
||||||
options?:{suppressHooks?: boolean},
|
|
||||||
actions?: A,
|
|
||||||
id?: string,
|
|
||||||
computed?: C
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReduxStoreHandler = {
|
|
||||||
reducer:(state:any,action:{type:string})=>any,
|
|
||||||
dispatch:(action:{type:string})=>void,
|
|
||||||
getState:()=>any,
|
|
||||||
subscribe:(listener:()=>void)=>((listener:()=>void)=>void)
|
|
||||||
replaceReducer: (reducer: (state:any,action:{type:string})=>any)=>void
|
|
||||||
_horizonXstore: StoreHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReduxAction = {
|
|
||||||
type:string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReduxMiddleware = (store:ReduxStoreHandler, extraArgument?:any) =>
|
|
||||||
(next:((action:ReduxAction)=>any)) =>
|
|
||||||
(action:(
|
|
||||||
ReduxAction|
|
|
||||||
((dispatch:(action:ReduxAction)=>void,store:ReduxStoreHandler,extraArgument?:any)=>any)
|
|
||||||
)) => ReduxStoreHandler
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -248,7 +245,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) {
|
||||||
|
@ -294,7 +292,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);
|
||||||
}
|
}
|
||||||
|
@ -369,9 +368,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),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,8 +388,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的时候会进入这个分支,同步渲染。
|
||||||
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
|
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -16,11 +16,7 @@
|
||||||
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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -351,7 +351,7 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC
|
||||||
|
|
||||||
if (rightNewNode) {
|
if (rightNewNode) {
|
||||||
appendNode(rightNewNode);
|
appendNode(rightNewNode);
|
||||||
setVNodesCIndex(rightNewNode, rightNewNode.cIndex + 1);
|
setVNodesCIndex(rightNewNode, prevNewNode.cIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultingFirstChild;
|
return resultingFirstChild;
|
||||||
|
@ -498,10 +498,10 @@ function diffIteratorNodesHandler(
|
||||||
newChildrenIterable: Iterable<any>
|
newChildrenIterable: Iterable<any>
|
||||||
): VNode | null {
|
): VNode | null {
|
||||||
const iteratorFn = getIteratorFn(newChildrenIterable);
|
const iteratorFn = getIteratorFn(newChildrenIterable);
|
||||||
const iteratorObj: Iterator<any> = iteratorFn.call(newChildrenIterable);
|
const iteratorObj = iteratorFn.call(newChildrenIterable);
|
||||||
|
|
||||||
// 把iterator转测数组
|
// 把iterator转测数组
|
||||||
const childrenArray: any[] = [];
|
const childrenArray = [];
|
||||||
let result = iteratorObj.next();
|
let result = iteratorObj.next();
|
||||||
while (!result.done) {
|
while (!result.done) {
|
||||||
childrenArray.push(result.value);
|
childrenArray.push(result.value);
|
||||||
|
@ -512,7 +512,7 @@ function diffIteratorNodesHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新节点是字符串类型
|
// 新节点是字符串类型
|
||||||
function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode | null, isComparing: boolean) {
|
function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode, isComparing: boolean) {
|
||||||
let newTextNode: VNode | null = null;
|
let newTextNode: VNode | null = null;
|
||||||
|
|
||||||
// 第一个vNode是Text,则复用
|
// 第一个vNode是Text,则复用
|
||||||
|
@ -540,6 +540,7 @@ function diffObjectNodeHandler(
|
||||||
parentNode: VNode,
|
parentNode: VNode,
|
||||||
firstChild: VNode | null,
|
firstChild: VNode | null,
|
||||||
newChild: any,
|
newChild: any,
|
||||||
|
firstChildVNode: VNode,
|
||||||
isComparing: boolean
|
isComparing: boolean
|
||||||
) {
|
) {
|
||||||
let canReuseNode: VNode | null = null;
|
let canReuseNode: VNode | null = null;
|
||||||
|
@ -558,7 +559,7 @@ function diffObjectNodeHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
let resultNode: VNode | null = null;
|
let resultNode: VNode | null = null;
|
||||||
let startDelVNode: VNode | null = firstChild;
|
let startDelVNode = firstChildVNode;
|
||||||
if (newChild.vtype === TYPE_COMMON_ELEMENT) {
|
if (newChild.vtype === TYPE_COMMON_ELEMENT) {
|
||||||
if (canReuseNode) {
|
if (canReuseNode) {
|
||||||
// 可以复用
|
// 可以复用
|
||||||
|
@ -653,7 +654,7 @@ export function createChildrenByDiff(
|
||||||
|
|
||||||
// 5. newChild是对象类型
|
// 5. newChild是对象类型
|
||||||
if (isObjectType(newChild)) {
|
if (isObjectType(newChild)) {
|
||||||
const newVNodes = diffObjectNodeHandler(parentNode, firstChild, newChild, isComparing);
|
const newVNodes = diffObjectNodeHandler(parentNode, firstChild, newChild, firstChild, isComparing);
|
||||||
if (newVNodes) {
|
if (newVNodes) {
|
||||||
return newVNodes;
|
return newVNodes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -29,23 +29,16 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,7 @@
|
||||||
|
|
||||||
import type { VNode } from '../Types';
|
import type { VNode } from '../Types';
|
||||||
|
|
||||||
import {
|
import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook';
|
||||||
getLastTimeHook,
|
|
||||||
setLastTimeHook,
|
|
||||||
setCurrentHook, getNextHook
|
|
||||||
} from './BaseHook';
|
|
||||||
import { HookStage, setHookStage } from './HookStage';
|
import { HookStage, setHookStage } from './HookStage';
|
||||||
|
|
||||||
// hook对外入口
|
// hook对外入口
|
||||||
|
@ -27,7 +23,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ export type CallBack<F> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Ref<V> = {
|
export type Ref<V> = {
|
||||||
current: V | null;
|
current: V;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Trigger<A> = (A) => void;
|
export type Trigger<A> = (A) => void;
|
||||||
|
|
|
@ -13,11 +13,7 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { createHook, getCurrentHook, throwNotInFuncError } from './BaseHook';
|
||||||
createHook,
|
|
||||||
getCurrentHook,
|
|
||||||
throwNotInFuncError
|
|
||||||
} from './BaseHook';
|
|
||||||
import { getHookStage, HookStage } from './HookStage';
|
import { getHookStage, HookStage } from './HookStage';
|
||||||
import { isArrayEqual } from '../utils/compare';
|
import { isArrayEqual } from '../utils/compare';
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,7 @@
|
||||||
* 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,
|
|
||||||
getCurrentHook,
|
|
||||||
getLastTimeHook,
|
|
||||||
throwNotInFuncError
|
|
||||||
} from './BaseHook';
|
|
||||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||||
import { EffectConstant } from './EffectConstant';
|
import { EffectConstant } from './EffectConstant';
|
||||||
import type { Effect, EffectList } from './HookType';
|
import type { Effect, EffectList } from './HookType';
|
||||||
|
@ -26,7 +21,7 @@ import {getHookStage, HookStage} from './HookStage';
|
||||||
import { isArrayEqual } from '../utils/compare';
|
import { isArrayEqual } from '../utils/compare';
|
||||||
import { getProcessingVNode } from '../GlobalVar';
|
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();
|
||||||
|
|
|
@ -21,7 +21,7 @@ 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);
|
||||||
|
|
|
@ -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,
|
|
||||||
getCurrentHook,
|
|
||||||
throwNotInFuncError
|
|
||||||
} from './BaseHook';
|
|
||||||
import { getHookStage, HookStage } from './HookStage';
|
import { getHookStage, HookStage } from './HookStage';
|
||||||
import { isArrayEqual } from '../utils/compare';
|
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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
// 循环遍历更新数组,计算新的状态值
|
// 循环遍历更新数组,计算新的状态值
|
||||||
|
|
|
@ -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,因此需要调用
|
||||||
|
|
|
@ -36,4 +36,3 @@ export function captureRender(processing: VNode): VNode | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bubbleRender() {}
|
export function bubbleRender() {}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
vNode,
|
||||||
|
node => {
|
||||||
const depContexts = node.depContexts;
|
const depContexts = node.depContexts;
|
||||||
if (depContexts && depContexts.length) {
|
if (depContexts && depContexts.length) {
|
||||||
isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
|
isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
|
||||||
}
|
}
|
||||||
}, node =>
|
},
|
||||||
|
node =>
|
||||||
// 如果这是匹配的provider,则不要更深入地扫描
|
// 如果这是匹配的provider,则不要更深入地扫描
|
||||||
node.tag === ContextProvider && node.type === processing.type
|
node.tag === ContextProvider && node.type === processing.type,
|
||||||
, processing, null);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
vNode,
|
||||||
|
node => {
|
||||||
if (node.tag === DomComponent || node.tag === DomText) {
|
if (node.tag === DomComponent || node.tag === DomText) {
|
||||||
appendChildElement(dom, node.realNode);
|
appendChildElement(dom, node.realNode);
|
||||||
}
|
}
|
||||||
}, node =>
|
},
|
||||||
|
node =>
|
||||||
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
||||||
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal
|
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal,
|
||||||
, processing, null);
|
processing,
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
processing.realNode = dom;
|
processing.realNode = dom;
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,3 @@ export function captureRender(processing: VNode): VNode | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bubbleRender() {}
|
export function bubbleRender() {}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,7 @@ 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';
|
||||||
|
@ -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;
|
||||||
|
@ -89,7 +80,7 @@ function captureLazyComponent(
|
||||||
'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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -32,7 +32,7 @@ function updateTreeRoot(processing) {
|
||||||
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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -18,20 +18,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { VNode } from '../Types';
|
import type { VNode } from '../Types';
|
||||||
import type {
|
import type { Effect as HookEffect, EffectList } from '../hooks/HookType';
|
||||||
Effect as HookEffect,
|
|
||||||
EffectList,
|
|
||||||
} from '../hooks/HookType';
|
|
||||||
import { runAsync } from '../taskExecutor/TaskExecutor';
|
import { runAsync } from '../taskExecutor/TaskExecutor';
|
||||||
import {
|
import { copyExecuteMode, InRender, setExecuteMode, changeMode } from '../ExecuteMode';
|
||||||
copyExecuteMode, InRender, setExecuteMode,changeMode
|
|
||||||
} from '../ExecuteMode';
|
|
||||||
import { EffectConstant } from '../hooks/EffectConstant';
|
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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -106,14 +101,16 @@ export function callEffectRemove(vNode: VNode) {
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,7 +135,9 @@ function callAfterSubmitLifeCycles(
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideOrUnhideAllChildren(vNode, isHidden) {
|
function hideOrUnhideAllChildren(vNode, isHidden) {
|
||||||
travelVNodeTree(vNode, (node: VNode) => {
|
travelVNodeTree(
|
||||||
|
vNode,
|
||||||
|
(node: VNode) => {
|
||||||
const instance = node.realNode;
|
const instance = node.realNode;
|
||||||
|
|
||||||
if (node.tag === DomComponent || node.tag === DomText) {
|
if (node.tag === DomComponent || node.tag === DomText) {
|
||||||
|
@ -162,7 +147,11 @@ function hideOrUnhideAllChildren(vNode, isHidden) {
|
||||||
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,7 +295,9 @@ function unmountDomComponents(vNode: VNode): void {
|
||||||
// 这两个变量要一起更新
|
// 这两个变量要一起更新
|
||||||
let currentParent;
|
let currentParent;
|
||||||
|
|
||||||
travelVNodeTree(vNode, (node) => {
|
travelVNodeTree(
|
||||||
|
vNode,
|
||||||
|
node => {
|
||||||
if (!currentParentIsValid) {
|
if (!currentParentIsValid) {
|
||||||
let parent = node.parent;
|
let parent = node.parent;
|
||||||
let tag;
|
let tag;
|
||||||
|
@ -333,14 +325,18 @@ function unmountDomComponents(vNode: VNode): void {
|
||||||
} else {
|
} else {
|
||||||
unmountVNode(node);
|
unmountVNode(node);
|
||||||
}
|
}
|
||||||
}, node =>
|
},
|
||||||
|
node =>
|
||||||
// 如果是dom不用再遍历child
|
// 如果是dom不用再遍历child
|
||||||
node.tag === DomComponent || node.tag === DomText, vNode, (node) => {
|
node.tag === DomComponent || node.tag === DomText,
|
||||||
|
vNode,
|
||||||
|
node => {
|
||||||
if (node.tag === DomPortal) {
|
if (node.tag === DomPortal) {
|
||||||
// 当离开portal,需要重新设置parent
|
// 当离开portal,需要重新设置parent
|
||||||
currentParentIsValid = false;
|
currentParentIsValid = false;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitClear(vNode: VNode): void {
|
function submitClear(vNode: VNode): void {
|
||||||
|
|
|
@ -21,27 +21,24 @@ 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,
|
|
||||||
setExecuteMode,
|
|
||||||
changeMode,
|
|
||||||
} from '../ExecuteMode';
|
|
||||||
import {
|
|
||||||
isSchedulingEffects,
|
|
||||||
setSchedulingEffects,
|
|
||||||
} from './HookEffectHandler';
|
|
||||||
import { getStartVNode } from '../GlobalVar';
|
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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ function callRenderQueue() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let callback;
|
let callback;
|
||||||
while (callback = renderQueue.shift()) {
|
while ((callback = renderQueue.shift())) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
@ -60,7 +57,8 @@ function callTasks() {
|
||||||
|
|
||||||
if (task === first()) {
|
if (task === first()) {
|
||||||
shift();
|
shift();
|
||||||
} else { // 执行任务中可能插入了新任务
|
} else {
|
||||||
|
// 执行任务中可能插入了新任务
|
||||||
remove(task);
|
remove(task);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,9 +107,4 @@ function cancelTask(task) {
|
||||||
task.callback = null;
|
task.callback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { ImmediatePriority, NormalPriority, runAsync, cancelTask };
|
||||||
ImmediatePriority,
|
|
||||||
NormalPriority,
|
|
||||||
runAsync,
|
|
||||||
cancelTask,
|
|
||||||
};
|
|
||||||
|
|
|
@ -57,8 +57,8 @@ 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]])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
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 节点
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(
|
||||||
|
vNode,
|
||||||
|
node => {
|
||||||
if (node.tag === DomComponent || node.tag === DomText) {
|
if (node.tag === DomComponent || node.tag === DomText) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, null, vNode, null);
|
},
|
||||||
|
null,
|
||||||
|
vNode,
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findDOMByClassInst(inst) {
|
export function findDOMByClassInst(inst) {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue