Match-id-607e5214b8210d9df6fe9c8e89c254bf68f8f9ce
This commit is contained in:
commit
0075694565
|
@ -40,3 +40,8 @@ steps:
|
|||
auto: true
|
||||
buildcheck: true
|
||||
mode: sync
|
||||
POST_BUILD:
|
||||
- compile_report:
|
||||
rules:
|
||||
- warning /.**/
|
||||
- error /.**/
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 0.0.45 (2023-05-10)
|
||||
- **core**: 修改belongClassVNode属性为Symbol提升性能
|
||||
- **core**: 优化内部循环实现,提升性能
|
||||
|
||||
## 0.0.44 (2023-04-03)
|
||||
- **core**: 修复horizonX-devtool Firefox75报错
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"keywords": [
|
||||
"horizon"
|
||||
],
|
||||
"version": "0.0.44",
|
||||
"version": "0.0.45",
|
||||
"homepage": "",
|
||||
"bugs": "",
|
||||
"main": "index.js",
|
||||
|
|
|
@ -48,7 +48,7 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i
|
|||
}
|
||||
} else if (propName === 'dangerouslySetInnerHTML') {
|
||||
dom.innerHTML = propVal.__html;
|
||||
} else if (!isInit || (isInit && propVal != null)) {
|
||||
} else if (!isInit || propVal != null) {
|
||||
updateCommonProp(dom, propName, propVal, isNativeTag);
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export function compareProps(oldProps: Object, newProps: Object): Object {
|
|||
for (let i = 0; i < oldPropsLength; i++) {
|
||||
propName = keysOfOldProps[i];
|
||||
// 新属性中包含该属性或者该属性为空值的属性不需要处理
|
||||
if (keysOfNewProps.includes(propName) || oldProps[propName] == null) {
|
||||
if ( oldProps[propName] == null || keysOfNewProps.includes(propName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,26 @@
|
|||
* 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) {
|
||||
return !(
|
||||
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',
|
||||
];
|
||||
|
|
|
@ -56,6 +56,14 @@ function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, i
|
|||
return listener;
|
||||
}
|
||||
|
||||
// 是否捕获事件
|
||||
function isCaptureEvent(horizonEventName) {
|
||||
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
|
||||
return false;
|
||||
}
|
||||
return horizonEventName.slice(-7) === 'Capture';
|
||||
}
|
||||
|
||||
// 事件懒委托,当用户定义事件后,再进行委托到根节点
|
||||
export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
|
||||
currentRoot.delegatedEvents.add(eventName);
|
||||
|
@ -74,8 +82,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
|
|||
}
|
||||
|
||||
if (!events[nativeFullName]) {
|
||||
const listener = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture);
|
||||
events[nativeFullName] = listener;
|
||||
events[nativeFullName] = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -94,14 +101,6 @@ function getNativeEvtName(horizonEventName, capture) {
|
|||
return nativeName.toLowerCase();
|
||||
}
|
||||
|
||||
// 是否捕获事件
|
||||
function isCaptureEvent(horizonEventName) {
|
||||
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
|
||||
return false;
|
||||
}
|
||||
return horizonEventName.slice(-7) === 'Capture';
|
||||
}
|
||||
|
||||
// 封装监听函数
|
||||
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
|
||||
return event => {
|
||||
|
|
|
@ -37,35 +37,6 @@ export function shouldControlValue(): boolean {
|
|||
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) {
|
||||
const { name, type } = props;
|
||||
|
||||
|
@ -87,3 +58,32 @@ function controlInputValue(inputDom: HTMLInputElement, props: 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;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { throwIfTrue } from '../renderer/utils/throwIfTrue';
|
|||
import { TYPE_COMMON_ELEMENT, TYPE_PORTAL } from './JSXElementType';
|
||||
|
||||
import { isValidElement, JSXElement } from './JSXElement';
|
||||
import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode';
|
||||
|
||||
// 生成key
|
||||
function getItemKey(item: any, index: number): string {
|
||||
|
@ -83,7 +84,7 @@ function callMapFun(children: any, arr: Array<any>, prefix: string, callback: Fu
|
|||
mappedChild.type,
|
||||
newKey,
|
||||
mappedChild.ref,
|
||||
mappedChild.belongClassVNode,
|
||||
mappedChild[BELONG_CLASS_VNODE_KEY],
|
||||
mappedChild.props,
|
||||
mappedChild.src
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import { TYPE_COMMON_ELEMENT } from './JSXElementType';
|
||||
import { getProcessingClassVNode } from '../renderer/GlobalVar';
|
||||
import { Source } from '../renderer/Types';
|
||||
import { BELONG_CLASS_VNODE_KEY } from '../renderer/vnode/VNode';
|
||||
|
||||
/**
|
||||
* vtype 节点的类型,这里固定是element
|
||||
|
@ -36,17 +37,9 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null)
|
|||
ref: ref,
|
||||
props: props,
|
||||
|
||||
// 所属的class组件
|
||||
belongClassVNode: null,
|
||||
// 所属的class组件,clonedeep jsxElement时需要防止无限循环
|
||||
[BELONG_CLASS_VNODE_KEY]: vNode,
|
||||
};
|
||||
|
||||
// 在 cloneDeep JSXElement 的时候会出现死循环,需要设置belongClassVNode的enumerable为false
|
||||
Object.defineProperty(ele, 'belongClassVNode', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: vNode,
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
// 为了test判断两个 JSXElement 对象是否相等时忽略src属性,需要设置src的enumerable为false
|
||||
Object.defineProperty(ele, 'src', {
|
||||
|
@ -60,11 +53,6 @@ export function JSXElement(type, key, ref, vNode, props, source: Source | null)
|
|||
return ele;
|
||||
}
|
||||
|
||||
function isValidKey(key) {
|
||||
const keyArray = ['key', 'ref', '__source', '__self'];
|
||||
return !keyArray.includes(key);
|
||||
}
|
||||
|
||||
function mergeDefault(sourceObj, defaultObj) {
|
||||
Object.keys(defaultObj).forEach(key => {
|
||||
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) {
|
||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
||||
const key = setting && setting.key !== undefined ? String(setting.key) : isClone ? type.key : null;
|
||||
const ref = setting && setting.ref !== undefined ? setting.ref : isClone ? type.ref : null;
|
||||
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
||||
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
||||
const props = isClone ? { ...type.props } : {};
|
||||
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
|
||||
let vNode = isClone ? type[BELONG_CLASS_VNODE_KEY] : getProcessingClassVNode();
|
||||
|
||||
if (setting !== null && setting !== undefined) {
|
||||
const keys = Object.keys(setting);
|
||||
const keyLength = keys.length;
|
||||
for (let i = 0; i < keyLength; i++) {
|
||||
const k = keys[i];
|
||||
if (isValidKey(k)) {
|
||||
|
||||
for (const k in setting) {
|
||||
if (!keyArray.includes(k)) {
|
||||
props[k] = setting[k];
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +98,6 @@ function buildElement(isClone, type, setting, children) {
|
|||
lineNumber: setting.__source.lineNumber,
|
||||
};
|
||||
}
|
||||
|
||||
return JSXElement(element, key, ref, vNode, props, src);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,20 @@ interface Thenable {
|
|||
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后面的代码是在刷新完成后才执行。
|
||||
function act(fun: () => void | Thenable): Thenable {
|
||||
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 {
|
||||
act
|
||||
};
|
||||
|
|
|
@ -54,15 +54,12 @@ export type ReduxMiddleware = (
|
|||
type Reducer = (state: any, action: ReduxAction) => any;
|
||||
|
||||
function mergeData(state, data) {
|
||||
console.log('merging data', { state, data });
|
||||
if (!data) {
|
||||
console.log('!data');
|
||||
state.stateWrapper = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) {
|
||||
console.log('data is array');
|
||||
state.stateWrapper.length = data.length;
|
||||
data.forEach((item, idx) => {
|
||||
if (item != state.stateWrapper[idx]) {
|
||||
|
@ -73,9 +70,10 @@ function mergeData(state, data) {
|
|||
}
|
||||
|
||||
if (typeof data === 'object' && typeof state?.stateWrapper === 'object') {
|
||||
console.log('data is object');
|
||||
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]) => {
|
||||
|
@ -86,7 +84,6 @@ function mergeData(state, data) {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('data is primitive or type mismatch');
|
||||
state.stateWrapper = data;
|
||||
}
|
||||
|
||||
|
@ -106,7 +103,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
|||
if (result === undefined) {
|
||||
return;
|
||||
} // NOTE: reducer should never return undefined, in this case, do not change state
|
||||
// mergeData(state,result);
|
||||
state.stateWrapper = result;
|
||||
},
|
||||
},
|
||||
|
@ -115,10 +111,6 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
|||
},
|
||||
})();
|
||||
|
||||
// store.$subscribe(()=>{
|
||||
// console.log('changed');
|
||||
// });
|
||||
|
||||
const result = {
|
||||
reducer,
|
||||
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 {
|
||||
middlewares = middlewares.slice();
|
||||
middlewares.reverse();
|
||||
|
@ -173,6 +159,12 @@ function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware
|
|||
store.dispatch = dispatch;
|
||||
}
|
||||
|
||||
export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void {
|
||||
return store => {
|
||||
return applyMiddlewares(store, middlewares);
|
||||
};
|
||||
}
|
||||
|
||||
type ActionCreator = (...params: any[]) => ReduxAction;
|
||||
type ActionCreators = { [key: string]: ActionCreator };
|
||||
export type BoundActionCreator = (...params: any[]) => void;
|
||||
|
|
|
@ -10,21 +10,6 @@ export function isPanelActive() {
|
|||
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
|
||||
function getType(value) {
|
||||
if (!value) return 'nullish';
|
||||
|
@ -39,6 +24,7 @@ function getType(value) {
|
|||
if (typeof value === 'object') return 'object';
|
||||
return 'primitive';
|
||||
}
|
||||
|
||||
function makeProxySnapshot(obj, visited: any[] = []) {
|
||||
const type = getType(obj);
|
||||
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 = {
|
||||
// returns vNode id from horizon devtools
|
||||
getVNodeId: vNode => {
|
||||
|
|
|
@ -27,6 +27,10 @@ const proxyMap = 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 {
|
||||
// 不是对象(是原始数据类型)不用代理
|
||||
if (!(rawObj && isObject(rawObj))) {
|
||||
|
@ -89,7 +93,3 @@ export function createProxy(rawObj: any, isHookObserver = true, listener: { curr
|
|||
|
||||
return proxyObj;
|
||||
}
|
||||
|
||||
export function getObserver(rawObj: any): Observer {
|
||||
return rawObj[OBSERVER_KEY];
|
||||
}
|
||||
|
|
|
@ -19,6 +19,40 @@ import { resolveMutation } from '../../CommonUtils';
|
|||
import { isPanelActive } from '../../devtools';
|
||||
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[] {
|
||||
let listeners = [] as ((...args) => void)[];
|
||||
|
||||
|
@ -118,37 +152,3 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) =
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -25,48 +25,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: {
|
|||
let oldData: [any, any][] = [];
|
||||
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) {
|
||||
const keyProxy = rawObj.has(key) ? key : proxies.get(key);
|
||||
|
@ -211,31 +169,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: {
|
|||
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(
|
||||
rawObj: { forEach: (callback: (value: any, key: any) => void) => 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 = {
|
||||
get,
|
||||
|
|
|
@ -18,6 +18,27 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
|||
import { OBSERVER_KEY } from '../../Constants';
|
||||
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>(
|
||||
rawObj: T,
|
||||
singleLevel = false,
|
||||
|
@ -99,24 +120,3 @@ export function createObjectProxy<T extends object>(
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -26,43 +26,6 @@ export function createSetProxy<T extends object>(
|
|||
let listeners: ((mutation) => {})[] = [];
|
||||
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方法
|
||||
function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object {
|
||||
if (!rawObj.has(proxies.get(value))) {
|
||||
|
@ -166,18 +129,44 @@ export function createSetProxy<T extends object>(
|
|||
return rawObj.size;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) {
|
||||
return wrapIterator(rawObj, rawObj.keys());
|
||||
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);
|
||||
}
|
||||
|
||||
function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) {
|
||||
return wrapIterator(rawObj, rawObj.values());
|
||||
if (key === 'size') {
|
||||
return size(rawObj);
|
||||
}
|
||||
|
||||
function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) {
|
||||
return wrapIterator(rawObj, rawObj.entries());
|
||||
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);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) {
|
||||
const observer = getObserver(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: {
|
||||
entries: () => { next: () => { value: any; done: boolean } };
|
||||
values: () => { next: () => { value: any; done: boolean } };
|
||||
|
|
|
@ -53,6 +53,100 @@ const idGenerator = {
|
|||
|
||||
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>>(
|
||||
config: StoreConfig<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);
|
||||
}
|
||||
|
||||
// 通过该方法执行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
|
||||
export function useStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>(
|
||||
id: string
|
||||
|
|
|
@ -15,15 +15,20 @@
|
|||
|
||||
import { VNode } from './vnode/VNode';
|
||||
|
||||
const currentRootStack: VNode[] = [];
|
||||
const currentRootStack: (VNode | undefined)[] = [];
|
||||
let index = -1;
|
||||
export function getCurrentRoot() {
|
||||
return currentRootStack[currentRootStack.length - 1];
|
||||
return currentRootStack[index];
|
||||
}
|
||||
|
||||
export function pushCurrentRoot(root: VNode) {
|
||||
return currentRootStack.push(root);
|
||||
index++;
|
||||
currentRootStack[index] = root;
|
||||
}
|
||||
|
||||
export function popCurrentRoot() {
|
||||
return currentRootStack.pop();
|
||||
const target = currentRootStack[index];
|
||||
currentRootStack[index] = undefined;
|
||||
index--;
|
||||
return target;
|
||||
}
|
||||
|
|
|
@ -228,6 +228,40 @@ export function calcStartUpdateVNode(treeRoot: VNode) {
|
|||
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) {
|
||||
const preMode = copyExecuteMode();
|
||||
|
@ -299,40 +333,6 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
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) {
|
||||
runAsyncEffects();
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
import { BELONG_CLASS_VNODE_KEY } from './vnode/VNode';
|
||||
|
||||
export { VNode } from './vnode/VNode';
|
||||
|
||||
type Trigger<A> = (A) => void;
|
||||
|
@ -32,7 +34,7 @@ export type JSXElement = {
|
|||
key: any;
|
||||
ref: any;
|
||||
props: any;
|
||||
belongClassVNode: any;
|
||||
[BELONG_CLASS_VNODE_KEY]: any;
|
||||
};
|
||||
|
||||
export type ProviderType<T> = {
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
import { isSameType, getIteratorFn, isTextType, isIteratorType, isObjectType } from './DiffTools';
|
||||
import { travelChildren } from '../vnode/VNodeUtils';
|
||||
import { markVNodePath } from '../utils/vNodePath';
|
||||
import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode';
|
||||
|
||||
enum DiffCategory {
|
||||
TEXT_NODE = 'TEXT_NODE',
|
||||
|
@ -166,11 +167,11 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
if (oldNode === null || !isSameType(oldNode, newChild)) {
|
||||
resultNode = createVNodeFromElement(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, newChild.props);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||
}
|
||||
break;
|
||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||
|
@ -231,6 +232,19 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
|
|||
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数组类型的节点,核心算法
|
||||
function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array<any>): VNode | null {
|
||||
let resultingFirstChild: VNode | null = null;
|
||||
|
@ -478,19 +492,6 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC
|
|||
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(
|
||||
parentNode: VNode,
|
||||
|
@ -570,7 +571,7 @@ function diffObjectNodeHandler(
|
|||
} else if (isSameType(canReuseNode, newChild)) {
|
||||
resultNode = updateVNode(canReuseNode, newChild.props);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||
startDelVNode = resultNode.next;
|
||||
resultNode.next = null;
|
||||
}
|
||||
|
@ -583,7 +584,7 @@ function diffObjectNodeHandler(
|
|||
} else {
|
||||
resultNode = createVNodeFromElement(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
resultNode[BELONG_CLASS_VNODE_KEY] = newChild[BELONG_CLASS_VNODE_KEY];
|
||||
}
|
||||
}
|
||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||
|
|
|
@ -18,6 +18,12 @@ import type { VNode } from '../Types';
|
|||
import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook';
|
||||
import { HookStage, setHookStage } from './HookStage';
|
||||
|
||||
function resetGlobalVariable() {
|
||||
setHookStage(null);
|
||||
setLastTimeHook(null);
|
||||
setCurrentHook(null);
|
||||
}
|
||||
|
||||
// hook对外入口
|
||||
export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
||||
funcComp: (props: Props, arg: Arg) => any,
|
||||
|
@ -57,9 +63,3 @@ export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
|||
|
||||
return comp;
|
||||
}
|
||||
|
||||
function resetGlobalVariable() {
|
||||
setHookStage(null);
|
||||
setLastTimeHook(null);
|
||||
setCurrentHook(null);
|
||||
}
|
||||
|
|
|
@ -21,27 +21,17 @@ import { getHookStage, HookStage } from './HookStage';
|
|||
import { isArrayEqual } from '../utils/compare';
|
||||
import { getProcessingVNode } from '../GlobalVar';
|
||||
|
||||
export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void {
|
||||
// 异步触发的effect
|
||||
useEffect(effectFunc, deps, EffectConstant.Effect);
|
||||
}
|
||||
function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect {
|
||||
const effect: Effect = {
|
||||
effect: effectFunc,
|
||||
removeEffect: removeFunc,
|
||||
dependencies: deps,
|
||||
effectConstant: effectConstant,
|
||||
};
|
||||
|
||||
export function useLayoutEffectImpl(effectFunc: () => (() => void) | void, deps?: Array<any> | null): void {
|
||||
// 同步触发的effect
|
||||
useEffect(effectFunc, deps, EffectConstant.LayoutEffect);
|
||||
}
|
||||
getProcessingVNode().effectList.push(effect);
|
||||
|
||||
function useEffect(effectFunc: () => (() => void) | void, deps: Array<any> | void | null, effectType: number): void {
|
||||
const stage = getHookStage();
|
||||
if (stage === null) {
|
||||
throwNotInFuncError();
|
||||
}
|
||||
|
||||
if (stage === HookStage.Init) {
|
||||
useEffectForInit(effectFunc, deps, effectType);
|
||||
} else if (stage === HookStage.Update) {
|
||||
useEffectForUpdate(effectFunc, deps, effectType);
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function createEffect(effectFunc, removeFunc, deps, effectConstant): Effect {
|
||||
const effect: Effect = {
|
||||
effect: effectFunc,
|
||||
removeEffect: removeFunc,
|
||||
dependencies: deps,
|
||||
effectConstant: effectConstant,
|
||||
};
|
||||
function useEffect(effectFunc: () => (() => void) | void, deps: Array<any> | void | null, effectType: number): void {
|
||||
const stage = getHookStage();
|
||||
if (stage === null) {
|
||||
throwNotInFuncError();
|
||||
}
|
||||
|
||||
getProcessingVNode().effectList.push(effect);
|
||||
|
||||
return effect;
|
||||
if (stage === HookStage.Init) {
|
||||
useEffectForInit(effectFunc, deps, effectType);
|
||||
} 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);
|
||||
}
|
||||
|
|
|
@ -18,20 +18,6 @@ import { getHookStage } from './HookStage';
|
|||
import { throwNotInFuncError } from './BaseHook';
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -22,29 +22,6 @@ import { getHookStage, HookStage } from './HookStage';
|
|||
import type { VNode } from '../Types';
|
||||
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数组
|
||||
function insertUpdate<S, A>(action: A, hook: Hook<S, A>): 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];
|
||||
}
|
||||
|
||||
// 计算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
|
||||
function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer): [S, Trigger<A>] {
|
||||
if (currentHookUpdates !== null) {
|
||||
|
@ -135,21 +131,25 @@ function updateReducerHookState<S, A>(currentHookUpdates, currentHook, reducer):
|
|||
return [currentHook.state.stateValue, currentHook.state.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);
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,28 +40,6 @@ export function isSchedulingEffects() {
|
|||
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() {
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(InRender, true);
|
||||
|
@ -98,6 +76,28 @@ export function runAsyncEffects() {
|
|||
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
|
||||
export function callEffectRemove(vNode: VNode) {
|
||||
const effectList: EffectList = vNode.effectList;
|
||||
|
|
|
@ -52,6 +52,7 @@ import {
|
|||
import { handleSubmitError } from '../ErrorHandler';
|
||||
import { travelVNodeTree, clearVNode, isDomVNode, getSiblingDom } from '../vnode/VNodeUtils';
|
||||
import { shouldAutoFocus } from '../../dom/utils/Common';
|
||||
import { BELONG_CLASS_VNODE_KEY } from '../vnode/VNode';
|
||||
|
||||
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
||||
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) {
|
||||
const ref = vNode.ref;
|
||||
|
||||
|
@ -166,60 +183,6 @@ function detachRef(vNode: VNode, isOldRef?: boolean) {
|
|||
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
|
||||
function unmountNestedVNodes(vNode: VNode): void {
|
||||
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()
|
||||
function unmountDomComponents(vNode: VNode): void {
|
||||
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 {
|
||||
const realNode = vNode.realNode;
|
||||
const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制
|
||||
|
@ -394,6 +398,13 @@ function submitDeletion(vNode: VNode): void {
|
|||
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 {
|
||||
switch (vNode.tag) {
|
||||
case FunctionComponent:
|
||||
|
@ -413,13 +424,9 @@ function submitUpdate(vNode: VNode): void {
|
|||
listenToPromise(vNode);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function submitSuspenseComponent(vNode: VNode) {
|
||||
const { childStatus } = vNode.suspenseState;
|
||||
if (childStatus !== SuspenseChildStatus.Init) {
|
||||
hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,63 +41,6 @@ const LOOPING_UPDATE_LIMIT = 50;
|
|||
let loopingUpdateCount = 0;
|
||||
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>) {
|
||||
let node;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,14 @@ export function isOverTime() {
|
|||
return false;
|
||||
}
|
||||
|
||||
function asyncCall() {
|
||||
if (isTestRuntime) {
|
||||
setTimeout(callRenderTasks, 0);
|
||||
} else {
|
||||
port2.postMessage(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 1、设置deadline;2、回调TaskExecutor传过来的browserCallback
|
||||
const callRenderTasks = () => {
|
||||
if (browserCallback === null) {
|
||||
|
@ -61,14 +69,6 @@ if (typeof MessageChannel === 'function') {
|
|||
isTestRuntime = true;
|
||||
}
|
||||
|
||||
function asyncCall() {
|
||||
if (isTestRuntime) {
|
||||
setTimeout(callRenderTasks, 0);
|
||||
} else {
|
||||
port2.postMessage(null);
|
||||
}
|
||||
}
|
||||
|
||||
export function requestBrowserCallback(callback) {
|
||||
browserCallback = callback;
|
||||
|
||||
|
|
|
@ -27,16 +27,6 @@ let callingQueueTask: any | null = null;
|
|||
// 防止重入
|
||||
let isCallingRenderQueue = false;
|
||||
|
||||
export function callRenderQueueImmediate() {
|
||||
if (callingQueueTask !== null) {
|
||||
// 取消异步调度
|
||||
cancelTask(callingQueueTask);
|
||||
callingQueueTask = null;
|
||||
}
|
||||
|
||||
callRenderQueue();
|
||||
}
|
||||
|
||||
// 执行render回调
|
||||
function callRenderQueue() {
|
||||
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) {
|
||||
if (renderQueue === null) {
|
||||
renderQueue = [callback];
|
||||
|
|
|
@ -38,6 +38,8 @@ import type { Hook } from '../hooks/HookType';
|
|||
import { InitFlag } from './VNodeFlags';
|
||||
import { Observer } from '../../horizonx/proxy/Observer';
|
||||
|
||||
export const BELONG_CLASS_VNODE_KEY = Symbol('belongClassVNode');
|
||||
|
||||
export class VNode {
|
||||
tag: VNodeTag;
|
||||
key: string | null; // 唯一标识符
|
||||
|
@ -97,7 +99,7 @@ export class VNode {
|
|||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||
delegatedEvents: Set<string>;
|
||||
|
||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
[BELONG_CLASS_VNODE_KEY]: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
|
||||
// 状态管理器HorizonX使用
|
||||
isStoreChange: boolean;
|
||||
|
@ -200,6 +202,8 @@ export class VNode {
|
|||
break;
|
||||
case Profiler:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,6 +194,8 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) {
|
|||
|
||||
vNode.updates = [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return vNode;
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
|
||||
import {isComment} from '../../dom/utils/Common';
|
||||
import {getNearestVNode} from '../../dom/DOMInternalKeys';
|
||||
import {Addition, InitFlag} from './VNodeFlags';
|
||||
import { BELONG_CLASS_VNODE_KEY } from './VNode';
|
||||
|
||||
export function travelChildren(
|
||||
beginVNode: VNode | null,
|
||||
|
@ -124,7 +124,7 @@ export function clearVNode(vNode: VNode) {
|
|||
|
||||
vNode.toUpdateNodes = null;
|
||||
|
||||
vNode.belongClassVNode = null;
|
||||
vNode[BELONG_CLASS_VNODE_KEY] = null;
|
||||
if (window.__HORIZON_DEV_HOOK__) {
|
||||
const hook = window.__HORIZON_DEV_HOOK__;
|
||||
hook.deleteVNode(vNode);
|
||||
|
@ -169,13 +169,6 @@ export function findDOMByClassInst(inst) {
|
|||
return domVNode !== null ? domVNode.realNode : null;
|
||||
}
|
||||
|
||||
// 判断dom树是否已经挂载
|
||||
export function isMounted(vNode: VNode) {
|
||||
const rootNode = getTreeRootVNode(vNode);
|
||||
// 如果根节点是 Dom 类型节点,表示已经挂载
|
||||
return rootNode.tag === TreeRoot;
|
||||
}
|
||||
|
||||
function getTreeRootVNode(vNode) {
|
||||
let node = vNode;
|
||||
while (node.parent) {
|
||||
|
@ -184,6 +177,13 @@ function getTreeRootVNode(vNode) {
|
|||
return node;
|
||||
}
|
||||
|
||||
// 判断dom树是否已经挂载
|
||||
export function isMounted(vNode: VNode) {
|
||||
const rootNode = getTreeRootVNode(vNode);
|
||||
// 如果根节点是 Dom 类型节点,表示已经挂载
|
||||
return rootNode.tag === TreeRoot;
|
||||
}
|
||||
|
||||
// 找到相邻的DOM
|
||||
export function getSiblingDom(vNode: VNode): Element | null {
|
||||
let node: VNode = vNode;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
* openGauss is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
|
||||
describe('JSX Element test', () => {
|
||||
it('symbol attribute prevent cloneDeep unlimited loop', function () {
|
||||
|
||||
function cloneDeep(obj) {
|
||||
const result = {};
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (obj[key] && typeof obj[key] === 'object') {
|
||||
result[key] = cloneDeep(obj[key]);
|
||||
} else {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
})
|
||||
return result;
|
||||
}
|
||||
class Demo extends Horizon.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
hello
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ele = Horizon.createElement(Demo);
|
||||
const copy = cloneDeep(ele);
|
||||
expect(copy.vtype).toEqual(ele.vtype);
|
||||
expect(Object.getOwnPropertySymbols(copy).length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue