Match-id-607e5214b8210d9df6fe9c8e89c254bf68f8f9ce

This commit is contained in:
* 2023-05-11 10:55:29 +08:00
commit 0075694565
35 changed files with 824 additions and 762 deletions

View File

@ -40,3 +40,8 @@ steps:
auto: true auto: true
buildcheck: true buildcheck: true
mode: sync mode: sync
POST_BUILD:
- compile_report:
rules:
- warning /.**/
- error /.**/

View File

@ -1,3 +1,7 @@
## 0.0.45 (2023-05-10)
- **core**: 修改belongClassVNode属性为Symbol提升性能
- **core**: 优化内部循环实现,提升性能
## 0.0.44 (2023-04-03) ## 0.0.44 (2023-04-03)
- **core**: 修复horizonX-devtool Firefox75报错 - **core**: 修复horizonX-devtool Firefox75报错

View File

@ -4,7 +4,7 @@
"keywords": [ "keywords": [
"horizon" "horizon"
], ],
"version": "0.0.44", "version": "0.0.45",
"homepage": "", "homepage": "",
"bugs": "", "bugs": "",
"main": "index.js", "main": "index.js",

View File

@ -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;
} }

View File

@ -13,6 +13,26 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
*/ */
/**
* css
*/
const noUnitCSS = [
'animationIterationCount',
'columnCount',
'columns',
'gridArea',
'fontWeight',
'lineClamp',
'lineHeight',
'opacity',
'order',
'orphans',
'tabSize',
'widows',
'zIndex',
'zoom',
];
function isNeedUnitCSS(styleName: string) { function isNeedUnitCSS(styleName: string) {
return !( return !(
noUnitCSS.includes(styleName) || noUnitCSS.includes(styleName) ||
@ -62,23 +82,3 @@ export function setStyles(dom, styles) {
} }
}); });
} }
/**
* css
*/
const noUnitCSS = [
'animationIterationCount',
'columnCount',
'columns',
'gridArea',
'fontWeight',
'lineClamp',
'lineHeight',
'opacity',
'order',
'orphans',
'tabSize',
'widows',
'zIndex',
'zoom',
];

View File

@ -56,6 +56,14 @@ function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, i
return listener; return listener;
} }
// 是否捕获事件
function isCaptureEvent(horizonEventName) {
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
return false;
}
return horizonEventName.slice(-7) === 'Capture';
}
// 事件懒委托,当用户定义事件后,再进行委托到根节点 // 事件懒委托,当用户定义事件后,再进行委托到根节点
export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
currentRoot.delegatedEvents.add(eventName); currentRoot.delegatedEvents.add(eventName);
@ -74,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;
} }
}); });
} }
@ -94,14 +101,6 @@ function getNativeEvtName(horizonEventName, capture) {
return nativeName.toLowerCase(); return nativeName.toLowerCase();
} }
// 是否捕获事件
function isCaptureEvent(horizonEventName) {
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
return false;
}
return horizonEventName.slice(-7) === 'Capture';
}
// 封装监听函数 // 封装监听函数
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
return event => { return event => {

View File

@ -37,35 +37,6 @@ export function shouldControlValue(): boolean {
return changeEventTargets !== null && changeEventTargets.length > 0; return changeEventTargets !== null && changeEventTargets.length > 0;
} }
// 从缓存队列中对受控组件进行赋值
export function tryControlValue() {
if (!changeEventTargets) {
return;
}
changeEventTargets.forEach(target => {
controlValue(target);
});
changeEventTargets = null;
}
// 受控组件值重新赋值
function controlValue(target: Element) {
const props = getVNodeProps(target);
if (props) {
const type = getDomTag(target);
switch (type) {
case 'input':
controlInputValue(<HTMLInputElement>target, props);
break;
case 'textarea':
updateTextareaValue(<HTMLTextAreaElement>target, props);
break;
default:
break;
}
}
}
function controlInputValue(inputDom: HTMLInputElement, props: Props) { function controlInputValue(inputDom: HTMLInputElement, props: Props) {
const { name, type } = props; const { name, type } = props;
@ -87,3 +58,32 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) {
updateInputValue(inputDom, props); updateInputValue(inputDom, props);
} }
} }
// 受控组件值重新赋值
function controlValue(target: Element) {
const props = getVNodeProps(target);
if (props) {
const type = getDomTag(target);
switch (type) {
case 'input':
controlInputValue(<HTMLInputElement>target, props);
break;
case 'textarea':
updateTextareaValue(<HTMLTextAreaElement>target, props);
break;
default:
break;
}
}
}
// 从缓存队列中对受控组件进行赋值
export function tryControlValue() {
if (!changeEventTargets) {
return;
}
changeEventTargets.forEach(target => {
controlValue(target);
});
changeEventTargets = null;
}

View File

@ -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
); );

View File

@ -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);
} }

View File

@ -23,6 +23,20 @@ interface Thenable {
then(resolve: (val?: any) => void, reject: (err: any) => void): void; then(resolve: (val?: any) => void, reject: (err: any) => void): void;
} }
// 防止死循环
const LOOPING_LIMIT = 50;
let loopingCount = 0;
function callRenderQueue() {
callRenderQueueImmediate();
while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) {
loopingCount++;
runAsyncEffects();
// effects可能产生刷新任务这里再执行一次
callRenderQueueImmediate();
}
}
// act用于测试作用是如果fun触发了刷新包含了异步刷新可以保证在act后面的代码是在刷新完成后才执行。 // act用于测试作用是如果fun触发了刷新包含了异步刷新可以保证在act后面的代码是在刷新完成后才执行。
function act(fun: () => void | Thenable): Thenable { function act(fun: () => void | Thenable): Thenable {
const funRet = asyncUpdates(fun); const funRet = asyncUpdates(fun);
@ -62,20 +76,6 @@ function act(fun: () => void | Thenable): Thenable {
} }
} }
// 防止死循环
const LOOPING_LIMIT = 50;
let loopingCount = 0;
function callRenderQueue() {
callRenderQueueImmediate();
while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) {
loopingCount++;
runAsyncEffects();
// effects可能产生刷新任务这里再执行一次
callRenderQueueImmediate();
}
}
export { export {
act act
}; };

View File

@ -54,15 +54,12 @@ export type ReduxMiddleware = (
type Reducer = (state: any, action: ReduxAction) => any; type Reducer = (state: any, action: ReduxAction) => any;
function mergeData(state, data) { function mergeData(state, data) {
console.log('merging data', { state, data });
if (!data) { if (!data) {
console.log('!data');
state.stateWrapper = data; state.stateWrapper = data;
return; return;
} }
if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) { if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) {
console.log('data is array');
state.stateWrapper.length = data.length; state.stateWrapper.length = data.length;
data.forEach((item, idx) => { data.forEach((item, idx) => {
if (item != state.stateWrapper[idx]) { if (item != state.stateWrapper[idx]) {
@ -73,9 +70,10 @@ function mergeData(state, data) {
} }
if (typeof data === 'object' && typeof state?.stateWrapper === 'object') { if (typeof data === 'object' && typeof state?.stateWrapper === 'object') {
console.log('data is object');
Object.keys(state.stateWrapper).forEach(key => { Object.keys(state.stateWrapper).forEach(key => {
if (!data.hasOwnProperty(key)) delete state.stateWrapper[key]; if (!Object.prototype.hasOwnProperty.call(data, key)) {
delete state.stateWrapper[key];
}
}); });
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
@ -86,7 +84,6 @@ function mergeData(state, data) {
return; return;
} }
console.log('data is primitive or type mismatch');
state.stateWrapper = data; state.stateWrapper = data;
} }
@ -106,7 +103,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
if (result === undefined) { if (result === undefined) {
return; return;
} // NOTE: reducer should never return undefined, in this case, do not change state } // NOTE: reducer should never return undefined, in this case, do not change state
// mergeData(state,result);
state.stateWrapper = result; state.stateWrapper = result;
}, },
}, },
@ -115,10 +111,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
}, },
})(); })();
// store.$subscribe(()=>{
// console.log('changed');
// });
const result = { const result = {
reducer, reducer,
getState: function () { getState: function () {
@ -157,12 +149,6 @@ export function combineReducers(reducers: { [key: string]: Reducer }): Reducer {
}; };
} }
export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void {
return store => {
return applyMiddlewares(store, middlewares);
};
}
function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware[]): void { function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware[]): void {
middlewares = middlewares.slice(); middlewares = middlewares.slice();
middlewares.reverse(); middlewares.reverse();
@ -173,6 +159,12 @@ function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware
store.dispatch = dispatch; store.dispatch = dispatch;
} }
export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void {
return store => {
return applyMiddlewares(store, middlewares);
};
}
type ActionCreator = (...params: any[]) => ReduxAction; type ActionCreator = (...params: any[]) => ReduxAction;
type ActionCreators = { [key: string]: ActionCreator }; type ActionCreators = { [key: string]: ActionCreator };
export type BoundActionCreator = (...params: any[]) => void; export type BoundActionCreator = (...params: any[]) => void;

View File

@ -10,21 +10,6 @@ export function isPanelActive() {
return window['__HORIZON_DEV_HOOK__']; return window['__HORIZON_DEV_HOOK__'];
} }
// serializes store and creates expanded object with baked-in containing current computed values
function makeStoreSnapshot({ type, data }) {
const expanded = {};
Object.keys(data.store.$c).forEach(key => {
expanded[key] = data.store[key];
});
data.store.expanded = expanded;
const snapshot = makeProxySnapshot({
data,
type,
sessionId,
});
return snapshot;
}
// safely serializes variables containing values wrapped in Proxy object // safely serializes variables containing values wrapped in Proxy object
function getType(value) { function getType(value) {
if (!value) return 'nullish'; if (!value) return 'nullish';
@ -39,6 +24,7 @@ function getType(value) {
if (typeof value === 'object') return 'object'; if (typeof value === 'object') return 'object';
return 'primitive'; return 'primitive';
} }
function makeProxySnapshot(obj, visited: any[] = []) { function makeProxySnapshot(obj, visited: any[] = []) {
const type = getType(obj); const type = getType(obj);
let clone; let clone;
@ -112,6 +98,21 @@ function makeProxySnapshot(obj, visited: any[] = []) {
} }
} }
// serializes store and creates expanded object with baked-in containing current computed values
function makeStoreSnapshot({ type, data }) {
const expanded = {};
Object.keys(data.store.$c).forEach(key => {
expanded[key] = data.store[key];
});
data.store.expanded = expanded;
const snapshot = makeProxySnapshot({
data,
type,
sessionId,
});
return snapshot;
}
export const devtools = { export const devtools = {
// returns vNode id from horizon devtools // returns vNode id from horizon devtools
getVNodeId: vNode => { getVNodeId: vNode => {

View File

@ -27,6 +27,10 @@ const proxyMap = new WeakMap();
export const hookObserverMap = new WeakMap(); export const hookObserverMap = new WeakMap();
export function getObserver(rawObj: any): Observer {
return rawObj[OBSERVER_KEY];
}
export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any { export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any {
// 不是对象(是原始数据类型)不用代理 // 不是对象(是原始数据类型)不用代理
if (!(rawObj && isObject(rawObj))) { if (!(rawObj && isObject(rawObj))) {
@ -89,7 +93,3 @@ export function createProxy(rawObj: any, isHookObserver = true, listener: { curr
return proxyObj; return proxyObj;
} }
export function getObserver(rawObj: any): Observer {
return rawObj[OBSERVER_KEY];
}

View File

@ -19,6 +19,40 @@ import { resolveMutation } from '../../CommonUtils';
import { isPanelActive } from '../../devtools'; import { isPanelActive } from '../../devtools';
import { OBSERVER_KEY } from '../../Constants'; import { OBSERVER_KEY } from '../../Constants';
function set(rawObj: any[], key: string, value: any, receiver: any) {
const oldValue = rawObj[key];
const oldLength = rawObj.length;
const newValue = value;
const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
const ret = Reflect.set(rawObj, key, newValue, receiver);
const newLength = rawObj.length;
const observer = getObserver(rawObj);
const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj);
if (!isSame(newValue, oldValue)) {
// 值不一样,触发监听器
if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue, mutation);
});
}
// 触发属性变化
observer.setProp(key, mutation);
}
if (oldLength !== newLength) {
// 触发数组的大小变化
observer.setProp('length', mutation);
}
return ret;
}
export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] { export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] {
let listeners = [] as ((...args) => void)[]; let listeners = [] as ((...args) => void)[];
@ -118,37 +152,3 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) =
return new Proxy(rawObj, handle); return new Proxy(rawObj, handle);
} }
function set(rawObj: any[], key: string, value: any, receiver: any) {
const oldValue = rawObj[key];
const oldLength = rawObj.length;
const newValue = value;
const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
const ret = Reflect.set(rawObj, key, newValue, receiver);
const newLength = rawObj.length;
const observer = getObserver(rawObj);
const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj);
if (!isSame(newValue, oldValue)) {
// 值不一样,触发监听器
if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue, mutation);
});
}
// 触发属性变化
observer.setProp(key, mutation);
}
if (oldLength !== newLength) {
// 触发数组的大小变化
observer.setProp('length', mutation);
}
return ret;
}

