From 1c561b9ec9e587cb55b639d49d845b95b56f219d Mon Sep 17 00:00:00 2001 From: fantasticsoul Date: Sun, 31 Dec 2023 20:33:56 +0800 Subject: [PATCH] build(3.6.2): see changelog --- docs-src/docs/guide/atom.md | 13 ++- docs-src/docs/guide/dep-tracking.md | 78 ++++++++------ docs-src/docs/guide/quick-start.md | 38 ++++--- docs-src/docs/guide/reactive.md | 67 +++++++----- docs-src/docs/guide/signal.md | 29 ++--- package.json | 2 +- packages/helux-core/src/consts/user.ts | 2 +- .../helux-core/src/factory/common/ctor.ts | 6 +- .../src/factory/creator/operateState.ts | 5 + .../src/factory/creator/reactive.ts | 102 ++++++++++++------ packages/helux-core/src/helpers/insCtx.ts | 5 +- .../helux-core/src/hooks/common/shared.ts | 6 +- packages/helux-core/src/types/api.d.ts | 26 ++--- packages/helux-core/src/types/base.d.ts | 6 ++ packages/helux-core/src/types/inner.d.ts | 4 + 15 files changed, 241 insertions(+), 148 deletions(-) diff --git a/docs-src/docs/guide/atom.md b/docs-src/docs/guide/atom.md index f486f4ca..05d65975 100644 --- a/docs-src/docs/guide/atom.md +++ b/docs-src/docs/guide/atom.md @@ -7,7 +7,7 @@ order: 2 # atom -阅读此章节可快速了解`atom`接口简单用法,更多使用方式请查阅[atom接口文档](xx)。 +阅读此章节可快速了解`atom`接口简单用法,更多使用方式请查阅[atom 接口文档](xx)。 ## 定义共享状态 @@ -31,13 +31,12 @@ console.log(sharedObj); // sharedObj: {info: { born: 2023 }} 优先考虑 share 共享字典对象,由于`share`接口没有装箱`{val: T}` 的操作,当共享对象为 `object` 时,可优先使用`share`来共享对象,避免一些无自动拆箱的场景多做一次`.val`取值操作 ::: - ## 修改共享状态 钩子 `useAtom` 返回一个元组,使用方式完全对齐 `react.useState` 接口,react 用户可 0 成本上手此方式,在组件内使用`useAtom`接口来获得组件内使用的共享对象,数据变更后自动通知组件重渲染 ```tsx | pure -import { atom, useAtom } from 'helux'; +import { useAtom } from 'helux'; const [numAtom] = atomx(1); function Demo() { @@ -60,7 +59,7 @@ export default function Demo() { ```tsx | pure import { atom, useAtom } from 'helux'; -const [numAtom, setAtom] = atom(1); +const [numAtom, setAtom] = atom(1); function Demo() { const [num] = useAtom(numAtom); @@ -70,7 +69,7 @@ function Demo() { ```tsx import { atom, useAtom } from 'helux'; -const [numAtom, setAtom] = atom(1); +const [numAtom, setAtom] = atom(1); export default function Demo() { const [num] = useAtom(numAtom); @@ -81,7 +80,7 @@ export default function Demo() { 可使用`ctx.useState`来简化`useAtom(xxxState)`写法,`ctx`来自于`atom`返回的元组第三位参数、或`atomx`接口返回结果 ```tsx | pure -import { atom, useAtom } from 'helux'; +import { atom } from 'helux'; const [numAtom, setAtom, ctx] = atom(1); // or const ctx = atomx(1); @@ -92,7 +91,7 @@ function Demo() { ``` ```tsx -import { atomx, useAtom } from 'helux'; +import { atomx } from 'helux'; const ctx = atomx(1); function Demo() { diff --git a/docs-src/docs/guide/dep-tracking.md b/docs-src/docs/guide/dep-tracking.md index b3645bd9..3d21268a 100644 --- a/docs-src/docs/guide/dep-tracking.md +++ b/docs-src/docs/guide/dep-tracking.md @@ -14,11 +14,10 @@ order: 4 组件时读取数据节点值时就产生了依赖,这些依赖被收集到`helux`内部为每个组件创建的实例上下文里暂存着,作为更新凭据来使用。 ```tsx | pure -import { useAtom } from 'helux'; const { state, setDraft, useState } = atomx({ a: 1, b: { b1: 1 } }); // 修改草稿,生成具有数据结构共享的新状态,当前修改只会触发 Demo1 组件渲染 -const changeObj = ()=> setDraft((draft) => draft.a = Math.random()); +const changeObj = () => setDraft((draft) => (draft.a = Math.random())); function Demo1() { const [obj] = useState(); @@ -34,11 +33,11 @@ function Demo2() { ``` ```tsx +import { Entry, MarkUpdate } from '@helux/demo-utils'; import { atomx } from 'helux'; -import { MarkUpdate, Entry } from '@helux/demo-utils'; const { state, setDraft, useState } = atomx({ a: 1, b: { b1: 1 } }); -const changeObj = ()=> setDraft((draft) => draft.a = Math.random()); +const changeObj = () => setDraft((draft) => (draft.a = Math.random())); function Demo1() { const [obj, , info] = useState(); @@ -50,7 +49,7 @@ function Demo2() { return {obj.b.b1}; } -export default ()=>( +export default () => ( @@ -66,17 +65,20 @@ export default ()=>( ```tsx | pure import { atomx } from 'helux'; -import { MarkUpdate, Entry } from '@helux/demo-utils'; const { state, setDraft, useState } = atomx({ a: 1, b: { b1: 1 } }); -const changeA = ()=> setDraft((draft) => draft.a += 1); -const changeB = ()=> setDraft((draft) => draft.a.b1 += 1); +const changeA = () => setDraft((draft) => (draft.a += 1)); +const changeB = () => setDraft((draft) => (draft.a.b1 += 1)); function Demo1() { const [obj] = useState(); // 大于 3 时,依赖为 a, b.b1 - if(obj.a>3){ - return

{obj.a} - {obj.b.b1}

; + if (obj.a > 3) { + return ( +

+ {obj.a} - {obj.b.b1} +

+ ); } return

{obj.a}

; @@ -84,29 +86,32 @@ function Demo1() { ``` :::tip -先点击下述示例`changeB1`按钮,发现并不会触发重渲染,然后再点击`plusA`按钮,待到`a`值大于3时,点击`changeB1`按钮,此时组件将被重渲染,点击`minusA`按钮,待到`a`值小于3时,点击`changeB1`按钮,此时组件将被不被重渲染 +先点击下述示例`changeB1`按钮,发现并不会触发重渲染,然后再点击`plusA`按钮,待到`a`值大于 3 时,点击`changeB1`按钮,此时组件将被重渲染,点击`minusA`按钮,待到`a`值小于 3 时,点击`changeB1`按钮,此时组件将被不被重渲染 ::: - ```tsx +import { Entry, MarkUpdate } from '@helux/demo-utils'; import { atomx } from 'helux'; -import { MarkUpdate, Entry } from '@helux/demo-utils'; const { state, setDraft, useState } = atomx({ a: 1, b: { b1: 1 } }); -const plusA = ()=> setDraft((draft) => draft.a += 1); -const minusA = ()=> setDraft((draft) => draft.a -= 1); -const changeB1 = ()=> setDraft((draft) => draft.b.b1 = Date.now()); +const plusA = () => setDraft((draft) => (draft.a += 1)); +const minusA = () => setDraft((draft) => (draft.a -= 1)); +const changeB1 = () => setDraft((draft) => (draft.b.b1 = Date.now())); function Demo1() { - const [obj,,info] = useState(); - if(obj.a>3){ - return {obj.a} - {obj.b.b1}; + const [obj, , info] = useState(); + if (obj.a > 3) { + return ( + + {obj.a} - {obj.b.b1} + + ); } return {obj.a}; } -export default ()=>( +export default () => ( @@ -120,12 +125,16 @@ export default ()=>( ```tsx | pure import { atomx } from 'helux'; -import { MarkUpdate, Entry, noop } from '@helux/demo-utils'; -const { state, setDraft, useState } = atomx({ a: 1, b: { b1: { b2: 1, ok: true } } }); -const changeB1 = ()=> setDraft((draft) => draft.b.b1 = {...draft.b.b1} ); -const changeB1_Ok_oldValue = ()=> setDraft((draft) => draft.b.b1.ok = draft.b.b1.ok ); -const changeB1_Ok_newValue = ()=> setDraft((draft) => draft.b.b1.ok = !draft.b.b1.ok ); +const { state, setDraft, useState } = atomx({ + a: 1, + b: { b1: { b2: 1, ok: true } }, +}); +const changeB1 = () => setDraft((draft) => (draft.b.b1 = { ...draft.b.b1 })); +const changeB1_Ok_oldValue = () => + setDraft((draft) => (draft.b.b1.ok = draft.b.b1.ok)); +const changeB1_Ok_newValue = () => + setDraft((draft) => (draft.b.b1.ok = !draft.b.b1.ok)); // 调用 changeB1_Ok_oldValue changeB1 Demo1 不会被重渲染 // 调用 changeB1_Ok_newValue ,Demo1 被重渲染 @@ -136,20 +145,25 @@ function Demo1() { ``` ```tsx +import { Entry, MarkUpdate } from '@helux/demo-utils'; import { atomx } from 'helux'; -import { MarkUpdate, Entry, noop } from '@helux/demo-utils'; -const { state, setDraft, useState } = atomx({ a: 1, b: { b1: { b2: 1, ok: true } } }); -const changeB1 = ()=> setDraft((draft) => draft.b.b1 = {...draft.b.b1} ); -const changeB1_Ok_oldValue = ()=> setDraft((draft) => draft.b.b1.ok = draft.b.b1.ok ); -const changeB1_Ok_newValue = ()=> setDraft((draft) => draft.b.b1.ok = !draft.b.b1.ok ); +const { state, setDraft, useState } = atomx({ + a: 1, + b: { b1: { b2: 1, ok: true } }, +}); +const changeB1 = () => setDraft((draft) => (draft.b.b1 = { ...draft.b.b1 })); +const changeB1_Ok_oldValue = () => + setDraft((draft) => (draft.b.b1.ok = draft.b.b1.ok)); +const changeB1_Ok_newValue = () => + setDraft((draft) => (draft.b.b1.ok = !draft.b.b1.ok)); function Demo1() { - const [obj,,info] = useState(); + const [obj, , info] = useState(); return obj.b.b1.ok {`${obj.b.b1.ok}`}; } -export default ()=>( +export default () => ( diff --git a/docs-src/docs/guide/quick-start.md b/docs-src/docs/guide/quick-start.md index 634984a4..29bc101e 100644 --- a/docs-src/docs/guide/quick-start.md +++ b/docs-src/docs/guide/quick-start.md @@ -17,9 +17,9 @@ order: 1 import { atom } from 'helux'; // 原始类型 atom -const [ numAtom ] = atom(1); +const [numAtom] = atom(1); // 字典对象类型 atom -const [ objAtom ] = atom({a:1,b:{b1:1}}); +const [objAtom] = atom({ a: 1, b: { b1: 1 } }); ``` ## 修改 atom @@ -27,20 +27,20 @@ const [ objAtom ] = atom({a:1,b:{b1:1}}); 原始值修改 ```ts -const [ numAtom, setAtom ] = atom(1); +const [numAtom, setAtom] = atom(1); setAtom(100); ``` 字典对象修改,基于回调的草稿对象直接修改即可 ```ts -const [ numAtom, setAtom ] = atom({a:1,b:{b1:1}}); -setAtom(draft=> { // draft 已拆箱 { val: T } 为 T +const [numAtom, setAtom] = atom({ a: 1, b: { b1: 1 } }); +setAtom((draft) => { + // draft 已拆箱 { val: T } 为 T draft.b.b1 += 1; }); ``` - ## 观察 atom 可观察整个根对象变化,也可以观察部分节点变化 @@ -48,13 +48,21 @@ setAtom(draft=> { // draft 已拆箱 { val: T } 为 T ```ts import { atom, watch, getSnap } from 'helux'; -watch(()=>{ - console.log(`change from ${getSnap(numAtom).val} to ${numAtom.val}`); -}, ()=>[atom]); +watch( + () => { + console.log(`change from ${getSnap(numAtom).val} to ${numAtom.val}`); + }, + () => [atom], +); -watch(()=>{ - console.log(`change from ${getSnap(numAtom).val.b.b1} to ${numAtom.val.b.b1}`); -}, ()=>[objAtom.val.b.b1]); +watch( + () => { + console.log( + `change from ${getSnap(numAtom).val.b.b1} to ${numAtom.val.b.b1}`, + ); + }, + () => [objAtom.val.b.b1], +); ``` ## 派生 atom @@ -66,8 +74,8 @@ watch(()=>{ ```ts import { atom, derive } from 'helux'; -const [ numAtom, setAtom ] = atom(1); -const plus100 = derive(()=> atom.val + 100); +const [numAtom, setAtom] = atom(1); +const plus100 = derive(() => atom.val + 100); setAtom(100); console.log(plus100); // { val: 200 } @@ -101,7 +109,7 @@ console.log(objAtom2.val.plusA100); // 200 react 组件通过`useAtom` 钩子可使用 atom 共享对象,该钩子返回一个元组,使用方式完全对齐 `react.useState` 接口,react 用户可 0 成本上手此方式 ```tsx | pure -import { atom, useAtom } from 'helux'; +import { useAtom } from 'helux'; const [numAtom] = atomx(1); function Demo() { diff --git a/docs-src/docs/guide/reactive.md b/docs-src/docs/guide/reactive.md index faa56d41..e5a1a15a 100644 --- a/docs-src/docs/guide/reactive.md +++ b/docs-src/docs/guide/reactive.md @@ -18,7 +18,7 @@ import { delay } from '@helux/demo-utils'; // reactive 已自动拆箱 const { state, reactive } = atomx({ a: 1, b: { b1: { b2: 1, ok: true } } }); -async function change(){ +async function change() { reactive.a = 100; console.log(state.val.a); // 1 await delay(1); @@ -37,7 +37,7 @@ const [state, , { reactive }] = share({ a: 1, b: { b1: { b2: 1, ok: true } } }); const { state, reactive } = sharex({ a: 1, b: { b1: { b2: 1, ok: true } } }); // 相比 atom.state,少了主动拆箱的过程 -async function change(){ +async function change() { reactive.a = 100; console.log(state.a); // 1 await delay(1); @@ -52,70 +52,79 @@ async function change(){ ```tsx | pure import { sharex } from 'helux'; -const { reactive, useReactive } = sharex({ a: 1, b: { b1: { b2: 1, ok: true } } }); +const { reactive, useReactive } = sharex({ + a: 1, + b: { b1: { b2: 1, ok: true } }, +}); // 定时修改 a b2 -setTimeout(()=>{ +setTimeout(() => { reactive.a += 1; reactive.b.b1.b2 += 1; }, 2000); // 组件外部切换 ok -function toogleOkOut(){ +function toogleOkOut() { reactive.b.b1.ok = !reactive.b.b1.ok; } -function Demo(){ +function Demo() { const [reactive] = useReactive(); - return

{reactive.a}

+ return

{reactive.a}

; } -function Demo2(){ +function Demo2() { const [reactive] = useReactive(); - return

{reactive.b.b1.b2}

+ return

{reactive.b.b1.b2}

; } -function Demo3(){ +function Demo3() { const [reactive] = useReactive(); // 组件内部切换 ok - const toogle = ()=> reactive.b.b1.ok = !reactive.b.b1.ok; - return

{`${reactive.b.b1.ok}`}

+ const toogle = () => (reactive.b.b1.ok = !reactive.b.b1.ok); + return

{`${reactive.b.b1.ok}`}

; } ``` ```tsx -import { sharex } from 'helux'; import { Entry, MarkUpdate } from '@helux/demo-utils'; +import { sharex } from 'helux'; -const { reactive, useReactive } = sharex({ a: 1, b: { b1: { b2: 1, ok: true } } }); +const { reactive, useReactive } = sharex({ + a: 1, + b: { b1: { b2: 1, ok: true } }, +}); -setInterval(()=>{ +setInterval(() => { reactive.a += 1; // reactive.b.b1.b2 += 1; }, 2000); -function toogleOkOut(){ +function toogleOkOut() { reactive.b.b1.ok = !reactive.b.b1.ok; } -function Demo(){ - const [reactive,, info] = useReactive(); - return {reactive.a} +function Demo() { + const [reactive, , info] = useReactive(); + return {reactive.a}; } -function Demo2(){ - const [reactive, ,info] = useReactive(); - return {reactive.b.b1.b2} +function Demo2() { + const [reactive, , info] = useReactive(); + return {reactive.b.b1.b2}; } -function Demo3(){ - const [reactive, ,info] = useReactive(); - const toogle = ()=> reactive.b.b1.ok = !reactive.b.b1.ok; - return
{`${reactive.b.b1.ok}`}
+function Demo3() { + const [reactive, , info] = useReactive(); + const toogle = () => (reactive.b.b1.ok = !reactive.b.b1.ok); + return ( + +
{`${reactive.b.b1.ok}`}
+
+ ); } -export default ()=> ( +export default () => ( -) +); ``` - diff --git a/docs-src/docs/guide/signal.md b/docs-src/docs/guide/signal.md index 79eda787..f32db719 100644 --- a/docs-src/docs/guide/signal.md +++ b/docs-src/docs/guide/signal.md @@ -7,7 +7,7 @@ order: 3 # signal -`signal`响应机制允许用户跳过`useAtom`直接将数据绑定到视图,实现 **0 hook**编码、**dom粒度**或**块粒度**更新。 +`signal`响应机制允许用户跳过`useAtom`直接将数据绑定到视图,实现 **0 hook**编码、**dom 粒度**或**块粒度**更新。 ## dom 粒度更新 @@ -25,16 +25,16 @@ import { $ } from 'helux'; 点击下述示例`click me`按钮,包装块颜色未变,`update at`值未变,表示整个组件除了`$()`包裹区域被重渲染之外,其他地方都没有被重渲染 ::: - ```tsx -import { share, $ } from 'helux'; import { MarkUpdate } from '@helux/demo-utils'; +import { $, share } from 'helux'; const [state, setState] = share({ a: 1, b: 2, info: { born: '2023-12-31', age: 2 }, }); -const change = ()=>setState((draft) => void (draft.info.born = `${Date.now()}`)); +const change = () => + setState((draft) => void (draft.info.born = `${Date.now()}`)); // or 箭头函数包 {},消除隐式返回值 // change = ()=> setState((draft) => { draft.info.born = `${Date.now()}` }); @@ -67,23 +67,22 @@ const UserBlock = block(() => ( ; ``` - ```tsx -import { share, $, block } from 'helux'; -import { MarkUpdate, Entry } from '@helux/demo-utils'; +import { Entry, MarkUpdate } from '@helux/demo-utils'; +import { block, share } from 'helux'; const [state, setState] = share({ name: 'helux', age: 1, detail: { desc: 'a powerful state engine' }, }); -function changeName(){ - setState(draft=>{ +function changeName() { + setState((draft) => { draft.name = `helux_${Date.now()}`; draft.detail.desc = `desc_${Date.now()}`; }); } -const Block1 = block(()=>{ +const Block1 = block(() => { return (

name: {state.name}

@@ -91,11 +90,15 @@ const Block1 = block(()=>{
); }); -const Block2 = block(()=>{ - return

age: {state.age}

; +const Block2 = block(() => { + return ( + +

age: {state.age}

{' '} +
+ ); }); -export default ()=>( +export default () => ( diff --git a/package.json b/package.json index 03f2a4f2..2b993695 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "helux", - "version": "3.6.0", + "version": "3.6.2", "description": "A state library core that integrates atom, signal, collection dep, derive and watch, it supports all react like frameworks( including react 18 ).", "keywords": [], "author": { diff --git a/packages/helux-core/src/consts/user.ts b/packages/helux-core/src/consts/user.ts index 0707b5b1..558464d1 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 = '3.6.1'; +export const VER = '3.6.2'; 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 68cf161f..03ea7cb0 100644 --- a/packages/helux-core/src/factory/common/ctor.ts +++ b/packages/helux-core/src/factory/common/ctor.ts @@ -19,10 +19,11 @@ export interface IBuildReactiveOpts { onRead?: OnOperate; from?: From; expired?: boolean; + insKey?: number; } export function newReactiveMeta(draft: any, buildOptions: IBuildReactiveOpts, finish: any = noop): IReactiveMeta { - const { desc = '', onRead, from = REACTIVE, depKeys = [], isTop = false, expired = false } = buildOptions; + const { desc = '', onRead, from = REACTIVE, depKeys = [], isTop = false, expired = false, insKey = 0 } = buildOptions; return { draft, finish, @@ -41,6 +42,7 @@ export function newReactiveMeta(draft: any, buildOptions: IBuildReactiveOpts, fi desc, onRead, from, + insKey, }; } @@ -55,6 +57,7 @@ export function newMutateCtx(options: ISetFactoryOpts): IMutateCtx { sn = genRenderSN(), isFirstCall = false, desc = '', + onRead, } = options; return { fnKey: '', @@ -75,6 +78,7 @@ export function newMutateCtx(options: ISetFactoryOpts): IMutateCtx { sn, isFirstCall, desc, + onRead, }; } diff --git a/packages/helux-core/src/factory/creator/operateState.ts b/packages/helux-core/src/factory/creator/operateState.ts index d6e68487..5e13cbb3 100644 --- a/packages/helux-core/src/factory/creator/operateState.ts +++ b/packages/helux-core/src/factory/creator/operateState.ts @@ -61,7 +61,12 @@ export function handleOperate(opParams: IOperateParams, opts: { internal: TInter // 来自实例的定制读行为,目前主要是响应式对象会有此操作, // 因为多个实例共享了一个响应式对象,但需要有自己的读行为操作来为实例本身收集依赖 // 注:全局响应式对象的读行为已将 currentOnRead 置空 + if (mutateCtx.onRead) { + // 来自实例 reactive 透传的 onRead + mutateCtx.onRead(opParams); + } if (currReactive.onRead) { + // 来自顶层 reactive 透传的 onRead currReactive.onRead(opParams); } else { getRunningFn().fnCtx && recordFnDepKeys([depKey], { sharedKey }); diff --git a/packages/helux-core/src/factory/creator/reactive.ts b/packages/helux-core/src/factory/creator/reactive.ts index 4302a012..081e79f1 100644 --- a/packages/helux-core/src/factory/creator/reactive.ts +++ b/packages/helux-core/src/factory/creator/reactive.ts @@ -1,4 +1,4 @@ -import { canUseDeep } from '@helux/utils'; +import { canUseDeep, delListItem, nodupPush, safeMapGet } from '@helux/utils'; import { FROM, IS_ATOM, REACTIVE_META_KEY, SHARED_KEY } from '../../consts'; import { getSharedKey } from '../../helpers/state'; import type { Dict } from '../../types/base'; @@ -11,8 +11,12 @@ import { REACTIVE_DESC, REACTIVE_META, TRIGGERED_WATCH } from './current'; const { REACTIVE } = FROM; -/** key: sharedKey, value: reactive meta object */ +/** key: sharedKey, value: top reactive meta */ const metas: Map = new Map(); +/** key: insKey, value: ins reactive meta */ +const insMetas: Map = new Map(); +/** key: sharedKey, value: ins metaKeys */ +const skey2insKeys: Map = new Map(); function canFlush(meta?: IReactiveMeta): meta is IReactiveMeta { return !!(meta && !meta.expired && meta.modified); @@ -29,9 +33,24 @@ function flushModified(meta: IReactiveMeta) { // 来自于 flush 记录的 desc 值,使用过一次就清除 const desc = REACTIVE_DESC.current(sharedKey); REACTIVE_DESC.del(sharedKey); + + // 所有 ins meta 均移除,再次获取时会重建 + const insKeys = skey2insKeys.get(sharedKey); + if (insKeys) { + insKeys.forEach((insKey) => insMetas.delete(insKey)); + insKeys.length = 0; + } return meta.finish(null, { desc }); } +export function clearInsMeta(sharedKey: number, insKey: number) { + const insKeys = skey2insKeys.get(sharedKey); + if (insKeys) { + delListItem(insKeys, insKey); + insMetas.delete(insKey); + } +} + /** * 记录修改描述,让 devtool 可观测到类似 Api_mutate@Reactive/changeA 的描述 */ @@ -91,44 +110,63 @@ export function nextTickFlush(sharedKey: number, desc?: string) { meta.nextTickFlush(desc); } +function buildMeta(internal: TInternal, options: IBuildReactiveOpts) { + const { from = REACTIVE, onRead } = options; + const { finish, draftRoot } = internal.setStateFactory({ isReactive: true, from, handleCbReturn: false, enableDep: true, onRead }); + const latestMeta = newReactiveMeta(draftRoot, options, finish); + latestMeta.key = getReactiveKey(); + latestMeta.nextTickFlush = (desc?: string) => { + const { expired, hasFlushTask } = latestMeta; + if (!expired) { + latestMeta.data = [desc]; + } + if (!hasFlushTask) { + latestMeta.hasFlushTask = true; + // push flush cb to micro task + Promise.resolve().then(() => { + const [desc] = latestMeta.data; + innerFlush(internal.sharedKey, desc); + }); + } + }; + return latestMeta; +} + /** * 全局独立使用或实例使用都共享同一个响应对象 */ function getReactiveInfo(internal: TInternal, options: IBuildReactiveOpts, forAtom: boolean) { const { sharedKey } = internal; - let meta = metas.get(sharedKey); - // 无响应对象、或响应对象已过期 - if (!meta || meta.expired) { - const { from = REACTIVE } = options; - const { finish, draftRoot } = internal.setStateFactory({ isReactive: true, from, handleCbReturn: false, enableDep: true }); - const latestMeta = newReactiveMeta(draftRoot, options, finish); - latestMeta.key = getReactiveKey(); - latestMeta.nextTickFlush = (desc?: string) => { - const { expired, hasFlushTask } = latestMeta; - if (!expired) { - latestMeta.data = [desc]; - } - if (!hasFlushTask) { - latestMeta.hasFlushTask = true; - // push flush cb to micro task - Promise.resolve().then(() => { - const [desc] = latestMeta.data; - innerFlush(sharedKey, desc); - }); - } - }; - meta = latestMeta; - metas.set(sharedKey, meta); - // 动态生成的映射关系会在 flushModified 里被删除 - REACTIVE_META.set(meta.key, meta); + const { insKey = 0 } = options; + let topMeta = metas.get(sharedKey) || fakeReativeMeta; + let targetMeta = topMeta; + + // 无顶层响应对象、或顶层响应对象已过期,则重建顶层 top reactive + if (topMeta.expired) { + topMeta = buildMeta(internal, { isTop: true }); + metas.set(sharedKey, topMeta); + REACTIVE_META.set(topMeta.key, topMeta); + // mark using + REACTIVE_META.markUsing(topMeta.key); + topMeta.fnKey = TRIGGERED_WATCH.current(); + targetMeta = topMeta; } - // mark using - REACTIVE_META.markUsing(meta.key); - meta.fnKey = TRIGGERED_WATCH.current(); + // 当前reactive操作来自于实例 + if (insKey) { + targetMeta = insMetas.get(insKey) || fakeReativeMeta; + if (targetMeta.expired) { + targetMeta = buildMeta(internal, options); + // 动态生成的映射关系会在 flushModified 里被删除 + // 或组件卸载时主动删除 + insMetas.set(insKey, targetMeta); + const insKeys = safeMapGet(skey2insKeys, sharedKey, []); + nodupPush(insKeys, insKey); + } + } - const { draft } = meta; - return { val: forAtom ? draft.val : draft, meta }; + const { draft } = targetMeta; + return { val: forAtom ? draft.val : draft, meta: targetMeta }; } /** diff --git a/packages/helux-core/src/helpers/insCtx.ts b/packages/helux-core/src/helpers/insCtx.ts index e3dee609..1c6afeb1 100644 --- a/packages/helux-core/src/helpers/insCtx.ts +++ b/packages/helux-core/src/helpers/insCtx.ts @@ -61,7 +61,7 @@ export function runInsUpdater(insCtx: InsCtxDef | undefined) { * 为实例创建代理对象并追加到 insCtx 上 */ export function attachInsProxyState(insCtx: InsCtxDef) { - const { internal, isReactive } = insCtx; + const { internal, isReactive, insKey } = insCtx; const { rawState, isDeep, sharedKey, onRead, forAtom } = internal; if (isDeep) { const onOperate: OnOperate = (opParams) => { @@ -80,7 +80,7 @@ export function attachInsProxyState(insCtx: InsCtxDef) { if (isReactive) { // 组件实例使用 useReactive(state) 返回的 reactive 对象时,会在 creator/operateState 里操作实例自己的 onOperate 句柄 - const { draft, draftRoot } = buildReactive(internal, { onRead: onOperate }); + const { draft, draftRoot } = buildReactive(internal, { onRead: onOperate, insKey }); insCtx.proxyState = draftRoot; insCtx.proxyStateVal = draft; } else { @@ -150,6 +150,7 @@ export function buildInsCtx(options: Ext): InsCtxDef { internal, rawState, sharedState, + sharedKey, proxyState: {}, proxyStateVal: {}, updater, diff --git a/packages/helux-core/src/hooks/common/shared.ts b/packages/helux-core/src/hooks/common/shared.ts index f55261a7..2e203435 100644 --- a/packages/helux-core/src/hooks/common/shared.ts +++ b/packages/helux-core/src/hooks/common/shared.ts @@ -3,6 +3,7 @@ import { isAtom } from '../../factory/common/atom'; import type { InsCtxDef } from '../../factory/creator/buildInternal'; import { INS_CTX } from '../../factory/creator/current'; import { delGlobalId, mapGlobalId } from '../../factory/creator/globalId'; +import { clearInsMeta } from '../../factory/creator/reactive'; import { attachInsProxyState } from '../../helpers/insCtx'; import { clearDep, recoverDep } from '../../helpers/insDep'; import { getInternal } from '../../helpers/state'; @@ -13,7 +14,7 @@ import type { Dict, Fn, IInsRenderInfo } from '../../types/base'; */ export function prepareTuple(insCtx: InsCtxDef): [any, Fn, IInsRenderInfo] { const { proxyState, internal, renderInfo, canCollect, isReactive } = insCtx; - const { sharedKey, sharedKeyStr, insSetState, forAtom, isPrimitive } = internal; + const { sharedKey, sharedKeyStr, insSetState, forAtom } = internal; renderInfo.snap = internal.snap; renderInfo.time = Date.now(); // atom 自动拆箱,注意这里 proxyState.val 已触发记录根值依赖 @@ -67,10 +68,11 @@ export function recoverInsCtx(insCtx: InsCtxDef) { export function delInsCtx(insCtx: InsCtxDef) { insCtx.mountStatus = UNMOUNT; - const { id, globalId, insKey } = insCtx; + const { id, globalId, insKey, sharedKey } = insCtx; insCtx.internal.delId(id, insKey); delGlobalId(globalId, insKey); clearDep(insCtx); + clearInsMeta(sharedKey, insKey); } /** diff --git a/packages/helux-core/src/types/api.d.ts b/packages/helux-core/src/types/api.d.ts index 00737300..1ff9594e 100644 --- a/packages/helux-core/src/types/api.d.ts +++ b/packages/helux-core/src/types/api.d.ts @@ -1,6 +1,6 @@ /* |------------------------------------------------------------------------------------------------ -| helux-core@3.6.1 +| helux-core@3.6.2 | A state library core that integrates atom, signal, collection dep, derive and watch, | it supports all react like frameworks ( including react 18 ). |------------------------------------------------------------------------------------------------ @@ -63,7 +63,7 @@ import type { WatchOptionsType, } from './base'; -export declare const VER: '3.6.1'; +export declare const VER: '3.6.2'; export declare const LIMU_VER: string; @@ -242,11 +242,11 @@ export function useAtom( sharedState: T, options?: IUseSharedStateOptions, ): [ - T extends ReadOnlyAtom ? AtomValType : T, - // AtomTupleSetState, - SetState, - IInsRenderInfo, - ]; + T extends ReadOnlyAtom ? AtomValType : T, + // AtomTupleSetState, + SetState, + IInsRenderInfo, +]; /** * 区别于 useAtom,useAtomX 返回对象 @@ -260,12 +260,12 @@ export function useReactive( sharedState: T, options?: IUseSharedStateOptions, ): [ - // 针对 atom,第一位 reactive 参数自动拆箱 - T extends Atom ? T['val'] : T, - // 代表 reactiveRoot - T, - IInsRenderInfo - ]; + // 针对 atom,第一位 reactive 参数自动拆箱 + T extends Atom ? T['val'] : T, + // 代表 reactiveRoot + T, + IInsRenderInfo, +]; /** * 更新当前共享状态的所有实例组件,谨慎使用此功能,会触发大面积的更新, diff --git a/packages/helux-core/src/types/base.d.ts b/packages/helux-core/src/types/base.d.ts index 6827a85f..4a057f0c 100644 --- a/packages/helux-core/src/types/base.d.ts +++ b/packages/helux-core/src/types/base.d.ts @@ -491,6 +491,10 @@ export interface IMutateCtx { * 修改描述 */ desc: string; + /** + * useReactive 透传的 onRead,方便为每个实例单独收集依赖 + */ + onRead?: OnOperate; } export interface IInnerSetStateOptions extends ISetStateOptions { @@ -519,6 +523,7 @@ export interface ISetFactoryOpts extends IInnerSetStateOptions { * 同时也减少不必要的运行时分析性能损耗 */ enableDep?: boolean; + onRead?: OnOperate; } /** @@ -1547,6 +1552,7 @@ export interface IInsCtx { sharedState: Dict; proxyState: Dict; proxyStateVal: Dict; + sharedKey: number; rootVal: any; updater: Fn; /** 未挂载 已挂载 已卸载 */ diff --git a/packages/helux-core/src/types/inner.d.ts b/packages/helux-core/src/types/inner.d.ts index 931a45fe..42e89a0c 100644 --- a/packages/helux-core/src/types/inner.d.ts +++ b/packages/helux-core/src/types/inner.d.ts @@ -55,4 +55,8 @@ export interface IReactiveMeta { from: From; desc: string; onRead?: Fn; + /** + * 当前 reactive 对像可能来自于实例 + */ + insKey: number; }