Match-id-70d03a8fc008ccce49a3f8517fc56c3d6ecbcf98

This commit is contained in:
* 2022-03-06 11:44:13 +08:00 committed by *
commit a1c73a6e2f
21 changed files with 106 additions and 149 deletions

View File

@ -19,7 +19,7 @@ import {
} from './valueHandler';
import {
compareProps,
setDomProps, updateDomProps
setDomProps,
} from './DOMPropertiesHandler/DOMPropertiesHandler';
import { isNativeElement, validateProps } from './validators/ValidateProps';
import { watchValueChange } from './valueHandler/ValueChangeHandler';
@ -91,7 +91,8 @@ export function initDomProps(dom: Element, tagName: string, rawProps: Props): bo
const props: Object = getPropsWithoutValue(tagName, dom, rawProps);
// 初始化DOM属性不包括valuedefaultValue
setDomProps(tagName, dom, props);
const isNativeTag = isNativeElement(tagName, props);
setDomProps(dom, props, isNativeTag, true);
if (tagName === 'input' || tagName === 'textarea') {
// 增加监听value和checked的set、get方法
@ -110,7 +111,7 @@ export function getPropChangeList(
type: string,
lastRawProps: Props,
nextRawProps: Props,
): Map<string, any> {
): Object {
// 校验两个对象的不同
validateProps(type, nextRawProps);
@ -118,8 +119,7 @@ export function getPropChangeList(
const oldProps: Object = getPropsWithoutValue(type, dom, lastRawProps);
const newProps: Object = getPropsWithoutValue(type, dom, nextRawProps);
const changeList = compareProps(oldProps, newProps);
return changeList;
return compareProps(oldProps, newProps);
}
export function isTextChild(type: string, props: Props): boolean {
@ -168,7 +168,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) {
updateCommonProp(element, 'checked', newProps.checked, true);
}
const isNativeTag = isNativeElement(type, newProps);
updateDomProps(element, changeList, isNativeTag);
setDomProps(element, changeList, isNativeTag, false);
updateValue(type, element, newProps);
}
}

View File

@ -6,37 +6,15 @@ import { setStyles } from './StyleHandler';
import {
listenNonDelegatedEvent
} from '../../event/EventBinding';
import { isEventProp, isNativeElement } from '../validators/ValidateProps';
import { isEventProp } from '../validators/ValidateProps';
function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) {
if (propName === 'style') {
setStyles(dom, propVal);
} else if (isEventProp(propName)) {
// 事件监听属性处理
if (!allDelegatedHorizonEvents.has(propName)) {
listenNonDelegatedEvent(propName, dom, propVal);
}
} else if (propName === 'children') { // 只处理纯文本子节点其他children在VNode树中处理
const type = typeof propVal;
if (type === 'string' || type === 'number') {
dom.textContent = propVal;
}
} else if (propName === 'dangerouslySetInnerHTML') {
dom.innerHTML = propVal.__html;
} else {
if (!isInit || (isInit && propVal != null)) {
updateCommonProp(dom, propName, propVal, isNativeTag);
}
}
}
// 初始化DOM属性
// 初始化DOM属性和更新 DOM 属性
export function setDomProps(
tagName: string,
dom: Element,
props: Object,
isNativeTag: boolean,
isInit: boolean,
): void {
const isNativeTag = isNativeElement(tagName, props);
const keysOfProps = Object.keys(props);
let propName;
let propVal;
@ -45,18 +23,23 @@ export function setDomProps(
propName = keysOfProps[i];
propVal = props[propName];
updateOneProp(dom, propName, isNativeTag, propVal, true);
}
}
// 更新 DOM 属性
export function updateDomProps(
dom: Element,
changeList: Map<string, any>,
isNativeTag: boolean,
): void {
for(const [propName, propVal] of changeList) {
updateOneProp(dom, propName, isNativeTag, propVal);
if (propName === 'style') {
setStyles(dom, propVal);
} else if (isEventProp(propName)) {
// 事件监听属性处理
if (!allDelegatedHorizonEvents.has(propName)) {
listenNonDelegatedEvent(propName, dom, propVal);
}
} else if (propName === 'children') { // 只处理纯文本子节点其他children在VNode树中处理
const type = typeof propVal;
if (type === 'string' || type === 'number') {
dom.textContent = propVal;
}
} else if (propName === 'dangerouslySetInnerHTML') {
dom.innerHTML = propVal.__html;
} else if (!isInit || (isInit && propVal != null)) {
updateCommonProp(dom, propName, propVal, isNativeTag);
}
}
}
@ -64,9 +47,9 @@ export function updateDomProps(
export function compareProps(
oldProps: Object,
newProps: Object,
): Map<string, any> {
): Object {
let updatesForStyle = {};
const toUpdateProps = new Map();
const toUpdateProps = {};
const keysOfOldProps = Object.keys(oldProps);
const keysOfNewProps = Object.keys(newProps);
@ -98,11 +81,11 @@ export function compareProps(
continue;
} else if (isEventProp(propName)) {
if (!allDelegatedHorizonEvents.has(propName)) {
toUpdateProps.set(propName, null);
toUpdateProps[propName] = null;
}
} else {
// 其它属性都要加入到删除队列里面,等待删除
toUpdateProps.set(propName, null);
toUpdateProps[propName] = null;
}
}
@ -144,7 +127,7 @@ export function compareProps(
}
} else { // 之前未设置 style 属性或者设置了空值
if (Object.keys(updatesForStyle).length === 0) {
toUpdateProps.set(propName, null);
toUpdateProps[propName] = null;
}
updatesForStyle = newPropValue;
}
@ -153,25 +136,25 @@ export function compareProps(
oldHTML = oldPropValue ? oldPropValue.__html : undefined;
if (newHTML != null) {
if (oldHTML !== newHTML) {
toUpdateProps.set(propName, newPropValue);
toUpdateProps[propName] = newPropValue;
}
}
} else if (propName === 'children') {
if (typeof newPropValue === 'string' || typeof newPropValue === 'number') {
toUpdateProps.set(propName, String(newPropValue));
toUpdateProps[propName] = String(newPropValue);
}
} else if (isEventProp(propName)) {
if (!allDelegatedHorizonEvents.has(propName)) {
toUpdateProps.set(propName, newPropValue);
toUpdateProps[propName] = newPropValue;
}
} else {
toUpdateProps.set(propName, newPropValue);
toUpdateProps[propName] = newPropValue;
}
}
// 处理style
if (Object.keys(updatesForStyle).length > 0) {
toUpdateProps.set('style', updatesForStyle);
toUpdateProps['style'] = updatesForStyle;
}
return toUpdateProps;

View File

@ -10,7 +10,7 @@ import {
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
import {
addOnPrefix, setPropertyWritable,
setPropertyWritable,
} from './utils';
import { decorateNativeEvent } from './customEvents/EventFactory';
import { getListenersFromTree } from './ListenerGetter';
@ -27,7 +27,9 @@ function getCommonListeners(
target: null | EventTarget,
isCapture: boolean,
): ListenerUnitList {
const horizonEvtName = addOnPrefix(CommonEventToHorizonMap[nativeEvtName]);
const name = CommonEventToHorizonMap[nativeEvtName];
const horizonEvtName = !name ? '' : `on${name[0].toUpperCase()}${name.slice(1)}`; // 例dragEnd -> onDragEnd
if (!horizonEvtName) {
return [];
}

View File

@ -6,15 +6,6 @@ export function isInputElement(dom?: HTMLElement): boolean {
return false;
}
// 例dragEnd -> onDragEnd
export function addOnPrefix(name) {
if (!name) {
return '';
}
return 'on' + name[0].toUpperCase() + name.slice(1);
}
export function setPropertyWritable(obj, propName) {
const desc = Object.getOwnPropertyDescriptor(obj, propName);
if (!desc || !desc.writable) {

View File

@ -59,11 +59,11 @@ function collectDirtyNodes(vNode: VNode, parent: VNode): void {
const dirtyNodes = vNode.dirtyNodes;
if (dirtyNodes !== null && dirtyNodes.length) {
if (parent.dirtyNodes === null) {
parent.dirtyNodes = [...vNode.dirtyNodes];
parent.dirtyNodes = dirtyNodes;
} else {
parent.dirtyNodes.push(...vNode.dirtyNodes);
dirtyNodes.length = 0;
}
dirtyNodes.length = 0;
vNode.dirtyNodes = null;
}
@ -236,7 +236,7 @@ function buildVNodeTree(treeRoot: VNode) {
// 当在componentWillUnmount中调用setStateparent可能是null因为startVNode会被clear
if (parent !== null) {
resetNamespaceCtx(parent);
setNamespaceCtx(parent, parent.outerDom);
setNamespaceCtx(parent, parent.realNode);
}
// 恢复父节点的context

View File

@ -39,7 +39,7 @@ export type ContextType<T> = {
export type PortalType = {
vtype: number;
key: null | string;
outerDom: any;
realNode: any;
children: any;
};

View File

@ -18,11 +18,6 @@ export enum UpdateState {
Error = 'Error',
}
// 初始化更新数组
export function createUpdateArray(vNode: VNode): void {
vNode.updates = []; // 新产生的update会加入这个数组
}
// 创建update对象
export function newUpdate(): Update {
return {
@ -35,8 +30,11 @@ export function newUpdate(): Update {
// 将update对象加入updates
export function pushUpdate(vNode: VNode, update: Update) {
const updates = vNode.updates;
updates?.push(update);
if (updates !== null) {
updates.push(update);
} else {
vNode.updates = [update];
}
}
// 根据update获取新的state
@ -98,12 +96,14 @@ export function processUpdates(vNode: VNode, inst: any, props: any): void {
const updates: Updates = vNode.updates;
vNode.isForceUpdate = false;
const toProcessUpdates = [...updates];
updates.length = 0;
if (toProcessUpdates.length) {
calcUpdates(vNode, props, inst, toProcessUpdates);
if (updates !== null) {
const toProcessUpdates = [...updates];
updates.length = 0;
if (toProcessUpdates.length) {
calcUpdates(vNode, props, inst, toProcessUpdates);
}
}
}
export function pushForceUpdate(vNode: VNode) {

View File

@ -3,13 +3,13 @@ import type {PortalType} from '../Types';
export function createPortal(
children: any,
outerDom: any,
realNode: any,
key: string = '',
): PortalType {
return {
vtype: TYPE_PORTAL,
key: key == '' ? '' : '' + key,
children,
outerDom,
realNode,
};
}

View File

@ -153,7 +153,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
}
break;
} else if (newChild.vtype === TYPE_PORTAL) {
if (oldNode === null || oldNode.tag !== DomPortal || oldNode.outerDom !== newChild.outerDom) {
if (oldNode === null || oldNode.tag !== DomPortal || oldNode.realNode !== newChild.realNode) {
resultNode = createPortalVNode(newChild);
} else {
resultNode = updateVNode(oldNode, newChild.children || []);
@ -578,7 +578,7 @@ function diffObjectNodeHandler(
} else if (newChild.vtype === TYPE_PORTAL) {
if (canReuseNode) {
// 可以复用
if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) {
if (canReuseNode.tag === DomPortal && canReuseNode.realNode === newChild.realNode) {
resultNode = updateVNode(canReuseNode, newChild.children || []);
startDelVNode = canReuseNode.next;
resultNode.next = null;

View File

@ -18,7 +18,7 @@ import componentRenders from './index';
function handlerContext(processing: VNode) {
switch (processing.tag) {
case TreeRoot:
setNamespaceCtx(processing, processing.outerDom);
setNamespaceCtx(processing, processing.realNode);
break;
case DomComponent:
setNamespaceCtx(processing);
@ -29,7 +29,7 @@ function handlerContext(processing: VNode) {
break;
}
case DomPortal:
setNamespaceCtx(processing, processing.outerDom);
setNamespaceCtx(processing, processing.realNode);
break;
case ContextProvider: {
const newValue = processing.props.value;

View File

@ -23,7 +23,6 @@ import {
import { FlagUtils, DidCapture } from '../vnode/VNodeFlags';
import { markRef } from './BaseComponent';
import {
createUpdateArray,
processUpdates,
} from '../UpdateHandler';
import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver';
@ -47,15 +46,13 @@ function mountInstance(clazz, processing: VNode, nextProps: object) {
}
// 构造实例
callConstructor(processing, clazz, nextProps);
const inst = callConstructor(processing, clazz, nextProps);
const inst = processing.realNode;
inst.props = nextProps;
inst.state = processing.state;
inst.context = getCurrentContext(clazz, processing);
inst.refs = {};
createUpdateArray(processing);
processUpdates(processing, inst, nextProps);
inst.state = processing.state;
@ -106,7 +103,15 @@ function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boole
}
// 用于类组件
export function captureClassComponent(processing: VNode, clazz: any, nextProps: object): VNode | null {
export function captureRender(processing: VNode): VNode | null {
let clazz = processing.type;
let nextProps = processing.props;
if (processing.isLazyComponent) {
nextProps = mergeDefaultProps(clazz, nextProps);
if (processing.promiseResolve) { // 该函数被 lazy 组件使用,未加载的组件需要加载组件的真实内容
clazz = clazz._load(clazz._content);
}
}
const isOldCxtExist = isOldProvider(clazz);
cacheOldCtx(processing, isOldCxtExist);
@ -123,18 +128,13 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
const newContext = getCurrentContext(clazz, processing);
// 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props
let oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps;
if (processing.isLazyComponent) {
oldProps = mergeDefaultProps(processing.type, oldProps);
}
inst.props = oldProps;
const oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps;
if (oldProps !== processing.props || inst.context !== newContext) {
// 在已挂载的组件接收新的 props 之前被调用
callComponentWillReceiveProps(inst, nextProps, newContext);
}
inst.state = processing.state;
processUpdates(processing, inst, nextProps);
// 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新
@ -183,13 +183,6 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
}
}
export function captureRender(processing: VNode): VNode | null {
const clazz = processing.type;
const props = processing.props;
const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props;
return captureClassComponent(processing, clazz, nextProps);
}
export function bubbleRender(processing: VNode) {
if (isOldProvider(processing.type)) {
resetOldCtx(processing);

View File

@ -38,14 +38,15 @@ function updateDom(
oldProps,
newProps,
);
processing.changeList = changeList;
// 输入类型的直接标记更新
if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') {
FlagUtils.markUpdate(processing);
processing.changeList = changeList;
} else {
// 其它的类型,数据有变化才标记更新
if (changeList.size) {
if (Object.keys(changeList).length) {
processing.changeList = changeList;
FlagUtils.markUpdate(processing);
}
}
@ -105,7 +106,7 @@ export function bubbleRender(processing: VNode) {
}
}
function captureDomComponent(processing: VNode): VNode | null {
export function captureRender(processing: VNode): VNode | null {
setNamespaceCtx(processing);
const type = processing.type;
@ -127,7 +128,3 @@ function captureDomComponent(processing: VNode): VNode | null {
processing.child = createChildrenByDiff(processing, processing.child, nextChildren, !processing.isCreated);
return processing.child;
}
export function captureRender(processing: VNode): VNode | null {
return captureDomComponent(processing);
}

View File

@ -7,12 +7,12 @@ export function bubbleRender(processing: VNode) {
resetNamespaceCtx(processing);
if (processing.isCreated) {
prePortal(processing.outerDom);
prePortal(processing.realNode);
}
}
function capturePortalComponent(processing: VNode) {
setNamespaceCtx(processing, processing.outerDom);
setNamespaceCtx(processing, processing.realNode);
const newElements = processing.props;
if (processing.isCreated) {

View File

@ -10,7 +10,7 @@ import {
} from '../vnode/VNodeTags';
import { throwIfTrue } from '../utils/throwIfTrue';
import { captureFunctionComponent } from './FunctionComponent';
import { captureClassComponent } from './ClassComponent';
import { captureRender as captureClassComponent } from './ClassComponent';
import { captureMemoComponent } from './MemoComponent';
export function bubbleRender() { }

View File

@ -12,7 +12,7 @@ export function bubbleRender(processing: VNode) {
}
function updateTreeRoot(processing) {
setNamespaceCtx(processing, processing.outerDom);
setNamespaceCtx(processing, processing.realNode);
const updates = processing.updates;
throwIfTrue(

View File

@ -118,15 +118,19 @@ export function callComponentWillUpdate(inst, newProps, newState, nextContext) {
}
export function callComponentWillReceiveProps(inst, newProps: object, newContext: object) {
const oldState = inst.state;
if (inst.componentWillReceiveProps) {
const oldState = inst.state;
inst.componentWillReceiveProps(newProps, newContext);
if (inst.state !== oldState) {
changeStateContent.call(inst, UpdateState.Override, inst.state, null);
}
}
if (inst.UNSAFE_componentWillReceiveProps) {
const oldState = inst.state;
inst.UNSAFE_componentWillReceiveProps(newProps, newContext);
}
if (inst.state !== oldState) {
changeStateContent.call(inst, UpdateState.Override, inst.state, null);
if (inst.state !== oldState) {
changeStateContent.call(inst, UpdateState.Override, inst.state, null);
}
}
}

View File

@ -225,12 +225,9 @@ function submitAddition(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent) {
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
parentDom = parent.realNode;
break;
} else if (tag === TreeRoot || tag === DomPortal) {
parentDom = parent.outerDom;
break;
}
parent = parent.parent;
}
@ -292,12 +289,9 @@ function unmountDomComponents(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent) {
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
currentParent = parent.realNode;
break;
} else if (tag === TreeRoot || tag === DomPortal) {
currentParent = parent.outerDom;
break;
}
parent = parent.parent;
}
@ -312,7 +306,7 @@ function unmountDomComponents(vNode: VNode): void {
removeChildDom(currentParent, node.realNode);
} else if (node.tag === DomPortal) {
if (node.child !== null) {
currentParent = node.outerDom;
currentParent = node.realNode;
}
} else {
unmountVNode(node);
@ -349,12 +343,9 @@ function submitClear(vNode: VNode): void {
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent) {
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
parentDom = parent.realNode;
break;
} else if (tag === TreeRoot || tag === DomPortal) {
parentDom = parent.outerDom;
break;
}
parent = parent.parent;
}

View File

@ -39,7 +39,6 @@ export class VNode {
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
shouldUpdate: boolean = false;
childShouldUpdate: boolean = false;
outerDom: any;
task: any;
// 使用这个变量来记录修改前的值,用于恢复。
@ -67,7 +66,7 @@ export class VNode {
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode处理ref的时候使用
constructor(tag: VNodeTag, props: any, key: null | string, outerDom) {
constructor(tag: VNodeTag, props: any, key: null | string, realNode) {
this.tag = tag; // 对应组件的类型比如ClassComponent等
this.key = key;
@ -75,10 +74,9 @@ export class VNode {
switch (tag) {
case TreeRoot:
this.outerDom = outerDom;
this.realNode = realNode;
this.task = null;
this.toUpdateNodes = new Set<VNode>();
this.realNode = null;
this.updates = null;
this.stateCallbacks = null;
this.state = null;
@ -138,6 +136,7 @@ export class VNode {
this.stateCallbacks = null;
this.isLazyComponent = true;
this.lazyType = null;
this.updates = null;
break;
case Fragment:
break;

View File

@ -16,7 +16,6 @@ import {
MemoComponent,
SuspenseComponent,
} from './VNodeTags';
import { createUpdateArray } from '../UpdateHandler';
import {
TYPE_CONTEXT,
TYPE_FORWARD_REF, TYPE_FRAGMENT,
@ -39,8 +38,8 @@ const typeMap = {
[TYPE_LAZY]: LazyComponent,
};
const newVirtualNode = function(tag: VNodeTag, key?: null | string, vNodeProps?: any, outerDom?: any): VNode {
return new VNode(tag, vNodeProps, key, outerDom);
const newVirtualNode = function(tag: VNodeTag, key?: null | string, vNodeProps?: any, realNode?: any): VNode {
return new VNode(tag, vNodeProps, key, realNode);
};
function isClassComponent(comp: Function) {
@ -100,7 +99,7 @@ export function createPortalVNode(portal) {
const children = portal.children ?? [];
const vNode = newVirtualNode(DomPortal, portal.key, children);
vNode.shouldUpdate = true;
vNode.outerDom = portal.outerDom;
vNode.realNode = portal.realNode;
return vNode;
}
@ -137,7 +136,7 @@ export function createUndeterminedVNode(type, key, props) {
export function createTreeRootVNode(container) {
const vNode = newVirtualNode(TreeRoot, null, null, container);
vNode.path += 0;
createUpdateArray(vNode);
vNode.updates = [];
return vNode;
}
@ -150,7 +149,7 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) {
vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]);
vNode.path += 0;
createUpdateArray(vNode);
vNode.updates = [];
break;
}

View File

@ -24,12 +24,10 @@ const LifecycleEffectArr = Update | Callback | Ref | Snapshot;
export class FlagUtils {
static removeFlag(node: VNode, flag: number) {
const flags = node.flags;
node.flags = flags & (~flag);
node.flags &= ~flag;
}
static removeLifecycleEffectFlags(node) {
const flags = node.flags;
node.flags = flags & (~LifecycleEffectArr);
node.flags &= ~LifecycleEffectArr;
}
static hasAnyFlag(node: VNode) { // 有标志位
return node.flags !== InitFlag;

View File

@ -210,7 +210,7 @@ function isPortalRoot(vNode, targetContainer) {
while (topVNode !== null) {
const grandTag = topVNode.tag;
if (grandTag === TreeRoot || grandTag === DomPortal) {
const topContainer = topVNode.outerDom;
const topContainer = topVNode.realNode;
// 如果topContainer是targetContainer不需要在这里处理
if (isSameContainer(topContainer, targetContainer)) {
return true;
@ -229,7 +229,7 @@ export function getExactNode(targetVNode, targetContainer) {
let vNode = targetVNode;
while (vNode !== null) {
if (vNode.tag === TreeRoot || vNode.tag === DomPortal) {
let container = vNode.outerDom;
let container = vNode.realNode;
if (isSameContainer(container, targetContainer)) {
break;
}