Match-id-ad7c692603a33470196a2c41cdb3ff21136732d9
This commit is contained in:
parent
a0e05e8f42
commit
3d42a535d6
|
@ -48,7 +48,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i
|
||||||
}
|
}
|
||||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||||
dom.innerHTML = propVal.__html;
|
dom.innerHTML = propVal.__html;
|
||||||
} else if (!isInit || (isInit && propVal != null)) {
|
} else if (!isInit || propVal != null) {
|
||||||
updateCommonProp(dom, propName, propVal, isNativeTag);
|
updateCommonProp(dom, propName, propVal, isNativeTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object {
|
||||||
for (let i = 0; i < oldPropsLength; i++) {
|
for (let i = 0; i < oldPropsLength; i++) {
|
||||||
propName = keysOfOldProps[i];
|
propName = keysOfOldProps[i];
|
||||||
// 新属性中包含该属性或者该属性为空值的属性不需要处理
|
// 新属性中包含该属性或者该属性为空值的属性不需要处理
|
||||||
if (keysOfNewProps.includes(propName) || oldProps[propName] == null) {
|
if ( oldProps[propName] == null || keysOfNewProps.includes(propName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!events[nativeFullName]) {
|
if (!events[nativeFullName]) {
|
||||||
const listener = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture);
|
events[nativeFullName] = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture);
|
||||||
events[nativeFullName] = listener;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { throwIfTrue } from '../renderer/utils/throwIfTrue';
|
||||||
import { TYPE_COMMON_ELEMENT, TYPE_PORTAL } from './JSXElementType';
|
import { TYPE_COMMON_ELEMENT, TYPE_PORTAL } from './JSXElementType';
|
||||||
|
|
||||||
import { isValidElement, JSXElement } from './JSXElement';
|
import { isValidElement, JSXElement } from './JSXElement';
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode';
|
||||||
|
|
||||||
// 生成key
|
// 生成key
|
||||||
function getItemKey(item: any, index: number): string {
|
function getItemKey(item: any, index: number): string {
|
||||||
|
@ -83,7 +84,7 @@ function callMapFun(children: any, arr: Array<any>, prefix: string, callback: Fu
|
||||||
mappedChild.type,
|
mappedChild.type,
|
||||||
newKey,
|
newKey,
|
||||||
mappedChild.ref,
|
mappedChild.ref,
|
||||||
mappedChild.belongClassVNode,
|
mappedChild[BELONG_CLASS_VNODE_KEY],
|
||||||
mappedChild.props,
|
mappedChild.props,
|
||||||
mappedChild.src
|
mappedChild.src
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import { TYPE_COMMON_ELEMENT } from './JSXElementType';
|
import { TYPE_COMMON_ELEMENT } from './JSXElementType';
|
||||||
import { getProcessingClassVNode } from '../renderer/GlobalVar';
|
import { getProcessingClassVNode } from '../renderer/GlobalVar';
|
||||||
import { Source } from '../renderer/Types';
|
import { Source } from '../renderer/Types';
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vtype 节点的类型,这里固定是element
|
* vtype 节点的类型,这里固定是element
|
||||||
|
@ -36,17 +37,9 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null)
|
||||||
ref: ref,
|
ref: ref,
|
||||||
props: props,
|
props: props,
|
||||||
|
|
||||||
// 所属的class组件
|
// 所属的class组件,clonedeep jsxElement时需要防止无限循环
|
||||||
belongClassVNode: null,
|
[BELONG_CLASS_VNODE_KEY]: vNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在 cloneDeep JSXElement 的时候会出现死循环,需要设置belongClassVNode的enumerable为false
|
|
||||||
Object.defineProperty(ele, 'belongClassVNode', {
|
|
||||||
configurable: false,
|
|
||||||
enumerable: false,
|
|
||||||
value: vNode,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
// 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false
|
// 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false
|
||||||
Object.defineProperty(ele, 'src', {
|
Object.defineProperty(ele, 'src', {
|
||||||
|
@ -60,11 +53,6 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null)
|
||||||
return ele;
|
return ele;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidKey(key) {
|
|
||||||
const keyArray = ['key', 'ref', '__source', '__self'];
|
|
||||||
return !keyArray.includes(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeDefault(sourceObj, defaultObj) {
|
function mergeDefault(sourceObj, defaultObj) {
|
||||||
Object.keys(defaultObj).forEach(key => {
|
Object.keys(defaultObj).forEach(key => {
|
||||||
if (sourceObj[key] === undefined) {
|
if (sourceObj[key] === undefined) {
|
||||||
|
@ -73,19 +61,20 @@ function mergeDefault(sourceObj, defaultObj) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ['key', 'ref', '__source', '__self']属性不从setting获取
|
||||||
|
const keyArray = ['key', 'ref', '__source', '__self'];
|
||||||
|
|
||||||
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[BELONG_CLASS_VNODE_KEY] : getProcessingClassVNode();
|
||||||
|
|
||||||
if (setting !== null && setting !== undefined) {
|
if (setting !== null && setting !== undefined) {
|
||||||
const keys = Object.keys(setting);
|
|
||||||
const keyLength = keys.length;
|
for (const k in setting) {
|
||||||
for (let i = 0; i < keyLength; i++) {
|
if (!keyArray.includes(k)) {
|
||||||
const k = keys[i];
|
|
||||||
if (isValidKey(k)) {
|
|
||||||
props[k] = setting[k];
|
props[k] = setting[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +98,6 @@ function buildElement(isClone, type, setting, children) {
|
||||||
lineNumber: setting.__source.lineNumber,
|
lineNumber: setting.__source.lineNumber,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSXElement(element, key, ref, vNode, props, src);
|
return JSXElement(element, key, ref, vNode, props, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,20 @@
|
||||||
|
|
||||||
import { VNode } from './vnode/VNode';
|
import { VNode } from './vnode/VNode';
|
||||||
|
|
||||||
const currentRootStack: VNode[] = [];
|
const currentRootStack: (VNode | undefined)[] = [];
|
||||||
|
let index = -1;
|
||||||
export function getCurrentRoot() {
|
export function getCurrentRoot() {
|
||||||
return currentRootStack[currentRootStack.length - 1];
|
return currentRootStack[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pushCurrentRoot(root: VNode) {
|
export function pushCurrentRoot(root: VNode) {
|
||||||
return currentRootStack.push(root);
|
index++;
|
||||||
|
currentRootStack[index] = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function popCurrentRoot() {
|
export function popCurrentRoot() {
|
||||||
return currentRootStack.pop();
|
const target = currentRootStack[index];
|
||||||
|
currentRootStack[index] = undefined;
|
||||||
|
index--;
|
||||||
|
return target;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from './vnode/VNode';
|
||||||
|
|
||||||
export { VNode } from './vnode/VNode';
|
export { VNode } from './vnode/VNode';
|
||||||
|
|
||||||
type Trigger<A> = (A) => void;
|
type Trigger<A> = (A) => void;
|
||||||
|
@ -32,7 +34,7 @@ export type JSXElement = {
|
||||||
key: any;
|
key: any;
|
||||||
ref: any;
|
ref: any;
|
||||||
props: any;
|
props: any;
|
||||||
belongClassVNode: any;
|
[BELONG_CLASS_VNODE_KEY]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProviderType<T> = {
|
export type ProviderType<T> = {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {
|
||||||
import { isSameType, getIteratorFn, isTextType, isIteratorType, isObjectType } from './DiffTools';
|
import { isSameType, getIteratorFn, isTextType, isIteratorType, isObjectType } from './DiffTools';
|
||||||
import { travelChildren } from '../vnode/VNodeUtils';
|
import { travelChildren } from '../vnode/VNodeUtils';
|
||||||
import { markVNodePath } from '../utils/vNodePath';
|
import { markVNodePath } from '../utils/vNodePath';
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode';
|
||||||
|
|
||||||
enum DiffCategory {
|
enum DiffCategory {
|
||||||
TEXT_NODE = 'TEXT_NODE',
|
TEXT_NODE = 'TEXT_NODE',
|
||||||
|
@ -166,11 +167,11 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
||||||
if (oldNode === null || !isSameType(oldNode, newChild)) {
|
if (oldNode === null || !isSameType(oldNode, newChild)) {
|
||||||
resultNode = createVNodeFromElement(newChild);
|
resultNode = createVNodeFromElement(newChild);
|
||||||
resultNode.ref = newChild.ref;
|
resultNode.ref = newChild.ref;
|
||||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||||
} else {
|
} else {
|
||||||
resultNode = updateVNode(oldNode, newChild.props);
|
resultNode = updateVNode(oldNode, newChild.props);
|
||||||
resultNode.ref = newChild.ref;
|
resultNode.ref = newChild.ref;
|
||||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||||
|
@ -570,7 +571,7 @@ function diffObjectNodeHandler(
|
||||||
} else if (isSameType(canReuseNode, newChild)) {
|
} else if (isSameType(canReuseNode, newChild)) {
|
||||||
resultNode = updateVNode(canReuseNode, newChild.props);
|
resultNode = updateVNode(canReuseNode, newChild.props);
|
||||||
resultNode.ref = newChild.ref;
|
resultNode.ref = newChild.ref;
|
||||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||||
startDelVNode = resultNode.next;
|
startDelVNode = resultNode.next;
|
||||||
resultNode.next = null;
|
resultNode.next = null;
|
||||||
}
|
}
|
||||||
|
@ -583,7 +584,7 @@ function diffObjectNodeHandler(
|
||||||
} else {
|
} else {
|
||||||
resultNode = createVNodeFromElement(newChild);
|
resultNode = createVNodeFromElement(newChild);
|
||||||
resultNode.ref = newChild.ref;
|
resultNode.ref = newChild.ref;
|
||||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ import {
|
||||||
import { handleSubmitError } from '../ErrorHandler';
|
import { handleSubmitError } from '../ErrorHandler';
|
||||||
import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils';
|
import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils';
|
||||||
import { shouldAutoFocus } from '../../dom/utils/Common';
|
import { shouldAutoFocus } from '../../dom/utils/Common';
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode';
|
||||||
|
|
||||||
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
||||||
try {
|
try {
|
||||||
|
@ -163,8 +164,8 @@ function handleRef(vNode: VNode, ref, val) {
|
||||||
} else if (refType === 'object') {
|
} else if (refType === 'object') {
|
||||||
(<RefType>ref).current = val;
|
(<RefType>ref).current = val;
|
||||||
} else {
|
} else {
|
||||||
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
|
if (vNode[BELONG_CLASS_VNODE_KEY] && vNode[BELONG_CLASS_VNODE_KEY].realNode) {
|
||||||
vNode.belongClassVNode.realNode.refs[String(ref)] = val;
|
vNode[BELONG_CLASS_VNODE_KEY].realNode.refs[String(ref)] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ import type { Hook } from '../hooks/HookType';
|
||||||
import { InitFlag } from './VNodeFlags';
|
import { InitFlag } from './VNodeFlags';
|
||||||
import { Observer } from '../../horizonx/proxy/Observer';
|
import { Observer } from '../../horizonx/proxy/Observer';
|
||||||
|
|
||||||
|
export const BELONG_CLASS_VNODE_KEY = Symbol('belongClassVNode');
|
||||||
|
|
||||||
export class VNode {
|
export class VNode {
|
||||||
tag: VNodeTag;
|
tag: VNodeTag;
|
||||||
key: string | null; // 唯一标识符
|
key: string | null; // 唯一标识符
|
||||||
|
@ -97,7 +99,7 @@ export class VNode {
|
||||||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||||
delegatedEvents: Set<string>;
|
delegatedEvents: Set<string>;
|
||||||
|
|
||||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
[BELONG_CLASS_VNODE_KEY]: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||||
|
|
||||||
// 状态管理器HorizonX使用
|
// 状态管理器HorizonX使用
|
||||||
isStoreChange: boolean;
|
isStoreChange: boolean;
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
import type {VNode} from '../Types';
|
import type {VNode} from '../Types';
|
||||||
|
|
||||||
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
|
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
|
||||||
import {isComment} from '../../dom/utils/Common';
|
|
||||||
import {getNearestVNode} from '../../dom/DOMInternalKeys';
|
import {getNearestVNode} from '../../dom/DOMInternalKeys';
|
||||||
import {Addition, InitFlag} from './VNodeFlags';
|
import {Addition, InitFlag} from './VNodeFlags';
|
||||||
|
import { BELONG_CLASS_VNODE_KEY } from './VNode';
|
||||||
|
|
||||||
export function travelChildren(
|
export function travelChildren(
|
||||||
beginVNode: VNode | null,
|
beginVNode: VNode | null,
|
||||||
|
@ -124,7 +124,7 @@ export function clearVNode(vNode: VNode) {
|
||||||
|
|
||||||
vNode.toUpdateNodes = null;
|
vNode.toUpdateNodes = null;
|
||||||
|
|
||||||
vNode.belongClassVNode = null;
|
vNode[BELONG_CLASS_VNODE_KEY] = null;
|
||||||
if (window.__HORIZON_DEV_HOOK__) {
|
if (window.__HORIZON_DEV_HOOK__) {
|
||||||
const hook = window.__HORIZON_DEV_HOOK__;
|
const hook = window.__HORIZON_DEV_HOOK__;
|
||||||
hook.deleteVNode(vNode);
|
hook.deleteVNode(vNode);
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
|
|
||||||
|
describe('JSX Element test', () => {
|
||||||
|
it('symbol attribute prevent cloneDeep unlimited loop', function () {
|
||||||
|
|
||||||
|
function cloneDeep(obj) {
|
||||||
|
const result = {};
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
if (obj[key] && typeof obj[key] === 'object') {
|
||||||
|
result[key] = cloneDeep(obj[key]);
|
||||||
|
} else {
|
||||||
|
result[key] = obj[key];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
class Demo extends Horizon.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
hello
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ele = Horizon.createElement(Demo);
|
||||||
|
const copy = cloneDeep(ele);
|
||||||
|
expect(copy.vtype).toEqual(ele.vtype);
|
||||||
|
expect(Object.getOwnPropertySymbols(copy).length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue