From db995c6ca7c1fa9dedee0318a90afce294ff726f Mon Sep 17 00:00:00 2001 From: fancyzhong Date: Thu, 20 Jun 2024 18:33:05 +0800 Subject: [PATCH] build: v4.3.6 --- CHANGELOG.md | 4 ++++ package.json | 2 +- packages/helux-core/src/consts/index.ts | 4 ++++ packages/helux-core/src/consts/user.ts | 2 +- packages/helux-core/src/factory/common/ctor.ts | 2 +- .../helux-core/src/factory/common/derived.ts | 2 +- .../helux-core/src/factory/creator/globalId.ts | 5 +++-- .../src/factory/creator/operateState.ts | 3 ++- packages/helux-core/src/helpers/blockCtx.ts | 8 +++++++- packages/helux-core/src/helpers/fnCtx.ts | 10 +++++++++- packages/helux-core/src/helpers/fnDep.ts | 11 +++++++++-- packages/helux-core/src/helpers/insCtx.ts | 18 +++++++++++++++--- packages/helux-core/src/helpers/state.ts | 7 ++++++- .../helux-core/src/hooks/common/derived.ts | 5 ++--- .../src/hooks/common/useAtomLogic.ts | 2 +- packages/helux-core/src/types/api.d.ts | 2 +- packages/helux-utils/src/index.d.ts | 2 ++ packages/helux-utils/src/is.ts | 2 +- 18 files changed, 70 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1972709..ea5a1097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ click helux-core [change log](./packages/helux-core/CHANGELOG.md) to see more details +[released] - 2024-06-20 + +relese `4.3.6` to fix server mem leak( add `RUN_AT_SERVER` judgement) + [released] - 2024-01-16 - fix issue [136](https://github.com/heluxjs/helux/issues/136) diff --git a/package.json b/package.json index ca86cec5..a1c0963a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "helux", - "version": "4.3.5", + "version": "4.3.6", "description": "A reactive atomic state engine for React like.", "keywords": [], "author": { diff --git a/packages/helux-core/src/consts/index.ts b/packages/helux-core/src/consts/index.ts index daff88e9..3fbff645 100644 --- a/packages/helux-core/src/consts/index.ts +++ b/packages/helux-core/src/consts/index.ts @@ -7,6 +7,10 @@ export const PROTO_KEY = '__proto__'; export const HAS_PROXY = isProxyAvailable(); +// export const RUN_AT_SERVER = isServer(); +export const RUN_AT_SERVER = false; +// export const RUN_AT_SERVER = true; + /** 提供给 sync 返回 undefined 时之用 */ export const UNDEFINED = createSymbol('HeluxUndefined'); diff --git a/packages/helux-core/src/consts/user.ts b/packages/helux-core/src/consts/user.ts index 0cd34bd2..ecdd99d5 100644 --- a/packages/helux-core/src/consts/user.ts +++ b/packages/helux-core/src/consts/user.ts @@ -1,6 +1,6 @@ import { VER as limuVer } from 'limu'; -export const VER = '4.3.5'; +export const VER = '4.3.6'; export const LIMU_VER = limuVer; diff --git a/packages/helux-core/src/factory/common/ctor.ts b/packages/helux-core/src/factory/common/ctor.ts index a5679a53..a04a0d72 100644 --- a/packages/helux-core/src/factory/common/ctor.ts +++ b/packages/helux-core/src/factory/common/ctor.ts @@ -138,7 +138,7 @@ export function newMutateFnItem(partial?: Partial): IMutateFnS export function newFnCtx() { const base: IFnCtx = { - fnKey: '', // 在 feDep.mapFn 阶段会生成 + fnKey: '', // 在 fnDep.mapFn 阶段会生成 fn: noop, subFnInfo: fnItem, checkDeadCycle: true, diff --git a/packages/helux-core/src/factory/common/derived.ts b/packages/helux-core/src/factory/common/derived.ts index 354e58b5..4c816019 100644 --- a/packages/helux-core/src/factory/common/derived.ts +++ b/packages/helux-core/src/factory/common/derived.ts @@ -174,7 +174,7 @@ export function initDeriveFn(options: IInitDeriveFnOptions) { // 第一次调用时,如未显示定义 immediate 值,则触发规律是没有 fn 则执行,有 fn 则不执行 const canRunTask = runAsync && asyncType === TASK && (immediate ?? !options.fn); if (task && canRunTask) { - runFn(curFnKey, { isFirstCall: true, sn: fnCtx.renderInfo.sn + 1 }) + runFn(curFnKey, { isFirstCall: true, sn: fnCtx.renderInfo.sn + 1, throwErr: true }) .then((data: [any, Error | null]) => { checkResult(fnCtx, data[0], forAtom); }) diff --git a/packages/helux-core/src/factory/creator/globalId.ts b/packages/helux-core/src/factory/creator/globalId.ts index 1a13ffe2..d18eb4dd 100644 --- a/packages/helux-core/src/factory/creator/globalId.ts +++ b/packages/helux-core/src/factory/creator/globalId.ts @@ -1,5 +1,5 @@ import { delListItem, nodupPush, safeMapGet } from '@helux/utils'; -import { STATE_TYPE } from '../../consts'; +import { RUN_AT_SERVER, STATE_TYPE } from '../../consts'; import { getInternal } from '../../helpers/state'; import type { CoreApiCtx } from '../../types/api-ctx'; import type { Fn, NumStrSymbol } from '../../types/base'; @@ -37,7 +37,8 @@ export function getGlobalEmptyInternal() { } export function mapGlobalId(id: NumStrSymbol, insKey: number) { - if (!id) return; + // 服务端运行时,不做 globalId 映射,避免内存浪费 + if (!id || RUN_AT_SERVER) return; const keys = getGlobalIdInsKeys(id); nodupPush(keys, insKey); } diff --git a/packages/helux-core/src/factory/creator/operateState.ts b/packages/helux-core/src/factory/creator/operateState.ts index d3ab5a67..a00b39d0 100644 --- a/packages/helux-core/src/factory/creator/operateState.ts +++ b/packages/helux-core/src/factory/creator/operateState.ts @@ -61,8 +61,9 @@ export function handleOperate(opParams: IOperateParams, opts: { internal: TInter if (currReactive.onRead) { currReactive.onRead(opParams); } else { + const runingFnCtx = getRunningFn().fnCtx; // 支持对 draft 操作时可以收集到依赖: draft.a = draft.b + 1 - if (getRunningFn().fnCtx) { + if (runingFnCtx) { recordFnDepKeys([depKey], { sharedKey }); } // 仅 top reactive 触发以下逻辑,为 block 收集依赖 diff --git a/packages/helux-core/src/helpers/blockCtx.ts b/packages/helux-core/src/helpers/blockCtx.ts index f5745cc6..54be0949 100644 --- a/packages/helux-core/src/helpers/blockCtx.ts +++ b/packages/helux-core/src/helpers/blockCtx.ts @@ -1,3 +1,4 @@ +import { RUN_AT_SERVER } from '../consts'; import { getBlockCtxMap } from '../factory/common/blockScope'; import { genBlockKey } from '../factory/common/key'; import { getBlockScope } from '../factory/common/speedup'; @@ -30,7 +31,12 @@ export function initBlockCtx(isDynamic: boolean, enableStatus = false) { } const blockKey = genBlockKey(); const blockCtx = newBlockCtx(blockKey, enableStatus); - getBlockCtxMap(isDynamic).set(blockKey, blockCtx); + + // 非服务器端执行才记录 blockCtx ,避免内存泄露 + if (!RUN_AT_SERVER) { + getBlockCtxMap(isDynamic).set(blockKey, blockCtx); + } + return blockCtx; } diff --git a/packages/helux-core/src/helpers/fnCtx.ts b/packages/helux-core/src/helpers/fnCtx.ts index 7980564c..3948a5ff 100644 --- a/packages/helux-core/src/helpers/fnCtx.ts +++ b/packages/helux-core/src/helpers/fnCtx.ts @@ -1,4 +1,5 @@ import { delListItem, includeOne, matchDictKey, nodupPush } from '@helux/utils'; +import { RUN_AT_SERVER } from '../consts'; import { newFnCtx } from '../factory/common/ctor'; import { getCtxMap, getFnCtx, getFnKey, markFnKey } from '../factory/common/fnScope'; import { getFnScope } from '../factory/common/speedup'; @@ -78,7 +79,14 @@ export function registerFn(fn: Fn, options: { specificProps: Partial & { const props = { fn, fnKey, ...specificProps }; // 如 fnCtxBase 存在则 fnCtx 指向用户透传的 fnCtxBase const fnCtx = fnCtxBase ? Object.assign(fnCtxBase, props) : buildFnCtx(props); - getCtxMap(scopeType).set(fnKey, fnCtx); + + // static 调用派生时始终记录 fnCtx + // hook 调用派生时仅在非服务器端执行才记录 fnCtx ,避免内存泄露 + if (scopeType === 'static' || !RUN_AT_SERVER) { + // debugger; + getCtxMap(scopeType).set(fnKey, fnCtx); + } + return fnCtx; } diff --git a/packages/helux-core/src/helpers/fnDep.ts b/packages/helux-core/src/helpers/fnDep.ts index b3be426d..11cb8957 100644 --- a/packages/helux-core/src/helpers/fnDep.ts +++ b/packages/helux-core/src/helpers/fnDep.ts @@ -1,5 +1,5 @@ import { nodupPush, safeMapGet } from '@helux/utils'; -import { DERIVE, EXPIRE_MS, NOT_MOUNT, PROTO_KEY, SIZE_LIMIT, UNMOUNT } from '../consts'; +import { DERIVE, EXPIRE_MS, NOT_MOUNT, PROTO_KEY, RUN_AT_SERVER, SIZE_LIMIT, UNMOUNT } from '../consts'; import { delFnDepData, getFnCtx, getRunningFn, opUpstreamFnKey } from '../factory/common/fnScope'; import { hasChangedNode } from '../factory/common/sharedScope'; import { getFnScope } from '../factory/common/speedup'; @@ -24,6 +24,12 @@ export function recordFnDepKeys(inputDepKeys: string[], options: { sharedKey?: n DEPS_CB.current()(inputDepKeys); return; } + const { fnKey, scopeType } = fnCtx; + // 服务端运行的话,不记录任何 hook 实例对应的相关映射关系,避免不必要的服务端内存开销 + if (RUN_AT_SERVER && scopeType === 'hook') { + return; + } + const { DEPKEY_FNKEYS_MAP, SKEY_FNKEYS_MAP } = getFnScope(); const { belongCtx, sharedKey } = options; @@ -42,7 +48,6 @@ export function recordFnDepKeys(inputDepKeys: string[], options: { sharedKey?: n nodupPush(belongCtx.nextLevelFnKeys, runningFnCtx.fnKey); } - const { fnKey } = fnCtx; inputDepKeys.forEach((depKey: string) => { if (PROTO_KEY === depKey || isIgnore) { return; @@ -70,6 +75,8 @@ export function ensureFnDepData(fnCtx?: IFnCtx) { /** TODO 后续接入内置 useEffect 后,这里可考虑移除 */ export function recoverDep(fnCtx: IFnCtx) { const { FNKEY_HOOK_CTX_MAP, UNMOUNT_INFO_MAP } = getFnScope(); + if (RUN_AT_SERVER) return; + const { fnKey } = fnCtx; FNKEY_HOOK_CTX_MAP.set(fnKey, fnCtx); opUpstreamFnKey(fnCtx, true); diff --git a/packages/helux-core/src/helpers/insCtx.ts b/packages/helux-core/src/helpers/insCtx.ts index 0d4613b4..55e8b1bb 100644 --- a/packages/helux-core/src/helpers/insCtx.ts +++ b/packages/helux-core/src/helpers/insCtx.ts @@ -1,6 +1,6 @@ import { delListItem, enureReturnArr, isFn, isSymbol, nodupPush, prefixValKey, warn } from '@helux/utils'; import { immut } from 'limu'; -import { DICT, EXPIRE_MS, IS_DERIVED_ATOM, NOT_MOUNT, OTHER, RENDER_END, RENDER_START } from '../consts'; +import { DICT, EXPIRE_MS, IS_DERIVED_ATOM, NOT_MOUNT, OTHER, RENDER_END, RENDER_START, RUN_AT_SERVER } from '../consts'; import { newOpParams } from '../factory/common/ctor'; import { hasRunningFn } from '../factory/common/fnScope'; import { genInsKey } from '../factory/common/key'; @@ -65,6 +65,8 @@ export function attachInsProxyState(insCtx: InsCtxDef) { const { rawState, isDeep, sharedKey, onRead, forAtom } = internal; if (isDeep) { const onOperate: OnOperate = (opParams) => { + // 服务端执行,无需记录依赖关系 + if (RUN_AT_SERVER) return; const { isBuiltInFnKey, key } = opParams; if (isBuiltInFnKey) return; if (isSymbol(key)) { @@ -99,6 +101,8 @@ export function attachInsProxyState(insCtx: InsCtxDef) { return true; }, get: (target: Dict, key: string) => { + // 服务端执行,无需记录依赖关系 + if (RUN_AT_SERVER) return; const value = target[key]; if (isSymbol(key)) { return handleHeluxKey(true, forAtom, sharedKey, key, value); @@ -252,8 +256,15 @@ export function buildInsCtx(options: Ext): InsCtxDef { } }, }; + globalId && mapGlobalId(globalId, insKey); attachInsProxyState(insCtx); + + // 是服务器端执行的话,不需要真正的去映射实例和共享状态关系,避免内存泄露 + if (RUN_AT_SERVER) { + return insCtx; + } + internal.mapInsCtx(insCtx, insKey); internal.recordId(id, insKey); @@ -281,7 +292,7 @@ export function buildInsCtx(options: Ext): InsCtxDef { export function attachInsDerivedResult(fnCtx: IFnCtx) { const { result, forAtom } = fnCtx; - // MARK: 此计算结果不具备依赖收集特性,如需要此特性可使用 share接口的 mutate 配置可变派生结果 + // MARK: 此计算结果不具备依赖收集特性,如需要此特性可使用 share 接口的 mutate 配置可变派生结果 // LABEL: proxyResult fnCtx.proxyResult = createOb(result, { set: () => { @@ -292,7 +303,8 @@ export function attachInsDerivedResult(fnCtx: IFnCtx) { if (IS_DERIVED_ATOM === resultKey) { return forAtom; } - if (RENDER_START === fnCtx.renderStatus) { + // 服务端运行时,就不需要记录hook使用导出结果时的相关映射关系了,避免内存浪费 + if (RENDER_START === fnCtx.renderStatus && !RUN_AT_SERVER) { fnDep.ensureFnDepData(fnCtx); } return result[resultKey]; diff --git a/packages/helux-core/src/helpers/state.ts b/packages/helux-core/src/helpers/state.ts index 736e6782..a7b374f8 100644 --- a/packages/helux-core/src/helpers/state.ts +++ b/packages/helux-core/src/helpers/state.ts @@ -1,5 +1,5 @@ import { getSafeNext, warn } from '@helux/utils'; -import { SHARED_KEY } from '../consts'; +import { RUN_AT_SERVER, SHARED_KEY } from '../consts'; import { getInternalMap } from '../factory/common/internal'; import { getSharedScope } from '../factory/common/speedup'; import type { TInternal } from '../factory/creator/buildInternal'; @@ -76,6 +76,11 @@ export function getSharedState(sharedKey: number) { } export function recordMod(sharedState: Dict, options: ParsedOptions) { + // 服务端运行,没必要记录模块信息到 global 上,避免服务端内存浪费、冗余的模块重复信息提示(nextjs里同一个地方的share代码会被多次调用) + if (RUN_AT_SERVER) { + return; + } + const { rootState, ctx } = getRoot(); const { moduleName, usefulName } = options; const existedShared = rootState[usefulName]; diff --git a/packages/helux-core/src/hooks/common/derived.ts b/packages/helux-core/src/hooks/common/derived.ts index 2b1fae36..78a27073 100644 --- a/packages/helux-core/src/hooks/common/derived.ts +++ b/packages/helux-core/src/hooks/common/derived.ts @@ -61,10 +61,9 @@ export function genDerivedResult(deriveCtx: IDeriveCtx, options: IUseDerivedLogi const isChanged = isInputChanged(fnCtx, input, result); if (!isChanged) { return; - } else { - isCtxChanged = true; - ensureHotReload(fnCtx); } + isCtxChanged = true; + ensureHotReload(fnCtx); } deriveCtx.input = result; diff --git a/packages/helux-core/src/hooks/common/useAtomLogic.ts b/packages/helux-core/src/hooks/common/useAtomLogic.ts index efb8c2d0..92ec6304 100644 --- a/packages/helux-core/src/hooks/common/useAtomLogic.ts +++ b/packages/helux-core/src/hooks/common/useAtomLogic.ts @@ -1,4 +1,4 @@ -import { Fn } from 'helux'; +import type { Fn } from 'helux'; import { RENDER_END, RENDER_START } from '../../consts'; import type { InsCtxDef } from '../../factory/creator/buildInternal'; import { INS_CTX } from '../../factory/creator/current'; diff --git a/packages/helux-core/src/types/api.d.ts b/packages/helux-core/src/types/api.d.ts index 9206802d..6c75f1d9 100644 --- a/packages/helux-core/src/types/api.d.ts +++ b/packages/helux-core/src/types/api.d.ts @@ -68,7 +68,7 @@ import type { } from './base'; export declare const cst: { - VER: '4.3.5'; + VER: '4.3.6'; LIMU_VER: string; EVENT_NAME: { /** 共享状态创建时的事件 */ diff --git a/packages/helux-utils/src/index.d.ts b/packages/helux-utils/src/index.d.ts index 55c06d24..0b9e9751 100644 --- a/packages/helux-utils/src/index.d.ts +++ b/packages/helux-utils/src/index.d.ts @@ -15,6 +15,8 @@ export declare function noopVoid(...args: any[]): void; export declare function noopArgs(...args: T): T; export declare function noopArr(...args: any[]): any[]; export declare function noopAny(...args: any[]): any; +/** 是否是在 server 端运行 */ +export declare function isServer(): boolean; export declare function isMap(mayMap: any): boolean; export declare function isMax(input: number): boolean; export declare function isDebug(): boolean; diff --git a/packages/helux-utils/src/is.ts b/packages/helux-utils/src/is.ts index 857ac8dd..d8534ace 100644 --- a/packages/helux-utils/src/is.ts +++ b/packages/helux-utils/src/is.ts @@ -6,7 +6,7 @@ const toString = Object.prototype.toString; const MAP_DESC = '[object Map]'; export function isServer() { - return !(typeof window != 'undefined' && window.document); + return GLOBAL_REF.window === undefined && GLOBAL_REF.global !== undefined; } export function isMap(mayMap: any) {