View File

@ -25,48 +25,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: {
let oldData: [any, any][] = []; let oldData: [any, any][] = [];
let proxies = new Map(); let proxies = new Map();
function get(rawObj: { size: number }, key: any, receiver: any): any {
if (key === 'size') {
return size(rawObj);
}
if (key === 'get') {
return getFun.bind(null, rawObj);
}
if (Object.prototype.hasOwnProperty.call(handler, key)) {
const value = Reflect.get(handler, key, receiver);
return value.bind(null, rawObj);
}
if (key === 'watch') {
const observer = getObserver(rawObj);
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
if (!observer.watchers[prop]) {
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
}
observer.watchers[prop].push(handler);
return () => {
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
};
};
}
if (key === 'addListener') {
return listener => {
listeners.push(listener);
};
}
if (key === 'removeListener') {
return listener => {
listeners = listeners.filter(item => item != listener);
};
}
return Reflect.get(rawObj, key, receiver);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) { function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) {
const keyProxy = rawObj.has(key) ? key : proxies.get(key); const keyProxy = rawObj.has(key) ? key : proxies.get(key);
@ -211,31 +169,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: {
return false; return false;
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function size(rawObj: { size: number }) {
const observer = getObserver(rawObj);
observer.useProp(COLLECTION_CHANGE);
return rawObj.size;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.keys(), 'keys');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.values(), 'values');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.entries(), 'entries');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function forOf(rawObj: {
entries: () => { next: () => { value: any; done: boolean } };
values: () => { next: () => { value: any; done: boolean } };
}) {
return wrapIterator(rawObj, rawObj.entries(), 'entries');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function forEach( function forEach(
rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, rawObj: { forEach: (callback: (value: any, key: any) => void) => void },
callback: (valProxy: any, keyProxy: any, rawObj: any) => void callback: (valProxy: any, keyProxy: any, rawObj: any) => void
@ -354,6 +287,75 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: {
}, },
}; };
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function size(rawObj: { size: number }) {
const observer = getObserver(rawObj);
observer.useProp(COLLECTION_CHANGE);
return rawObj.size;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.keys(), 'keys');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.values(), 'values');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.entries(), 'entries');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function forOf(rawObj: {
entries: () => { next: () => { value: any; done: boolean } };
values: () => { next: () => { value: any; done: boolean } };
}) {
return wrapIterator(rawObj, rawObj.entries(), 'entries');
}
function get(rawObj: { size: number }, key: any, receiver: any): any {
if (key === 'size') {
return size(rawObj);
}
if (key === 'get') {
return getFun.bind(null, rawObj);
}
if (Object.prototype.hasOwnProperty.call(handler, key)) {
const value = Reflect.get(handler, key, receiver);
return value.bind(null, rawObj);
}
if (key === 'watch') {
const observer = getObserver(rawObj);
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
if (!observer.watchers[prop]) {
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
}
observer.watchers[prop].push(handler);
return () => {
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
};
};
}
if (key === 'addListener') {
return listener => {
listeners.push(listener);
};
}
if (key === 'removeListener') {
return listener => {
listeners = listeners.filter(item => item != listener);
};
}
return Reflect.get(rawObj, key, receiver);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const handler = { const handler = {
get, get,

View File

@ -18,6 +18,27 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
import { OBSERVER_KEY } from '../../Constants'; import { OBSERVER_KEY } from '../../Constants';
import { isPanelActive } from '../../devtools'; import { isPanelActive } from '../../devtools';
function set(rawObj: object, key: string, value: any, receiver: any): boolean {
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
const observer = getObserver(rawObj);
const oldValue = rawObj[key];
const newValue = value;
const ret = Reflect.set(rawObj, key, newValue, receiver);
const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj);
if (!isSame(newValue, oldValue)) {
if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue, mutation);
});
}
observer.setProp(key, mutation);
}
return ret;
}
export function createObjectProxy<T extends object>( export function createObjectProxy<T extends object>(
rawObj: T, rawObj: T,
singleLevel = false, singleLevel = false,
@ -99,24 +120,3 @@ export function createObjectProxy<T extends object>(
return proxy; return proxy;
} }
function set(rawObj: object, key: string, value: any, receiver: any): boolean {
const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null;
const observer = getObserver(rawObj);
const oldValue = rawObj[key];
const newValue = value;
const ret = Reflect.set(rawObj, key, newValue, receiver);
const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj);
if (!isSame(newValue, oldValue)) {
if (observer.watchers?.[key]) {
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue, mutation);
});
}
observer.setProp(key, mutation);
}
return ret;
}

View File

@ -26,43 +26,6 @@ export function createSetProxy<T extends object>(
let listeners: ((mutation) => {})[] = []; let listeners: ((mutation) => {})[] = [];
let proxies = new WeakMap(); let proxies = new WeakMap();
function get(rawObj: { size: number }, key: any, receiver: any): any {
if (Object.prototype.hasOwnProperty.call(handler, key)) {
const value = Reflect.get(handler, key, receiver);
return value.bind(null, rawObj);
}
if (key === 'size') {
return size(rawObj);
}
if (key === 'addListener') {
return listener => {
listeners.push(listener);
};
}
if (key === 'removeListener') {
return listener => {
listeners = listeners.filter(item => item != listener);
};
}
if (key === 'watch') {
const observer = getObserver(rawObj);
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
if (!observer.watchers[prop]) {
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
}
observer.watchers[prop].push(handler);
return () => {
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
};
};
}
return Reflect.get(rawObj, key, receiver);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Set的add方法 // Set的add方法
function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object { function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object {
if (!rawObj.has(proxies.get(value))) { if (!rawObj.has(proxies.get(value))) {
@ -166,18 +129,44 @@ export function createSetProxy<T extends object>(
return rawObj.size; return rawObj.size;
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { function get(rawObj: { size: number }, key: any, receiver: any): any {
return wrapIterator(rawObj, rawObj.keys()); if (Object.prototype.hasOwnProperty.call(handler, key)) {
const value = Reflect.get(handler, key, receiver);
return value.bind(null, rawObj);
} }
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { if (key === 'size') {
return wrapIterator(rawObj, rawObj.values()); return size(rawObj);
} }
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { if (key === 'addListener') {
return wrapIterator(rawObj, rawObj.entries()); return listener => {
listeners.push(listener);
};
} }
if (key === 'removeListener') {
return listener => {
listeners = listeners.filter(item => item != listener);
};
}
if (key === 'watch') {
const observer = getObserver(rawObj);
return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => {
if (!observer.watchers[prop]) {
observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[];
}
observer.watchers[prop].push(handler);
return () => {
observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler);
};
};
}
return Reflect.get(rawObj, key, receiver);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) { function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) {
const observer = getObserver(rawObj); const observer = getObserver(rawObj);
const hookObserver = hookObserverMap.get(rawObj); const hookObserver = hookObserverMap.get(rawObj);
@ -224,6 +213,18 @@ export function createSetProxy<T extends object>(
}; };
} }
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.keys());
}
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.values());
}
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
return wrapIterator(rawObj, rawObj.entries());
}
function forOf(rawObj: { function forOf(rawObj: {
entries: () => { next: () => { value: any; done: boolean } }; entries: () => { next: () => { value: any; done: boolean } };
values: () => { next: () => { value: any; done: boolean } }; values: () => { next: () => { value: any; done: boolean } };

View File

@ -53,6 +53,100 @@ const idGenerator = {
const storeMap = new Map<string, StoreObj<any, any, any>>(); const storeMap = new Map<string, StoreObj<any, any, any>>();
// 通过该方法执行store.$queue中的action
function tryNextAction(storeObj, proxyObj, config, plannedActions) {
if (!plannedActions.length) {
if (proxyObj.$pending) {
const timestamp = Date.now();
const duration = timestamp - proxyObj.$pending;
proxyObj.$pending = false;
devtools.emit(QUEUE_FINISHED, {
store: storeObj,
endedAt: timestamp,
duration,
});
}
return;
}
const nextAction = plannedActions.shift()!;
const result = config.actions
? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload)
: undefined;
if (isPromise(result)) {
result.then(value => {
nextAction.resolve(value);
tryNextAction(storeObj, proxyObj, config, plannedActions);
});
} else {
nextAction.resolve(result);
tryNextAction(storeObj, proxyObj, config, plannedActions);
}
}
// 删除Observers中保存的这个VNode的相关数据
export function clearVNodeObservers(vNode: VNode) {
if (!vNode.observers) {
return;
}
vNode.observers.forEach(observer => {
observer.clearByVNode(vNode);
});
vNode.observers.clear();
}
// 注册VNode销毁时的清理动作
function registerDestroyFunction() {
const processingVNode = getProcessingVNode();
// 获取不到当前运行的VNode说明不在组件中运行属于非法场景
if (!processingVNode) {
return;
}
if (!processingVNode.observers) {
processingVNode.observers = new Set<Observer>();
}
// 函数组件
if (processingVNode.tag === FunctionComponent) {
const vNodeRef = useRef(processingVNode);
useEffect(() => {
return () => {
clearVNodeObservers(vNodeRef.current);
vNodeRef.current.observers = null;
};
}, []);
} else if (processingVNode.tag === ClassComponent) {
// 类组件
if (!processingVNode.classComponentWillUnmount) {
processingVNode.classComponentWillUnmount = vNode => {
clearVNodeObservers(vNode);
vNode.observers = null;
};
}
}
}
// createStore返回的是一个getStore的函数这个函数必须要在组件函数/类组件里面被执行因为要注册VNode销毁时的清理动作
function createGetStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
storeObj: StoreObj<S, A, C>
): () => StoreObj<S, A, C> {
const getStore = () => {
if (!storeObj.$config.options?.isReduxAdapter) {
registerDestroyFunction();
}
return storeObj;
};
return getStore;
}
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> config: StoreConfig<S, A, C>
): () => StoreObj<S, A, C> { ): () => StoreObj<S, A, C> {
@ -216,100 +310,6 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
return createGetStore(storeObj); return createGetStore(storeObj);
} }
// 通过该方法执行store.$queue中的action
function tryNextAction(storeObj, proxyObj, config, plannedActions) {
if (!plannedActions.length) {
if (proxyObj.$pending) {
const timestamp = Date.now();
const duration = timestamp - proxyObj.$pending;
proxyObj.$pending = false;
devtools.emit(QUEUE_FINISHED, {
store: storeObj,
endedAt: timestamp,
duration,
});
}
return;
}
const nextAction = plannedActions.shift()!;
const result = config.actions
? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload)
: undefined;
if (isPromise(result)) {
result.then(value => {
nextAction.resolve(value);
tryNextAction(storeObj, proxyObj, config, plannedActions);
});
} else {
nextAction.resolve(result);
tryNextAction(storeObj, proxyObj, config, plannedActions);
}
}
// createStore返回的是一个getStore的函数这个函数必须要在组件函数/类组件里面被执行因为要注册VNode销毁时的清理动作
function createGetStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
storeObj: StoreObj<S, A, C>
): () => StoreObj<S, A, C> {
const getStore = () => {
if (!storeObj.$config.options?.isReduxAdapter) {
registerDestroyFunction();
}
return storeObj;
};
return getStore;
}
// 删除Observers中保存的这个VNode的相关数据
export function clearVNodeObservers(vNode: VNode) {
if (!vNode.observers) {
return;
}
vNode.observers.forEach(observer => {
observer.clearByVNode(vNode);
});
vNode.observers.clear();
}
// 注册VNode销毁时的清理动作
function registerDestroyFunction() {
const processingVNode = getProcessingVNode();
// 获取不到当前运行的VNode说明不在组件中运行属于非法场景
if (!processingVNode) {
return;
}
if (!processingVNode.observers) {
processingVNode.observers = new Set<Observer>();
}
// 函数组件
if (processingVNode.tag === FunctionComponent) {
const vNodeRef = useRef(processingVNode);
useEffect(() => {
return () => {
clearVNodeObservers(vNodeRef.current);
vNodeRef.current.observers = null;
};
}, []);
} else if (processingVNode.tag === ClassComponent) {
// 类组件
if (!processingVNode.classComponentWillUnmount) {
processingVNode.classComponentWillUnmount = vNode => {
clearVNodeObservers(vNode);
vNode.observers = null;
};
}
}
}
// 函数组件中使用的hook // 函数组件中使用的hook
export function useStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>( export function useStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
id: string id: string

View File

@ -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;
} }

View File

@ -228,6 +228,40 @@ export function calcStartUpdateVNode(treeRoot: VNode) {
return node; return node;
} }
// 在局部更新时从上到下恢复父节点的context和PortalStack
function recoverTreeContext(vNode: VNode) {
const contextProviders: VNode[] = [];
let parent = vNode.parent;
while (parent !== null) {
if (parent.tag === ContextProvider) {
contextProviders.unshift(parent);
}
if (parent.tag === DomPortal) {
pushCurrentRoot(parent);
}
parent = parent.parent;
}
contextProviders.forEach(node => {
setContext(node, node.props.value);
});
}
// 在局部更新时从下到上重置父节点的context
function resetTreeContext(vNode: VNode) {
let parent = vNode.parent;
while (parent !== null) {
if (parent.tag === ContextProvider) {
resetContext(parent);
}
if (parent.tag === DomPortal) {
popCurrentRoot();
}
parent = parent.parent;
}
}
// ============================== 深度遍历 ============================== // ============================== 深度遍历 ==============================
function buildVNodeTree(treeRoot: VNode) { function buildVNodeTree(treeRoot: VNode) {
const preMode = copyExecuteMode(); const preMode = copyExecuteMode();
@ -299,40 +333,6 @@ function buildVNodeTree(treeRoot: VNode) {
setExecuteMode(preMode); setExecuteMode(preMode);
} }
// 在局部更新时从上到下恢复父节点的context和PortalStack
function recoverTreeContext(vNode: VNode) {
const contextProviders: VNode[] = [];
let parent = vNode.parent;
while (parent !== null) {
if (parent.tag === ContextProvider) {
contextProviders.unshift(parent);
}
if (parent.tag === DomPortal) {
pushCurrentRoot(parent);
}
parent = parent.parent;
}
contextProviders.forEach(node => {
setContext(node, node.props.value);
});
}
// 在局部更新时从下到上重置父节点的context
function resetTreeContext(vNode: VNode) {
let parent = vNode.parent;
while (parent !== null) {
if (parent.tag === ContextProvider) {
resetContext(parent);
}
if (parent.tag === DomPortal) {
popCurrentRoot();
}
parent = parent.parent;
}
}
// 总体任务入口 // 总体任务入口
function renderFromRoot(treeRoot) { function renderFromRoot(treeRoot) {
runAsyncEffects(); runAsyncEffects();

View File

@ -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> = {

View File

@ -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) {
@ -231,6 +232,19 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
return null; return null;
} }
// 设置vNode中的cIndex属性cIndex是节点在children中的位置
function setVNodesCIndex(startChild: VNode | null, startIdx: number) {
let node: VNode | null = startChild;
let idx = startIdx;
while (node !== null) {
node.cIndex = idx;
markVNodePath(node);
node = node.next;
idx++;
}
}
// diff数组类型的节点核心算法 // diff数组类型的节点核心算法
function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array<any>): VNode | null { function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array<any>): VNode | null {
let resultingFirstChild: VNode | null = null; let resultingFirstChild: VNode | null = null;
@ -478,19 +492,6 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC
return resultingFirstChild; return resultingFirstChild;
} }
// 设置vNode中的cIndex属性cIndex是节点在children中的位置
function setVNodesCIndex(startChild: VNode | null, startIdx: number) {
let node: VNode | null = startChild;
let idx = startIdx;
while (node !== null) {
node.cIndex = idx;
markVNodePath(node);
node = node.next;
idx++;
}
}
// 新节点是迭代器类型 // 新节点是迭代器类型
function diffIteratorNodesHandler( function diffIteratorNodesHandler(
parentNode: VNode, parentNode: VNode,
@ -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) {

View File

@ -18,6 +18,12 @@ import type { VNode } from '../Types';
import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook'; import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook';
import { HookStage, setHookStage } from './HookStage'; import { HookStage, setHookStage } from './HookStage';
function resetGlobalVariable() {
setHookStage(null);
setLastTimeHook(null);
setCurrentHook(null);
}
// hook对外入口 // hook对外入口
export function runFunctionWithHooks<Props extends Record<string, any>, Arg>( export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
funcComp: (props: Props, arg: Arg) => any, funcComp: (props: Props, arg: Arg) => any,
@ -57,9 +63,3 @@ export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
return comp; return comp;
} }
function resetGlobalVariable() {
setHookStage(null);
setLastTimeHook(null);
setCurrentHook(null);
}

View File

@ -21,27 +21,17 @@ 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 { function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect {
// 异步触发的effect const effect: Effect = {
useEffect(effectFunc, deps, EffectConstant.Effect); effect: effectFunc,
} removeEffect: removeFunc,
dependencies: deps,
effectConstant: effectConstant,
};
export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void { getProcessingVNode().effectList.push(effect);
// 同步触发的effect
useEffect(effectFunc, deps, EffectConstant.LayoutEffect);
}
function useEffect(effectFunc: () => (() => void) | void, deps: Array<any> | void | null, effectType: number): void { return effect;
const stage = getHookStage();
if (stage === null) {
throwNotInFuncError();
}
if (stage === HookStage.Init) {
useEffectForInit(effectFunc, deps, effectType);
} else if (stage === HookStage.Update) {
useEffectForUpdate(effectFunc, deps, effectType);
}
} }
export function useEffectForInit(effectFunc, deps, effectType): void { export function useEffectForInit(effectFunc, deps, effectType): void {
@ -76,15 +66,25 @@ export function useEffectForUpdate(effectFunc, deps, effectType): void {
hook.state = createEffect(effectFunc, removeFunc, nextDeps, EffectConstant.DepsChange | effectType); hook.state = createEffect(effectFunc, removeFunc, nextDeps, EffectConstant.DepsChange | effectType);
} }
function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect { function useEffect(effectFunc: () => (() => void) | void, deps: Array<any> | void | null, effectType: number): void {
const effect: Effect = { const stage = getHookStage();
effect: effectFunc, if (stage === null) {
removeEffect: removeFunc, throwNotInFuncError();
dependencies: deps, }
effectConstant: effectConstant,
};
getProcessingVNode().effectList.push(effect); if (stage === HookStage.Init) {
useEffectForInit(effectFunc, deps, effectType);
return effect; } else if (stage === HookStage.Update) {
useEffectForUpdate(effectFunc, deps, effectType);
}
}
export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void {
// 异步触发的effect
useEffect(effectFunc, deps, EffectConstant.Effect);
}
export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void {
// 同步触发的effect
useEffect(effectFunc, deps, EffectConstant.LayoutEffect);
} }

View File

@ -18,20 +18,6 @@ import { getHookStage } from './HookStage';
import { throwNotInFuncError } from './BaseHook'; import { throwNotInFuncError } from './BaseHook';
import type { Ref } from './HookType'; import type { Ref } from './HookType';
export function useImperativeHandleImpl<R>(
ref: { current: R | null } | ((any) => any) | null | void,
func: () => R,
dependencies?: Array<any> | null
): void {
const stage = getHookStage();
if (stage === null) {
throwNotInFuncError();
}
const params = isNotNull(dependencies) ? dependencies.concat([ref]) : null;
useLayoutEffectImpl(effectFunc.bind(null, func, ref), params);
}
function isNotNull(object: any): boolean { function isNotNull(object: any): boolean {
return object !== null && object !== undefined; return object !== null && object !== undefined;
} }
@ -52,3 +38,17 @@ function effectFunc<R>(func: () => R, ref: Ref<R> | ((any) => any) | null): (()
}; };
} }
} }
export function useImperativeHandleImpl<R>(
ref: { current: R | null } | ((any) => any) | null | void,
func: () => R,
dependencies?: Array<any> | null
): void {
const stage = getHookStage();
if (stage === null) {
throwNotInFuncError();
}
const params = isNotNull(dependencies) ? dependencies.concat([ref]) : null;
useLayoutEffectImpl(effectFunc.bind(null, func, ref), params);
}

View File

@ -22,29 +22,6 @@ import { getHookStage, HookStage } from './HookStage';
import type { VNode } from '../Types'; import type { VNode } from '../Types';
import { getProcessingVNode } from '../GlobalVar'; import { getProcessingVNode } from '../GlobalVar';
export function useReducerImpl<S, P, A>(
reducer: (S, A) => S,
initArg: P,
init?: (P) => S,
isUseState?: boolean
): [S, Trigger<A>] | void {
const stage = getHookStage();
if (stage === null) {
throwNotInFuncError();
}
if (stage === HookStage.Init) {
return useReducerForInit(reducer, initArg, init, isUseState);
} else if (stage === HookStage.Update) {
// 获取当前的hook
const currentHook = getCurrentHook();
// 获取currentHook的更新数组
const currentHookUpdates = (currentHook.state as Reducer<S, A>).updates;
return updateReducerHookState(currentHookUpdates, currentHook, reducer);
}
}
// 构造新的Update数组 // 构造新的Update数组
function insertUpdate<S, A>(action: A, hook: Hook<S, A>): Update<S, A> { function insertUpdate<S, A>(action: A, hook: Hook<S, A>): Update<S, A> {
const newUpdate: Update<S, A> = { const newUpdate: Update<S, A> = {
@ -116,6 +93,25 @@ export function useReducerForInit<S, A>(reducer, initArg, init, isUseState?: boo
return [hook.state.stateValue, trigger]; return [hook.state.stateValue, trigger];
} }
// 计算stateValue值
function calculateNewState<S, A>(currentHookUpdates: Array<Update<S, A>>, currentHook, reducer: (S, A) => S) {
const reducerObj = currentHook.state;
let state = reducerObj.stateValue;
// 循环遍历更新数组,计算新的状态值
currentHookUpdates.forEach(update => {
// 1. didCalculated = true 说明state已经计算过; 2. 如果来自 isUseState
if (update.didCalculated && reducerObj.isUseState) {
state = update.state;
} else {
const action = update.action;
state = reducer(state, action);
}
});
return state;
}
// 更新hook.state // 更新hook.state
function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer): [S, Trigger<A>] { function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer): [S, Trigger<A>] {
if (currentHookUpdates !== null) { if (currentHookUpdates !== null) {
@ -135,21 +131,25 @@ function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer):
return [currentHook.state.stateValue, currentHook.state.trigger]; return [currentHook.state.stateValue, currentHook.state.trigger];
} }
// 计算stateValue值 export function useReducerImpl<S, P, A>(
function calculateNewState<S, A>(currentHookUpdates: Array<Update<S, A>>, currentHook, reducer: (S, A) => S) { reducer: (S, A) => S,
const reducerObj = currentHook.state; initArg: P,
let state = reducerObj.stateValue; init?: (P) => S,
isUseState?: boolean
// 循环遍历更新数组,计算新的状态值 ): [S, Trigger<A>] | void {
currentHookUpdates.forEach(update => { const stage = getHookStage();
// 1. didCalculated = true 说明state已经计算过; 2. 如果来自 isUseState if (stage === null) {
if (update.didCalculated && reducerObj.isUseState) { throwNotInFuncError();
state = update.state;
} else {
const action = update.action;
state = reducer(state, action);
} }
});
return state; if (stage === HookStage.Init) {
return useReducerForInit(reducer, initArg, init, isUseState);
} else if (stage === HookStage.Update) {
// 获取当前的hook
const currentHook = getCurrentHook();
// 获取currentHook的更新数组
const currentHookUpdates = (currentHook.state as Reducer<S, A>).updates;
return updateReducerHookState(currentHookUpdates, currentHook, reducer);
}
} }

View File

