Match-id-34cc8d2e71fcba103088bb87b33f8df28ae8c31d

This commit is contained in:
* 2021-12-25 11:52:31 +08:00 committed by *
parent 34abe28056
commit 19a39513e6
15 changed files with 411 additions and 206 deletions

View File

@ -3,19 +3,17 @@ import {
syncUpdates, startUpdate, syncUpdates, startUpdate,
} from '../renderer/Renderer'; } from '../renderer/Renderer';
import {createPortal} from '../renderer/components/CreatePortal'; import {createPortal} from '../renderer/components/CreatePortal';
import {
clearContainer, saveContainer,
} from './DOMInternalKeys';
import type {Container} from './DOMOperator'; import type {Container} from './DOMOperator';
import {isElement} from './utils/Common'; import {isElement} from './utils/Common';
import {listenDelegatedEvents} from '../event/EventBinding'; import {listenDelegatedEvents} from '../event/EventBinding';
import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils'; import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils';
import {TreeRoot} from '../renderer/vnode/VNodeTags'; import {TreeRoot} from '../renderer/vnode/VNodeTags';
import {Callback} from '../renderer/UpdateHandler';
function executeRender( function executeRender(
children: any, children: any,
container: Container, container: Container,
callback?: Function, callback?: Callback,
) { ) {
let treeRoot = container._treeRoot; let treeRoot = container._treeRoot;
@ -36,7 +34,7 @@ function executeRender(
return getFirstCustomDom(treeRoot); return getFirstCustomDom(treeRoot);
} }
function createRoot(children: any, container: Container, callback?: Function) { function createRoot(children: any, container: Container, callback?: Callback) {
// 清空容器 // 清空容器
let child = container.lastChild; let child = container.lastChild;
while (child) { while (child) {
@ -46,11 +44,10 @@ function createRoot(children: any, container: Container, callback?: Function) {
// 调度器创建根节点并给容器dom赋vNode结构体 // 调度器创建根节点并给容器dom赋vNode结构体
const treeRoot = createVNode(TreeRoot, container); const treeRoot = createVNode(TreeRoot, container);
saveContainer(container, treeRoot._domRoot);
container._treeRoot = treeRoot; container._treeRoot = treeRoot;
// 根节点挂接全量事件 // 根节点挂接全量事件
listenDelegatedEvents(container); listenDelegatedEvents(container as Element);
// 执行回调 // 执行回调
if (typeof callback === 'function') { if (typeof callback === 'function') {
@ -89,7 +86,6 @@ function destroy(container: Container) {
syncUpdates(() => { syncUpdates(() => {
executeRender(null, container, () => { executeRender(null, container, () => {
container._treeRoot = null; container._treeRoot = null;
clearContainer(container);
}); });
}); });

View File

@ -11,7 +11,7 @@ import type {
import { import {
DomComponent, DomComponent,
DomText, DomText,
DomRoot, TreeRoot,
} from '../renderer/vnode/VNodeTags'; } from '../renderer/vnode/VNodeTags';
const suffixKey = new Date().getTime().toString(); const suffixKey = new Date().getTime().toString();
@ -20,7 +20,6 @@ const prefix = '_horizon';
const internalKeys = { const internalKeys = {
VNode: `${prefix}VNode@${suffixKey}`, VNode: `${prefix}VNode@${suffixKey}`,
props: `${prefix}Props@${suffixKey}`, props: `${prefix}Props@${suffixKey}`,
container: `${prefix}Container@${suffixKey}`,
events: `${prefix}Events@${suffixKey}`, events: `${prefix}Events@${suffixKey}`,
nonDelegatedEvents: `${prefix}NonDelegatedEvents@${suffixKey}`, nonDelegatedEvents: `${prefix}NonDelegatedEvents@${suffixKey}`,
}; };
@ -42,33 +41,33 @@ export function saveVNode(
} }
// 用 DOM 节点,来找其对应的 VNode 实例 // 用 DOM 节点,来找其对应的 VNode 实例
export function getVNode(dom: Node): VNode | null { export function getVNode(dom: Node|Container): VNode | null {
const vNode = dom[internalKeys.VNode] || dom[internalKeys.container]; const vNode = dom[internalKeys.VNode] || (dom as Container)._treeRoot;
if (vNode) { if (vNode) {
const {tag} = vNode; const {tag} = vNode;
if (tag === DomComponent || tag === DomText || tag === DomRoot) { if (tag === DomComponent || tag === DomText || tag === TreeRoot) {
return vNode; return vNode;
} }
} }
return null; return null;
} }
// 用 DOM 对象,来寻找其对应或者说是最近的 VNode 实例 // 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode
export function getNearestVNode(dom: Node): null | VNode { export function getNearestVNode(dom: Node): null | VNode {
let vNode = dom[internalKeys.VNode]; let vNode = dom[internalKeys.VNode];
if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例 if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例
return vNode; return vNode;
} }
// 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过 // 下面处理的是为被框架标记过的 DOM 节点,向上找其父节点是否被框架标记过
let parent = dom.parentNode; let parentDom = dom.parentNode;
let nearVNode = null; let nearVNode = null;
while (parent) { while (parentDom) {
vNode = parent[internalKeys.VNode]; vNode = parentDom[internalKeys.VNode];
if (vNode) { if (vNode) {
nearVNode = vNode; nearVNode = vNode;
break; break;
} }
parent = parent.parentNode; parentDom = parentDom.parentNode;
} }
return nearVNode; return nearVNode;
} }
@ -99,11 +98,3 @@ export function getEventToListenerMap(target: EventTarget): Map<string, EventLis
} }
return eventsMap; return eventsMap;
} }
export function saveContainer(dom: Container, domRoot: VNode): void {
dom[internalKeys.container] = domRoot;
}
export function clearContainer(dom: Container): void {
dom[internalKeys.container] = null;
}

View File

@ -1,7 +1,3 @@
/**
* Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
*/
import { import {
saveVNode, saveVNode,
updateVNodeProps, updateVNodeProps,
@ -10,7 +6,7 @@ import {
createDom, createDom,
} from './utils/DomCreator'; } from './utils/DomCreator';
import {getSelectionInfo, resetSelectionRange, selectionData} from './SelectionRangeHandler'; import {getSelectionInfo, resetSelectionRange, selectionData} from './SelectionRangeHandler';
import {isElement, isComment, isDocument, isDocumentFragment} from './utils/Common'; import {isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus} from './utils/Common';
import {NSS} from './utils/DomCreator'; import {NSS} from './utils/DomCreator';
import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler'; import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler';
@ -20,7 +16,7 @@ import {
setInitValue, setInitValue,
getPropsWithoutValue, getPropsWithoutValue,
updateValue, updateValue,
} from './valueHandler/ValueHandler'; } from './valueHandler';
import { import {
compareProps, compareProps,
setDomProps, updateDomProps setDomProps, updateDomProps
@ -31,76 +27,39 @@ import {DomComponent, DomText} from '../renderer/vnode/VNodeTags';
import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp'; import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp';
export type Props = { export type Props = {
autoFocus?: boolean, autoFocus?: boolean;
children?: any, children?: any;
dangerouslySetInnerHTML?: any, dangerouslySetInnerHTML?: any;
disabled?: boolean, disabled?: boolean;
hidden?: boolean, hidden?: boolean;
style?: { display?: string }, style?: { display?: string };
}; };
export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode }); export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode });
let selectionInfo: null | selectionData = null; let selectionInfo: null | selectionData = null;
const types = ['button', 'input', 'select', 'textarea']; function getChildNS(parentNS: string | null, tagName: string): string {
if (parentNS === NSS.svg && tagName === 'foreignObject') {
// button、input、select、textarea、如果有 autoFocus 属性需要focus
function shouldAutoFocus(type: string, props: Props): boolean {
return types.includes(type) ? Boolean(props.autoFocus) : false;
}
function getChildNS(parent: string | null, type: string,): string {
if (parent === NSS.svg && type === 'foreignObject') {
return NSS.html; return NSS.html;
} }
if (parent == null || parent === NSS.html) {
// 没有父命名空间 if (parentNS == null || parentNS === NSS.html) {
return Object.keys(NSS).includes(type) ? NSS[type] : NSS.html; // 没有父命名空间或父命名空间为xhtml
return NSS[tagName] ?? NSS.html;
} }
// 默认返回parentNamespace. // 默认返回parentNamespace.
return parent; return parentNS;
}
function getRootNS(dom, root, nextRoot) {
let namespace;
let tag;
let container, ownNamespace;
if (isDocument(dom)) {
tag = '#document';
namespace = root ? root.namespaceURI : getChildNS(null, '');
} else if (isDocumentFragment(dom)) {
tag = '#fragment';
namespace = root ? root.namespaceURI : getChildNS(null, '');
} else if (isComment(dom)) {
container = nextRoot.parentNode;
ownNamespace = container.namespaceURI || null;
tag = container.tagName;
namespace = getChildNS(ownNamespace, tag);
} else {
container = nextRoot;
ownNamespace = container.namespaceURI || null;
tag = container.tagName;
namespace = getChildNS(ownNamespace, tag);
}
return namespace;
} }
// 获取容器 // 获取容器
export function getNSCtx( export function getNSCtx(dom: Container, parentNS: string, type: string): string {
nextRoot: Container,
ctxNamespace: string,
type: string): string {
let namespace; let namespace;
if (nextRoot) { if (dom) {
// 获取并解析根节点容器 namespace = getChildNS(dom.namespaceURI ?? null, dom.nodeName);
const root = nextRoot.documentElement;
namespace = getRootNS(nextRoot, root, nextRoot);
} else { } else {
// 获取子节点容器 namespace = getChildNS(parentNS, type);
namespace = getChildNS(ctxNamespace, type);
} }
return namespace; return namespace;
} }
@ -114,21 +73,14 @@ export function resetAfterSubmit(): void {
selectionInfo = null; selectionInfo = null;
} }
/** // 创建 DOM 对象
* DOM
* @param tagName
* @param props
* @param parentNamespace
* @param vNode VNode
* @returns DOM
*/
export function newDom( export function newDom(
tagName: string, tagName: string,
props: Props, props: Props,
parentNamespace: string, parentNamespace: string,
vNode: VNode, vNode: VNode,
): Element { ): Element {
const dom: Element = createDom(tagName, props, parentNamespace); const dom: Element = createDom(tagName, parentNamespace);
// 将 vNode 节点挂到 DOM 对象上 // 将 vNode 节点挂到 DOM 对象上
saveVNode(vNode, dom); saveVNode(vNode, dom);
// 将属性挂到 DOM 对象上 // 将属性挂到 DOM 对象上
@ -164,7 +116,7 @@ export function getPropChangeList(
type: string, type: string,
lastRawProps: Props, lastRawProps: Props,
nextRawProps: Props, nextRawProps: Props,
): null | Array<any> { ): Array<any> {
// 校验两个对象的不同 // 校验两个对象的不同
validateProps(type, nextRawProps); validateProps(type, nextRawProps);
@ -177,15 +129,11 @@ export function getPropChangeList(
} }
export function isTextChild(type: string, props: Props): boolean { export function isTextChild(type: string, props: Props): boolean {
if (type === 'textarea') { const typeArray = ['textarea', 'option', 'noscript'];
const typeOfPropsChild = ['string', 'number'];
if (typeArray.indexOf(type) >= 0) {
return true; return true;
} else if (type === 'option') { } else if (typeOfPropsChild.indexOf(typeof props.children) >= 0) {
return true;
} else if (type === 'noscript') {
return true;
} else if (typeof props.children === 'string') {
return true;
} else if (typeof props.children === 'number') {
return true; return true;
} else { } else {
return ( return (
@ -205,19 +153,8 @@ export function newTextDom(
return textNode; return textNode;
} }
export function submitMount(
dom: Element,
type: string,
newProps: Props,
): void {
if (shouldAutoFocus(type, newProps)) {
// button、input、select、textarea、如果有 autoFocus 属性需要focus
dom.focus();
}
}
// 提交vNode的类型为Component或者Text的更新 // 提交vNode的类型为Component或者Text的更新
export function submitDomUpdate(tag: number, vNode: VNode) { export function submitDomUpdate(tag: string, vNode: VNode) {
const newProps = vNode.props; const newProps = vNode.props;
const element: Element = vNode.realNode; const element: Element = vNode.realNode;
@ -251,71 +188,44 @@ export function clearText(dom: Element): void {
} }
// 添加child元素 // 添加child元素
export function appendChildElement(isContainer: boolean, export function appendChildElement(
parent: Element | Container, parent: Element | Container,
child: Element | Text): void { child: Element | Text
if (isContainer && isComment(parent)) { ): void {
parent.parentNode.insertBefore(child, parent); parent.appendChild(child);
} else {
parent.appendChild(child);
}
} }
// 插入dom元素 // 插入dom元素
export function insertDomBefore( export function insertDomBefore(
isContainer: boolean,
parent: Element | Container, parent: Element | Container,
child: Element | Text, child: Element | Text,
beforeChild: Element | Text, beforeChild: Element | Text,
) { ) {
if (isContainer && isComment(parent)) { parent.insertBefore(child, beforeChild);
parent.parentNode.insertBefore(child, beforeChild);
} else {
parent.insertBefore(child, beforeChild);
}
} }
export function removeChildDom( export function removeChildDom(
isContainer: boolean,
parent: Element | Container, parent: Element | Container,
child: Element | Text child: Element | Text
) { ) {
if (isContainer && isComment(parent)) { parent.removeChild(child);
parent.parentNode.removeChild(child);
} else {
parent.removeChild(child);
}
} }
// 隐藏元素 // 隐藏元素
export function hideDom(tag: number, element: Element | Text) { export function hideDom(tag: string, dom: Element | Text) {
if (tag === DomComponent) { if (tag === DomComponent) {
// DomComponent类型 dom.style.display = 'none';
const {style} = element;
if (style.setProperty && typeof style.setProperty === 'function') {
style.setProperty('display', 'none', 'important');
} else {
style.display = 'none';
}
} else if (tag === DomText) { } else if (tag === DomText) {
// text类型 dom.textContent = '';
element.textContent = '';
} }
} }
// 不隐藏元素 // 不隐藏元素
export function unHideDom(tag: number, element: Element | Text, props: Props) { export function unHideDom(tag: string, dom: Element | Text, props: Props) {
if (tag === DomComponent) { if (tag === DomComponent) {
// DomComponent类型 dom.style.display = adjustStyleValue('display', props?.style?.display ?? '');
const style = props.style;
let display = null;
if (style !== undefined && style !== null && style.hasOwnProperty('display')) {
display = style.display;
}
element.style.display = adjustStyleValue('display', display);
} else if (tag === DomText) { } else if (tag === DomText) {
// text类型 dom.textContent = props;
element.textContent = props;
} }
} }

View File

@ -63,7 +63,7 @@ function updateOneProp(dom, propName, propVal, isNativeTag, isInit = false) {
export function compareProps( export function compareProps(
oldProps: Object, oldProps: Object,
newProps: Object, newProps: Object,
): null | Array<any> { ): Array<any> {
let updatesForStyle = {}; let updatesForStyle = {};
const toBeDeletedProps = []; const toBeDeletedProps = [];
const toBeUpdatedProps = []; const toBeUpdatedProps = [];

View File

@ -7,29 +7,44 @@ export function setStyles(dom, styles) {
} }
const style = dom.style; const style = dom.style;
const styleKeys = Object.keys(styles); Object.keys(styles).forEach((name) => {
const styleVal = styles[name];
for (let i = 0; i < styleKeys.length; i++) { const validStyleValue = adjustStyleValue(name, styleVal);
const styleKey = styleKeys[i];
const styleVal = styles[styleKey];
const validStyleValue = adjustStyleValue(styleKey, styleVal); style[name] = validStyleValue;
});
style[styleKey] = validStyleValue;
}
} }
/** /**
* 1. * css
* 2. */
const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp',
'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom'];
function isNeedUnitCSS(propName: string) {
return !(noUnitCSS.includes(propName)
|| propName.startsWith('borderImage')
|| propName.startsWith('flex')
|| propName.startsWith('gridRow')
|| propName.startsWith('gridColumn')
|| propName.startsWith('stroke')
|| propName.startsWith('box')
|| propName.endsWith('Opacity'));
}
/**
* width: 10 => width: 10px
*
*
*/ */
export function adjustStyleValue(name, value) { export function adjustStyleValue(name, value) {
let validValue; let validValue = value;
if (value === '' || value == null || typeof value === 'boolean') { if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) {
validValue = `${value}px`;
} else if (value === '' || value == null || typeof value === 'boolean') {
validValue = ''; validValue = '';
} else {
validValue = String(value).trim();
} }
return validValue; return validValue;

View File

@ -5,6 +5,18 @@ import {
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';
const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits',
'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']);
/** /**
* dom * dom
@ -20,8 +32,10 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN
if (!isNativeTag || propDetails === null) { if (!isNativeTag || propDetails === null) {
// 特殊处理svg的属性把驼峰式的属性名称转成'-' // 特殊处理svg的属性把驼峰式的属性名称转成'-'
if (dom.tagName.toLowerCase() === 'svg' || getNamespaceCtx() === NSS.svg) { if (getDomTag(dom) === 'svg' || getNamespaceCtx() === NSS.svg) {
attrName = convertToLowerCase(attrName); if (!svgHumpAttr.has(attrName)) {
attrName = convertToLowerCase(attrName);
}
} }
if (value === null) { if (value === null) {

View File

@ -1,13 +1,14 @@
import {HorizonDom} from './Interface'; import {HorizonDom} from './Interface';
import {Props} from '../DOMOperator';
/** /**
* input textarea * input textarea
* @param currentDoc document * @param doc document
*/ */
export function getFocusedDom(currentDoc?: Document): HorizonDom | void { export function getFocusedDom(doc?: Document): HorizonDom | void {
let currentDocument; let currentDocument;
if (currentDoc) { if (doc) {
currentDocument = currentDoc; currentDocument = doc;
} else { } else {
if (document) { if (document) {
currentDocument = document; currentDocument = document;
@ -63,13 +64,13 @@ export function isDocumentFragment(dom) {
return dom.nodeType === 11; return dom.nodeType === 11;
} }
export function getRootElement(dom: HorizonDom): HorizonDom { export function getDomTag(dom) {
let rootElement = dom; return dom.nodeName.toLowerCase();
}
while (rootElement.parentNode) {
// @ts-ignore const types = ['button', 'input', 'select', 'textarea'];
rootElement = rootElement.parentNode;
} // button、input、select、textarea、如果有 autoFocus 属性需要focus
export function shouldAutoFocus(tagName: string, props: Props): boolean {
return rootElement; return types.includes(tagName) ? Boolean(props.autoFocus) : false;
} }

View File

@ -8,7 +8,6 @@ export const NSS = {
// 创建DOM元素 // 创建DOM元素
export function createDom( export function createDom(
tagName: string, tagName: string,
props: Object,
parentNamespace: string, parentNamespace: string,
): Element { ): Element {
let dom: Element; let dom: Element;
@ -20,6 +19,5 @@ export function createDom(
} else { } else {
dom = document.createElement(tagName); dom = document.createElement(tagName);
} }
return dom; return dom;
} }

View File

@ -100,7 +100,7 @@ export function validateProps(type, props) {
throw new Error('style should be a object.'); throw new Error('style should be a object.');
} }
if (__DEV__) { if (isDev) {
// 校验属性 // 校验属性
const invalidProps = Object.keys(props).filter(key => !isValidProp(type, key, props[key])); const invalidProps = Object.keys(props).filter(key => !isValidProp(type, key, props[key]));

View File

@ -1,7 +1,6 @@
import {updateCommonProp} from '../DOMPropertiesHandler/UpdateCommonProp'; import {updateCommonProp} from '../DOMPropertiesHandler/UpdateCommonProp';
import {getVNodeProps} from '../DOMInternalKeys'; import {getVNodeProps} from '../DOMInternalKeys';
import {IProperty} from '../utils/Interface'; import {IProperty} from '../utils/Interface';
import {getRootElement} from '../utils/Common';
import {isInputValueChanged} from './ValueChangeHandler'; import {isInputValueChanged} from './ValueChangeHandler';
function getInitValue(dom: HTMLInputElement, properties: IProperty) { function getInitValue(dom: HTMLInputElement, properties: IProperty) {
@ -33,12 +32,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, properties: IPr
export function updateInputValue(dom: HTMLInputElement, properties: IProperty) { export function updateInputValue(dom: HTMLInputElement, properties: IProperty) {
const {value, checked} = properties; const {value, checked} = properties;
if (checked != null) { if (value != null) { // 处理 dom.value 逻辑
updateCommonProp(dom, 'checked', checked);
} else if (value != null) { // 处理 dom.value 逻辑
if (dom.value !== String(value)) { if (dom.value !== String(value)) {
dom.value = String(value); dom.value = String(value);
} }
} else if (checked != null) {
updateCommonProp(dom, 'checked', checked);
} }
} }
@ -64,17 +63,10 @@ export function resetInputValue(dom: HTMLInputElement, properties: IProperty) {
const {name, type} = properties; const {name, type} = properties;
// 如果是 radio先更新相同 name 的 radio // 如果是 radio先更新相同 name 的 radio
if (type === 'radio' && name != null) { if (type === 'radio' && name != null) {
// radio 的根节点 const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`);
const radioRoot = getRootElement(dom);
const radioList = radioRoot.querySelectorAll(`input[type="radio"]`);
for (let i = 0; i < radioList.length; i++) { for (let i = 0; i < radioList.length; i++) {
const radio = radioList[i]; const radio = radioList[i];
// @ts-ignore
if (radio.name !== name) {
continue;
}
if (radio === dom) { if (radio === dom) {
continue; continue;
} }

View File

@ -12,11 +12,7 @@ function getInitValue(properties: IProperty) {
// children content存在时会覆盖defaultValue // children content存在时会覆盖defaultValue
if (children != null) { if (children != null) {
// 子节点不是纯文本,则取第一个子节点 // 子节点不是纯文本,则取第一个子节点
if (children instanceof Array) { initValue = children instanceof Array ? children[0] : children;
initValue = children[0];
} else {
initValue = children;
}
} }
// defaultValue 属性未配置,置为空字符串 // defaultValue 属性未配置,置为空字符串

View File

@ -0,0 +1,150 @@
import {throwIfTrue} from '../renderer/utils/throwIfTrue';
import {TYPE_ELEMENT, TYPE_PORTAL} from '../renderer/utils/elementType';
import {isValidElement, HorizonElement} from './HorizonElement';
// 生成key
function getItemKey(item: any, index: number): string {
if (typeof item === 'object' && item !== null && item.key != null) {
return '.$' + item.key;
}
// 使用36进制减少生成字符串的长度以节省空间
return '.' + index.toString(36);
}
function mapChildrenToArray(
children: any,
arr: Array<any>,
prefix: string,
callback?: Function,
): number | void {
const type = typeof children;
switch (type) {
// 继承原有规格undefined和boolean类型按照null处理
case 'undefined':
case 'boolean':
callMapFun(null, arr, prefix, callback);
return;
case 'number':
case 'string':
callMapFun(children, arr, prefix, callback);
return;
case 'object':
if (children === null) {
callMapFun(null, arr, prefix, callback);
return;
}
const vtype = children.vtype;
if (vtype === TYPE_ELEMENT || vtype === TYPE_PORTAL) {
callMapFun(children, arr, prefix, callback) ;
return;
}
if (Array.isArray(children)) {
processArrayChildren(children, arr, prefix, callback);
return;
}
throw new Error(
'Object is invalid as a Horizon child. '
);
default:
return;
}
}
function processArrayChildren(
children: any,
arr: Array<any>,
prefix: string,
callback: Function,
) {
for (let i = 0; i < children.length; i++) {
const childItem = children[i];
const nextPrefix = prefix + getItemKey(childItem, i);
mapChildrenToArray(
childItem,
arr,
nextPrefix,
callback,
);
}
}
function callMapFun(
children: any,
arr: Array<any>,
prefix: string,
callback: Function,
) {
let mappedChild = callback(children);
if (Array.isArray(mappedChild)) {
// 维持原有规格如果callback返回结果是数组处理函数修改为返回数组item
processArrayChildren(mappedChild, arr, prefix + '/', subChild => subChild);
} else if (mappedChild !== null && mappedChild !== undefined) {
// 给一个key值确保返回的对象一定带有key
if (isValidElement(mappedChild)) {
const childKey = prefix === '' ? getItemKey(children, 0) : '';
const mappedKey = getItemKey(mappedChild, 0);
const newKey = prefix + childKey + (mappedChild.key && mappedKey !== getItemKey(children, 0)
? '.$' + mappedChild.key
: '');
// 返回一个修改key的children
mappedChild = HorizonElement(
mappedChild.type,
newKey,
mappedChild.ref,
mappedChild._vNode,
mappedChild.props,
);
}
arr.push(mappedChild);
}
}
// 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg
function mapChildren(
children: any,
func: Function,
context?: any,
): Array<any> {
if (children === null || children === undefined) {
return children;
}
let count = 0;
const result = [];
mapChildrenToArray(children, result, '', (child) => {
return func.call(context, child, count++);
});
return result;
}
const Children = {
forEach: (children, func, context?: any) => {
// 不返回数组即可
mapChildren(children, func, context);
},
map: mapChildren,
// 并非所有元素都会计数,只计数调用callMapFun函数次数
count: (children) => {
let n = 0;
mapChildren(children, () => {
n++;
});
return n;
},
only: (children) => {
throwIfTrue(
!isValidElement(children),
'Horizon.Children.only function received invalid element.'
);
return children;
},
toArray: (children) => {
const result = [];
mapChildrenToArray(children, result, '', child => child);
return result;
},
}
export {
Children
};

60
libs/horizon/src/external/Horizon.ts vendored Normal file
View File

@ -0,0 +1,60 @@
import {
TYPE_FRAGMENT,
TYPE_PROFILER,
TYPE_STRICT_MODE,
TYPE_SUSPENSE,
} from '../renderer/utils/elementType';
import {Component, PureComponent} from '../renderer/components/BaseClassComponent';
import {createRef} from '../renderer/components/CreateRef';
import {Children} from './ChildrenUtil';
import {
createElement,
cloneElement,
isValidElement,
} from './HorizonElement';
import {createContext} from '../renderer/components/context/CreateContext';
import {lazy} from '../renderer/components/Lazy';
import {forwardRef} from '../renderer/components/ForwardRef';
import {memo} from '../renderer/components/Memo';
import hookMapping from '../renderer/hooks/HookMapping';
import {
useCallback,
useContext,
useEffect,
useImperativeHandle,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
} from '../renderer/hooks/HookExternal';
export {
Children,
createRef,
Component,
PureComponent,
createContext,
forwardRef,
lazy,
memo,
useCallback,
useContext,
useEffect,
useImperativeHandle,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
TYPE_FRAGMENT as Fragment,
TYPE_PROFILER as Profiler,
TYPE_STRICT_MODE as StrictMode,
TYPE_SUSPENSE as Suspense,
createElement,
cloneElement,
isValidElement,
hookMapping,
};

View File

@ -0,0 +1,82 @@
import { TYPE_ELEMENT } from '../renderer/utils/elementType';
import ProcessingVNode from '../renderer/vnode/ProcessingVNode';
/**
* vtype element
* type,dom节点的名称或者组件的函数地址
* key key属性
* ref ref属性
* props
*/
export function HorizonElement(type, key, ref, vNode, props) {
return {
// Horizon元素标识符
vtype: TYPE_ELEMENT,
// 属于元素的内置属性
type: type,
key: key,
ref: ref,
props: props,
// 记录负责创建此元素的组件。
_vNode: vNode,
};
};
function isValidKey(key) {
return key !== 'key' && key !== 'ref' && key !== '__source';
}
function buildElement(isClone, type, setting, ...children) {
// setting中的值优先级最高clone情况下从 type 中取值,创建情况下直接赋值为 null
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
const props = isClone ? {...type.props} : {};
let vNode = isClone ? type._vNode : ProcessingVNode.val;
if (setting != null) {
Object.keys(setting).forEach(k => {
if (isValidKey(k)) {
props[k] = setting[k];
}
});
if (setting.ref !== undefined && isClone) {
vNode = ProcessingVNode.val;
}
}
if (children.length) {
props.children = children.length === 1 ? children[0] : children;
}
const element = isClone ? type.type : type;
//合并默认属性
if (element && element.defaultProps) {
mergeDefault(props, element.defaultProps);
}
return HorizonElement(element, key, ref, vNode, props);
}
//创建Element结构体供JSX编译时调用
export function createElement(type, setting, ...children) {
return buildElement(false, type, setting, ...children);
}
function mergeDefault(sourceObj, defaultObj) {
Object.keys(defaultObj).forEach((key) => {
if (sourceObj[key] === undefined) {
sourceObj[key] = defaultObj[key];
}
});
}
export function cloneElement(element, setting, ...children) {
return buildElement(true, element, setting, ...children);
}
// 检测结构体是否为合法的Element
export function isValidElement(element) {
return !!(element && element.vtype === TYPE_ELEMENT);
}