From 2556d53607689e6f182de5c1cc626a907cf39f9e Mon Sep 17 00:00:00 2001 From: haiqin Date: Thu, 25 Jan 2024 16:04:16 +0800 Subject: [PATCH] refactor: reactive type --- .eslintrc.js | 3 +-- packages/inula-reactive/src/RNode.ts | 9 +++---- packages/inula-reactive/src/RNodeAccessor.ts | 4 +-- packages/inula-reactive/src/RNodeCreator.ts | 25 +++++++++++-------- packages/inula-reactive/src/RProxyNode.ts | 22 ++++++++++++++-- packages/inula-reactive/src/Types.ts | 22 ++++++++++++++++ packages/inula-reactive/src/Utils.ts | 3 ++- .../inula-reactive/src/proxy/RProxyHandler.ts | 4 +-- 8 files changed, 67 insertions(+), 25 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 61851c83..5e4838ae 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,7 +22,7 @@ module.exports = { ], root: true, - plugins: ['jest', 'no-for-of-loops', 'no-function-declare-after-return', 'react', '@typescript-eslint'], + plugins: ['jest', 'no-function-declare-after-return', 'react', '@typescript-eslint'], parser: '@typescript-eslint/parser', parserOptions: { @@ -56,7 +56,6 @@ module.exports = { 'comma-dangle': ['error', 'only-multiline'], 'no-constant-condition': 'off', - 'no-for-of-loops/no-for-of-loops': 'error', 'no-function-declare-after-return/no-function-declare-after-return': 'error', }, globals: { diff --git a/packages/inula-reactive/src/RNode.ts b/packages/inula-reactive/src/RNode.ts index a25677d3..a6ffe307 100644 --- a/packages/inula-reactive/src/RNode.ts +++ b/packages/inula-reactive/src/RNode.ts @@ -14,6 +14,8 @@ */ import { printNode } from './utils/printNode'; +import { Signal } from './Types'; +import { isFunction } from './Utils'; let runningRNode: RNode | undefined = undefined; // 当前正执行的RNode let calledGets: RNode[] | null = null; @@ -47,7 +49,7 @@ function defaultEquality(a: any, b: any) { return a === b; } -export class RNode { +export class RNode implements Signal { _value: T; fn?: () => T; @@ -57,7 +59,6 @@ export class RNode { private state: State; isEffect = false; - cleanups: ((oldValue: T) => void)[] = []; equals = defaultEquality; @@ -128,7 +129,7 @@ export class RNode { const prevValue = this.getValue(); - const value = typeof fnOrValue === 'function' ? fnOrValue(prevValue) : fnOrValue; + const value = isFunction(fnOrValue) ? fnOrValue(prevValue) : fnOrValue; this.compare(prevValue, value); @@ -291,7 +292,6 @@ export class RNode { setValue(value: any) { this._value = value; } - } export function onCleanup(fn: (oldValue: T) => void): void { @@ -324,4 +324,3 @@ export function untrack(fn) { runningRNode = preRContext; } } - diff --git a/packages/inula-reactive/src/RNodeAccessor.ts b/packages/inula-reactive/src/RNodeAccessor.ts index 4d062642..bd2662f5 100644 --- a/packages/inula-reactive/src/RNodeAccessor.ts +++ b/packages/inula-reactive/src/RNodeAccessor.ts @@ -43,12 +43,12 @@ export function setRNodeVal(rNode: RProxyNode, value: unknown): void { if (isRoot) { prevValue = rNode.root!.$; - newValue = isFunction<(...prev: any) => any>(value) ? value(prevValue) : value; + newValue = isFunction(value) ? value(prevValue) : value; rNode.root!.$ = newValue; } else { const parentVal = getRNodeVal(parent!); prevValue = parentVal[key]; - newValue = isFunction<(...prev: any) => any>(value) ? value(prevValue) : value; + newValue = isFunction(value) ? value(prevValue) : value; parentVal[key] = newValue; } } diff --git a/packages/inula-reactive/src/RNodeCreator.ts b/packages/inula-reactive/src/RNodeCreator.ts index 5beb4ded..cfefecb5 100644 --- a/packages/inula-reactive/src/RNodeCreator.ts +++ b/packages/inula-reactive/src/RNodeCreator.ts @@ -15,25 +15,28 @@ import { isPrimitive } from './Utils'; import { RNode } from './RNode'; -import { ProxyRNode } from './Types'; -import { RProxyNode } from './RProxyNode'; -import { getRNodeVal, getRootRNode } from './RNodeAccessor'; +import { Fn, NonFunctionType, Signal } from './Types'; +import { Proxy, RProxyNode } from './RProxyNode'; +import { getRNodeVal } from './RNodeAccessor'; -export type Reactive = RNode | Atom; - -export function createReactive(raw?: T): ReactiveProxy { +export function createReactive(raw?: T): Signal; +export function createReactive(raw?: T): Signal; +export function createReactive(raw?: T): Signal; +export function createReactive(raw?: T): Signal; +export function createReactive | Array | symbol>(raw?: T): Proxy; +export function createReactive(raw?: T): Proxy | Signal { if (isPrimitive(raw) || raw === null || raw === undefined) { return new RNode(raw, { isSignal: true }); } else { - const node = new RProxyNode(null, { + const node = new RProxyNode(null, { root: { $: raw }, }); - return node.proxy as ReactiveProxy; + return node.proxy; } } -export function createComputed(fn: T) { - const rNode = new RProxyNode(fn, { isComputed: true }); +export function createComputed(fn: T) { + const rNode = new RProxyNode(fn, { isComputed: true }); return rNode.proxy; } @@ -45,7 +48,7 @@ export function createWatch(fn: T) { rNode.get(); } -export function getOrCreateChildProxy(value: unknown, parent: RProxyNode, key: string | symbol): ProxyRNode { +export function getOrCreateChildProxy(value: unknown, parent: RProxyNode, key: string | symbol) { const child = getOrCreateChildRNode(parent, key); return child.proxy; diff --git a/packages/inula-reactive/src/RProxyNode.ts b/packages/inula-reactive/src/RProxyNode.ts index 0cca9941..2157b6ab 100644 --- a/packages/inula-reactive/src/RProxyNode.ts +++ b/packages/inula-reactive/src/RProxyNode.ts @@ -18,6 +18,23 @@ import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor'; import { preciseCompare } from './comparison/InDepthComparison'; import { isObject } from './Utils'; import { RNode, Root, runEffects } from './RNode'; +import { Computation, ProxyRNode, Signal } from './Types'; + +export type Proxy = T extends Record + ? SignalProxy + : T extends () => infer Return + ? ComputationProxy + : Signal; + +export type SignalProxy = { + [Val in keyof T]: SignalProxy; +} & Signal; + +export type ComputationProxy = T extends Record + ? { + readonly [Val in keyof T]: ComputationProxy; + } & Computation + : Computation; export interface RNodeOptions { root?: Root | null; @@ -39,7 +56,7 @@ export class RProxyNode extends RNode { key: KEY | null; children: Map | null = null; - proxy: any = null; + proxy: Proxy = null; extend: any; // 用于扩展,放一些自定义属性 @@ -50,7 +67,8 @@ export class RProxyNode extends RNode { this.isComputed = options?.isComputed || false; - this.proxy = createProxy(this); + // Proxy type should be optimized + this.proxy = createProxy as unknown as Proxy; this.parent = options?.parent || null; this.key = options?.key as KEY; this.root = options?.root || {}; diff --git a/packages/inula-reactive/src/Types.ts b/packages/inula-reactive/src/Types.ts index c6cc9a11..dd448a35 100644 --- a/packages/inula-reactive/src/Types.ts +++ b/packages/inula-reactive/src/Types.ts @@ -93,3 +93,25 @@ export type RNode = RootRNode | ChildrenRNode; export type Reactive = RNode | Atom; +export type Signal = { + /** + * 返回响应式对象的值,自动追踪依赖 + */ + get(): T; + /** + * 返回响应式对象的值,不追踪依赖 + */ + read(): T; + set(value: T): void; +}; + +export type Computation = { + get(): T; + read(): T; +}; + +export type NonFunctionType = Exclude; + +// Use Fn instead of native Function for tslint +export type Fn = (...args: any[]) => any; +export type NoArgFn = () => any; diff --git a/packages/inula-reactive/src/Utils.ts b/packages/inula-reactive/src/Utils.ts index b2607c81..ff09dec5 100644 --- a/packages/inula-reactive/src/Utils.ts +++ b/packages/inula-reactive/src/Utils.ts @@ -15,6 +15,7 @@ import { RNode } from './RNode'; import { RProxyNode } from './RProxyNode'; +import { Fn } from './Types'; export function isReactiveObj(obj: any) { return obj instanceof RNode || obj instanceof RProxyNode; @@ -30,7 +31,7 @@ export function isPrimitive(obj: unknown): boolean { return obj != null && type !== 'object' && type !== 'function'; } -export function isFunction any>(obj: unknown): obj is Function { +export function isFunction(obj: unknown): obj is Fn { return typeof obj === 'function'; } diff --git a/packages/inula-reactive/src/proxy/RProxyHandler.ts b/packages/inula-reactive/src/proxy/RProxyHandler.ts index 9771fcad..48a20bcd 100644 --- a/packages/inula-reactive/src/proxy/RProxyHandler.ts +++ b/packages/inula-reactive/src/proxy/RProxyHandler.ts @@ -17,7 +17,7 @@ import { getOrCreateChildProxy } from '../RNodeCreator'; import { getRNodeVal } from '../RNodeAccessor'; import { isArray } from '../Utils'; import { RNode } from '../RNode'; -import {RProxyNode} from "../RProxyNode"; +import {Proxy, RProxyNode} from '../RProxyNode'; const GET = 'get'; const SET = 'set'; @@ -44,7 +44,7 @@ const MODIFY_ARR_FNS = new Set([ // 数组的遍历方法 const LOOP_ARR_FNS = new Set(['forEach', 'map', 'every', 'some', 'filter', 'join']); -export function createProxy(proxyNode: RNode) { +export function createProxy(proxyNode: T): Proxy { return new Proxy(proxyNode, { get, set,