@ -40,28 +40,6 @@ export function isSchedulingEffects() {
return isScheduling; return isScheduling;
} }
export function callUseEffects(vNode: VNode) {
const effectList: EffectList = vNode.effectList;
if (effectList !== null) {
effectList.forEach(effect => {
const { effectConstant } = effect;
if (
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
) {
hookEffects.push(effect);
hookRemoveEffects.push(effect);
// 异步调用
if (!isScheduling) {
isScheduling = true;
runAsync(runAsyncEffects);
}
}
});
}
}
export function runAsyncEffects() { export function runAsyncEffects() {
const preMode = copyExecuteMode(); const preMode = copyExecuteMode();
changeMode(InRender, true); changeMode(InRender, true);
@ -98,6 +76,28 @@ export function runAsyncEffects() {
setExecuteMode(preMode); setExecuteMode(preMode);
} }
export function callUseEffects(vNode: VNode) {
const effectList: EffectList = vNode.effectList;
if (effectList !== null) {
effectList.forEach(effect => {
const { effectConstant } = effect;
if (
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
) {
hookEffects.push(effect);
hookRemoveEffects.push(effect);
// 异步调用
if (!isScheduling) {
isScheduling = true;
runAsync(runAsyncEffects);
}
}
});
}
}
// 在销毁vNode的时候调用remove // 在销毁vNode的时候调用remove
export function callEffectRemove(vNode: VNode) { export function callEffectRemove(vNode: VNode) {
const effectList: EffectList = vNode.effectList; const effectList: EffectList = vNode.effectList;

View File

@ -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 {
@ -154,6 +155,22 @@ function hideOrUnhideAllChildren(vNode, isHidden) {
); );
} }
function handleRef(vNode: VNode, ref, val) {
if (ref !== null && ref !== undefined) {
const refType = typeof ref;
if (refType === 'function') {
ref(val);
} else if (refType === 'object') {
(<RefType>ref).current = val;
} else {
if (vNode[BELONG_CLASS_VNODE_KEY] && vNode[BELONG_CLASS_VNODE_KEY].realNode) {
vNode[BELONG_CLASS_VNODE_KEY].realNode.refs[String(ref)] = val;
}
}
}
}
function attachRef(vNode: VNode) { function attachRef(vNode: VNode) {
const ref = vNode.ref; const ref = vNode.ref;
@ -166,60 +183,6 @@ function detachRef(vNode: VNode, isOldRef?: boolean) {
handleRef(vNode, ref, null); handleRef(vNode, ref, null);
} }
function handleRef(vNode: VNode, ref, val) {
if (ref !== null && ref !== undefined) {
const refType = typeof ref;
if (refType === 'function') {
ref(val);
} else if (refType === 'object') {
(<RefType>ref).current = val;
} else {
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
vNode.belongClassVNode.realNode.refs[String(ref)] = val;
}
}
}
}
// 卸载一个vNode不会递归
function unmountVNode(vNode: VNode): void {
switch (vNode.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent: {
callEffectRemove(vNode);
break;
}
case ClassComponent: {
detachRef(vNode);
const instance = vNode.realNode;
// 当constructor中抛出异常时instance会是null这里判断一下instance是否为空
// suspense打断时不需要触发WillUnmount
if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) {
callComponentWillUnmount(vNode, instance);
}
// HorizonX会在classComponentWillUnmount中清除对VNode的引入用
if (vNode.classComponentWillUnmount) {
vNode.classComponentWillUnmount(vNode);
vNode.classComponentWillUnmount = null;
}
break;
}
case DomComponent: {
detachRef(vNode);
break;
}
case DomPortal: {
// 这里会递归
unmountDomComponents(vNode);
break;
}
}
}
// 卸载vNode递归遍历子vNode // 卸载vNode递归遍历子vNode
function unmountNestedVNodes(vNode: VNode): void { function unmountNestedVNodes(vNode: VNode): void {
travelVNodeTree( travelVNodeTree(
@ -235,59 +198,6 @@ function unmountNestedVNodes(vNode: VNode): void {
); );
} }
function submitAddition(vNode: VNode): void {
let parent = vNode.parent;
let parentDom;
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
parentDom = parent.realNode;
break;
}
parent = parent.parent;
}
if ((parent.flags & ResetText) === ResetText) {
// 在insert之前先reset
clearText(parentDom);
FlagUtils.removeFlag(parent, ResetText);
}
if ((vNode.flags & DirectAddition) === DirectAddition) {
insertOrAppendPlacementNode(vNode, null, parentDom);
FlagUtils.removeFlag(vNode, DirectAddition);
return;
}
const before = getSiblingDom(vNode);
insertOrAppendPlacementNode(vNode, before, parentDom);
}
function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void {
const { tag, realNode } = node;
if (isDomVNode(node)) {
insertDom(parent, realNode, beforeDom);
} else if (tag === DomPortal) {
// 这里不做处理直接在portal中处理
} else {
// 插入子节点们
let child = node.child;
while (child !== null) {
insertOrAppendPlacementNode(child, beforeDom, parent);
child = child.next;
}
}
}
function insertDom(parent, realNode, beforeDom) {
if (beforeDom) {
insertDomBefore(parent, realNode, beforeDom);
} else {
appendChildElement(parent, realNode);
}
}
// 遍历所有子节点删除dom节点detach ref 和 调用componentWillUnmount() // 遍历所有子节点删除dom节点detach ref 和 调用componentWillUnmount()
function unmountDomComponents(vNode: VNode): void { function unmountDomComponents(vNode: VNode): void {
let currentParentIsValid = false; let currentParentIsValid = false;
@ -339,6 +249,100 @@ function unmountDomComponents(vNode: VNode): void {
); );
} }
// 卸载一个vNode不会递归
function unmountVNode(vNode: VNode): void {
switch (vNode.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent: {
callEffectRemove(vNode);
break;
}
case ClassComponent: {
detachRef(vNode);
const instance = vNode.realNode;
// 当constructor中抛出异常时instance会是null这里判断一下instance是否为空
// suspense打断时不需要触发WillUnmount
if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) {
callComponentWillUnmount(vNode, instance);
}
// HorizonX会在classComponentWillUnmount中清除对VNode的引入用
if (vNode.classComponentWillUnmount) {
vNode.classComponentWillUnmount(vNode);
vNode.classComponentWillUnmount = null;
}
break;
}
case DomComponent: {
detachRef(vNode);
break;
}
case DomPortal: {
// 这里会递归
unmountDomComponents(vNode);
break;
}
default: {
break;
}
}
}
function insertDom(parent, realNode, beforeDom) {
if (beforeDom) {
insertDomBefore(parent, realNode, beforeDom);
} else {
appendChildElement(parent, realNode);
}
}
function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void {
const { tag, realNode } = node;
if (isDomVNode(node)) {
insertDom(parent, realNode, beforeDom);
} else if (tag === DomPortal) {
// 这里不做处理直接在portal中处理
} else {
// 插入子节点们
let child = node.child;
while (child !== null) {
insertOrAppendPlacementNode(child, beforeDom, parent);
child = child.next;
}
}
}
function submitAddition(vNode: VNode): void {
let parent = vNode.parent;
let parentDom;
let tag;
while (parent !== null) {
tag = parent.tag;
if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) {
parentDom = parent.realNode;
break;
}
parent = parent.parent;
}
if ((parent.flags & ResetText) === ResetText) {
// 在insert之前先reset
clearText(parentDom);
FlagUtils.removeFlag(parent, ResetText);
}
if ((vNode.flags & DirectAddition) === DirectAddition) {
insertOrAppendPlacementNode(vNode, null, parentDom);
FlagUtils.removeFlag(vNode, DirectAddition);
return;
}
const before = getSiblingDom(vNode);
insertOrAppendPlacementNode(vNode, before, parentDom);
}
function submitClear(vNode: VNode): void { function submitClear(vNode: VNode): void {
const realNode = vNode.realNode; const realNode = vNode.realNode;
const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制 const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制
@ -394,6 +398,13 @@ function submitDeletion(vNode: VNode): void {
clearVNode(vNode); clearVNode(vNode);
} }
function submitSuspenseComponent(vNode: VNode) {
const { childStatus } = vNode.suspenseState;
if (childStatus !== SuspenseChildStatus.Init) {
hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback);
}
}
function submitUpdate(vNode: VNode): void { function submitUpdate(vNode: VNode): void {
switch (vNode.tag) { switch (vNode.tag) {
case FunctionComponent: case FunctionComponent:
@ -413,13 +424,9 @@ function submitUpdate(vNode: VNode): void {
listenToPromise(vNode); listenToPromise(vNode);
break; break;
} }
default: {
break;
} }
}
function submitSuspenseComponent(vNode: VNode) {
const { childStatus } = vNode.suspenseState;
if (childStatus !== SuspenseChildStatus.Init) {
hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback);
} }
} }

View File

@ -41,63 +41,6 @@ const LOOPING_UPDATE_LIMIT = 50;
let loopingUpdateCount = 0; let loopingUpdateCount = 0;
let lastRoot: VNode | null = null; let lastRoot: VNode | null = null;
export function submitToRender(treeRoot) {
treeRoot.shouldUpdate = treeRoot.childShouldUpdate;
// 置空task让才能加入新的render任务
treeRoot.task = null;
const startVNode = getStartVNode();
if (FlagUtils.hasAnyFlag(startVNode)) {
// 把自己加上
if (startVNode.dirtyNodes === null) {
startVNode.dirtyNodes = [startVNode];
} else {
startVNode.dirtyNodes.push(startVNode);
}
}
const dirtyNodes = startVNode.dirtyNodes;
if (dirtyNodes !== null && dirtyNodes.length) {
const preMode = copyExecuteMode();
changeMode(InRender, true);
prepareForSubmit();
// before submit阶段
beforeSubmit(dirtyNodes);
// submit阶段
submit(dirtyNodes);
resetAfterSubmit();
// after submit阶段
afterSubmit(dirtyNodes);
setExecuteMode(preMode);
dirtyNodes.length = 0;
startVNode.dirtyNodes = null;
}
if (isSchedulingEffects()) {
setSchedulingEffects(false);
}
// 统计root同步重渲染的次数如果太多可能是无线循环
countLoopingUpdate(treeRoot);
// 在退出`submit` 之前始终调用此函数以确保任何已计划在此根上执行的update被执行。
tryRenderFromRoot(treeRoot);
if (rootThrowError) {
const error = rootThrowError;
rootThrowError = null;
throw error;
}
return null;
}
function beforeSubmit(dirtyNodes: Array<VNode>) { function beforeSubmit(dirtyNodes: Array<VNode>) {
let node; let node;
const nodesLength = dirtyNodes.length; const nodesLength = dirtyNodes.length;
@ -214,3 +157,60 @@ export function checkLoopingUpdateLimit() {
); );
} }
} }
export function submitToRender(treeRoot) {
treeRoot.shouldUpdate = treeRoot.childShouldUpdate;
// 置空task让才能加入新的render任务
treeRoot.task = null;
const startVNode = getStartVNode();
if (FlagUtils.hasAnyFlag(startVNode)) {
// 把自己加上
if (startVNode.dirtyNodes === null) {
startVNode.dirtyNodes = [startVNode];
} else {
startVNode.dirtyNodes.push(startVNode);
}
}
const dirtyNodes = startVNode.dirtyNodes;
if (dirtyNodes !== null && dirtyNodes.length) {
const preMode = copyExecuteMode();
changeMode(InRender, true);
prepareForSubmit();
// before submit阶段
beforeSubmit(dirtyNodes);
// submit阶段
submit(dirtyNodes);
resetAfterSubmit();
// after submit阶段
afterSubmit(dirtyNodes);
setExecuteMode(preMode);
dirtyNodes.length = 0;
startVNode.dirtyNodes = null;
}
if (isSchedulingEffects()) {
setSchedulingEffects(false);
}
// 统计root同步重渲染的次数如果太多可能是无线循环
countLoopingUpdate(treeRoot);
// 在退出`submit` 之前始终调用此函数以确保任何已计划在此根上执行的update被执行。
tryRenderFromRoot(treeRoot);
if (rootThrowError) {
const error = rootThrowError;
rootThrowError = null;
throw error;
}
return null;
}

View File

@ -27,6 +27,14 @@ export function isOverTime() {
return false; return false;
} }
function asyncCall() {
if (isTestRuntime) {
setTimeout(callRenderTasks, 0);
} else {
port2.postMessage(null);
}
}
// 1、设置deadline2、回调TaskExecutor传过来的browserCallback // 1、设置deadline2、回调TaskExecutor传过来的browserCallback
const callRenderTasks = () => { const callRenderTasks = () => {
if (browserCallback === null) { if (browserCallback === null) {
@ -61,14 +69,6 @@ if (typeof MessageChannel === 'function') {
isTestRuntime = true; isTestRuntime = true;
} }
function asyncCall() {
if (isTestRuntime) {
setTimeout(callRenderTasks, 0);
} else {
port2.postMessage(null);
}
}
export function requestBrowserCallback(callback) { export function requestBrowserCallback(callback) {
browserCallback = callback; browserCallback = callback;

View File

@ -27,16 +27,6 @@ let callingQueueTask: any | null = null;
// 防止重入 // 防止重入
let isCallingRenderQueue = false; let isCallingRenderQueue = false;
export function callRenderQueueImmediate() {
if (callingQueueTask !== null) {
// 取消异步调度
cancelTask(callingQueueTask);
callingQueueTask = null;
}
callRenderQueue();
}
// 执行render回调 // 执行render回调
function callRenderQueue() { function callRenderQueue() {
if (!isCallingRenderQueue && renderQueue !== null) { if (!isCallingRenderQueue && renderQueue !== null) {
@ -58,6 +48,16 @@ function callRenderQueue() {
} }
} }
export function callRenderQueueImmediate() {
if (callingQueueTask !== null) {
// 取消异步调度
cancelTask(callingQueueTask);
callingQueueTask = null;
}
callRenderQueue();
}
export function pushRenderCallback(callback: RenderCallback) { export function pushRenderCallback(callback: RenderCallback) {
if (renderQueue === null) { if (renderQueue === null) {
renderQueue = [callback]; renderQueue = [callback];

View File

@ -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;
@ -200,6 +202,8 @@ export class VNode {
break; break;
case Profiler: case Profiler:
break; break;
default:
break;
} }
} }
} }

View File

@ -194,6 +194,8 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) {
vNode.updates = []; vNode.updates = [];
break; break;
default:
break;
} }
return vNode; return vNode;

View File

@ -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);
@ -169,13 +169,6 @@ export function findDOMByClassInst(inst) {
return domVNode !== null ? domVNode.realNode : null; return domVNode !== null ? domVNode.realNode : null;
} }
// 判断dom树是否已经挂载
export function isMounted(vNode: VNode) {
const rootNode = getTreeRootVNode(vNode);
// 如果根节点是 Dom 类型节点,表示已经挂载
return rootNode.tag === TreeRoot;
}
function getTreeRootVNode(vNode) { function getTreeRootVNode(vNode) {
let node = vNode; let node = vNode;
while (node.parent) { while (node.parent) {
@ -184,6 +177,13 @@ function getTreeRootVNode(vNode) {
return node; return node;
} }
// 判断dom树是否已经挂载
export function isMounted(vNode: VNode) {
const rootNode = getTreeRootVNode(vNode);
// 如果根节点是 Dom 类型节点,表示已经挂载
return rootNode.tag === TreeRoot;
}
// 找到相邻的DOM // 找到相邻的DOM
export function getSiblingDom(vNode: VNode): Element | null { export function getSiblingDom(vNode: VNode): Element | null {
let node: VNode = vNode; let node: VNode = vNode;

View File

@ -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);
});
});