!121 refactor: reactive type
Merge pull request !121 from Hoikan/reactive
This commit is contained in:
commit
a0d073704a
|
@ -22,7 +22,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
root: true,
|
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',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
@ -56,7 +56,6 @@ module.exports = {
|
||||||
'comma-dangle': ['error', 'only-multiline'],
|
'comma-dangle': ['error', 'only-multiline'],
|
||||||
|
|
||||||
'no-constant-condition': 'off',
|
'no-constant-condition': 'off',
|
||||||
'no-for-of-loops/no-for-of-loops': 'error',
|
|
||||||
'no-function-declare-after-return/no-function-declare-after-return': 'error',
|
'no-function-declare-after-return/no-function-declare-after-return': 'error',
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { printNode } from './utils/printNode';
|
import { printNode } from './utils/printNode';
|
||||||
|
import { Signal } from './Types';
|
||||||
|
import { isFunction } from './Utils';
|
||||||
|
|
||||||
let runningRNode: RNode<any> | undefined = undefined; // 当前正执行的RNode
|
let runningRNode: RNode<any> | undefined = undefined; // 当前正执行的RNode
|
||||||
let calledGets: RNode<any>[] | null = null;
|
let calledGets: RNode<any>[] | null = null;
|
||||||
|
@ -47,7 +49,7 @@ function defaultEquality(a: any, b: any) {
|
||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RNode<T = any> {
|
export class RNode<T = any> implements Signal<T> {
|
||||||
_value: T;
|
_value: T;
|
||||||
fn?: () => T;
|
fn?: () => T;
|
||||||
|
|
||||||
|
@ -57,7 +59,6 @@ export class RNode<T = any> {
|
||||||
private state: State;
|
private state: State;
|
||||||
isEffect = false;
|
isEffect = false;
|
||||||
|
|
||||||
|
|
||||||
cleanups: ((oldValue: T) => void)[] = [];
|
cleanups: ((oldValue: T) => void)[] = [];
|
||||||
equals = defaultEquality;
|
equals = defaultEquality;
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ export class RNode<T = any> {
|
||||||
|
|
||||||
const prevValue = this.getValue();
|
const prevValue = this.getValue();
|
||||||
|
|
||||||
const value = typeof fnOrValue === 'function' ? fnOrValue(prevValue) : fnOrValue;
|
const value = isFunction(fnOrValue) ? fnOrValue(prevValue) : fnOrValue;
|
||||||
|
|
||||||
this.compare(prevValue, value);
|
this.compare(prevValue, value);
|
||||||
|
|
||||||
|
@ -291,7 +292,6 @@ export class RNode<T = any> {
|
||||||
setValue(value: any) {
|
setValue(value: any) {
|
||||||
this._value = value;
|
this._value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onCleanup<T = any>(fn: (oldValue: T) => void): void {
|
export function onCleanup<T = any>(fn: (oldValue: T) => void): void {
|
||||||
|
@ -324,4 +324,3 @@ export function untrack(fn) {
|
||||||
runningRNode = preRContext;
|
runningRNode = preRContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,12 +43,12 @@ export function setRNodeVal(rNode: RProxyNode<any>, value: unknown): void {
|
||||||
|
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
prevValue = rNode.root!.$;
|
prevValue = rNode.root!.$;
|
||||||
newValue = isFunction<(...prev: any) => any>(value) ? value(prevValue) : value;
|
newValue = isFunction(value) ? value(prevValue) : value;
|
||||||
rNode.root!.$ = newValue;
|
rNode.root!.$ = newValue;
|
||||||
} else {
|
} else {
|
||||||
const parentVal = getRNodeVal(parent!);
|
const parentVal = getRNodeVal(parent!);
|
||||||
prevValue = parentVal[key];
|
prevValue = parentVal[key];
|
||||||
newValue = isFunction<(...prev: any) => any>(value) ? value(prevValue) : value;
|
newValue = isFunction(value) ? value(prevValue) : value;
|
||||||
parentVal[key] = newValue;
|
parentVal[key] = newValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,25 +15,28 @@
|
||||||
|
|
||||||
import { isPrimitive } from './Utils';
|
import { isPrimitive } from './Utils';
|
||||||
import { RNode } from './RNode';
|
import { RNode } from './RNode';
|
||||||
import { ProxyRNode } from './Types';
|
import { Fn, NonFunctionType, Signal } from './Types';
|
||||||
import { RProxyNode } from './RProxyNode';
|
import { Proxy, RProxyNode } from './RProxyNode';
|
||||||
import { getRNodeVal, getRootRNode } from './RNodeAccessor';
|
import { getRNodeVal } from './RNodeAccessor';
|
||||||
|
|
||||||
export type Reactive<T = any> = RNode<T> | Atom<T>;
|
export function createReactive<T extends string>(raw?: T): Signal<string>;
|
||||||
|
export function createReactive<T extends number>(raw?: T): Signal<number>;
|
||||||
export function createReactive<T extends any>(raw?: T): ReactiveProxy<T> {
|
export function createReactive<T extends symbol>(raw?: T): Signal<symbol>;
|
||||||
|
export function createReactive<T extends number | string | symbol>(raw?: T): Signal<T>;
|
||||||
|
export function createReactive<T extends Record<any, any> | Array<any> | symbol>(raw?: T): Proxy<T>;
|
||||||
|
export function createReactive<T extends NonFunctionType>(raw?: T): Proxy<T> | Signal<T> {
|
||||||
if (isPrimitive(raw) || raw === null || raw === undefined) {
|
if (isPrimitive(raw) || raw === null || raw === undefined) {
|
||||||
return new RNode(raw, { isSignal: true });
|
return new RNode(raw, { isSignal: true });
|
||||||
} else {
|
} else {
|
||||||
const node = new RProxyNode(null, {
|
const node = new RProxyNode<T>(null, {
|
||||||
root: { $: raw },
|
root: { $: raw },
|
||||||
});
|
});
|
||||||
return node.proxy as ReactiveProxy<T>;
|
return node.proxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createComputed<T>(fn: T) {
|
export function createComputed<T extends Fn>(fn: T) {
|
||||||
const rNode = new RProxyNode(fn, { isComputed: true });
|
const rNode = new RProxyNode<T>(fn, { isComputed: true });
|
||||||
return rNode.proxy;
|
return rNode.proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +48,7 @@ export function createWatch<T>(fn: T) {
|
||||||
rNode.get();
|
rNode.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrCreateChildProxy(value: unknown, parent: RProxyNode<any>, key: string | symbol): ProxyRNode<any> {
|
export function getOrCreateChildProxy(value: unknown, parent: RProxyNode<any>, key: string | symbol) {
|
||||||
const child = getOrCreateChildRNode(parent, key);
|
const child = getOrCreateChildRNode(parent, key);
|
||||||
|
|
||||||
return child.proxy;
|
return child.proxy;
|
||||||
|
|
|
@ -18,6 +18,23 @@ import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor';
|
||||||
import { preciseCompare } from './comparison/InDepthComparison';
|
import { preciseCompare } from './comparison/InDepthComparison';
|
||||||
import { isObject } from './Utils';
|
import { isObject } from './Utils';
|
||||||
import { RNode, Root, runEffects } from './RNode';
|
import { RNode, Root, runEffects } from './RNode';
|
||||||
|
import { Computation, ProxyRNode, Signal } from './Types';
|
||||||
|
|
||||||
|
export type Proxy<T> = T extends Record<string, unknown>
|
||||||
|
? SignalProxy<T>
|
||||||
|
: T extends () => infer Return
|
||||||
|
? ComputationProxy<Return>
|
||||||
|
: Signal<T>;
|
||||||
|
|
||||||
|
export type SignalProxy<T> = {
|
||||||
|
[Val in keyof T]: SignalProxy<T[Val]>;
|
||||||
|
} & Signal<T>;
|
||||||
|
|
||||||
|
export type ComputationProxy<T> = T extends Record<string, unknown>
|
||||||
|
? {
|
||||||
|
readonly [Val in keyof T]: ComputationProxy<T[Val]>;
|
||||||
|
} & Computation<T>
|
||||||
|
: Computation<T>;
|
||||||
|
|
||||||
export interface RNodeOptions {
|
export interface RNodeOptions {
|
||||||
root?: Root<any> | null;
|
root?: Root<any> | null;
|
||||||
|
@ -39,7 +56,7 @@ export class RProxyNode<T = any> extends RNode<T> {
|
||||||
key: KEY | null;
|
key: KEY | null;
|
||||||
children: Map<KEY, RProxyNode> | null = null;
|
children: Map<KEY, RProxyNode> | null = null;
|
||||||
|
|
||||||
proxy: any = null;
|
proxy: Proxy<T> = null;
|
||||||
|
|
||||||
extend: any; // 用于扩展,放一些自定义属性
|
extend: any; // 用于扩展,放一些自定义属性
|
||||||
|
|
||||||
|
@ -50,7 +67,8 @@ export class RProxyNode<T = any> extends RNode<T> {
|
||||||
|
|
||||||
this.isComputed = options?.isComputed || false;
|
this.isComputed = options?.isComputed || false;
|
||||||
|
|
||||||
this.proxy = createProxy(this);
|
// Proxy type should be optimized
|
||||||
|
this.proxy = createProxy as unknown as Proxy<T>;
|
||||||
this.parent = options?.parent || null;
|
this.parent = options?.parent || null;
|
||||||
this.key = options?.key as KEY;
|
this.key = options?.key as KEY;
|
||||||
this.root = options?.root || {};
|
this.root = options?.root || {};
|
||||||
|
|
|
@ -93,3 +93,25 @@ export type RNode<T = any> = RootRNode<T> | ChildrenRNode<T>;
|
||||||
|
|
||||||
export type Reactive<T = any> = RNode<T> | Atom<T>;
|
export type Reactive<T = any> = RNode<T> | Atom<T>;
|
||||||
|
|
||||||
|
export type Signal<T> = {
|
||||||
|
/**
|
||||||
|
* 返回响应式对象的值,自动追踪依赖
|
||||||
|
*/
|
||||||
|
get(): T;
|
||||||
|
/**
|
||||||
|
* 返回响应式对象的值,不追踪依赖
|
||||||
|
*/
|
||||||
|
read(): T;
|
||||||
|
set(value: T): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Computation<T> = {
|
||||||
|
get(): T;
|
||||||
|
read(): T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NonFunctionType<T = any> = Exclude<T, Fn>;
|
||||||
|
|
||||||
|
// Use Fn instead of native Function for tslint
|
||||||
|
export type Fn = (...args: any[]) => any;
|
||||||
|
export type NoArgFn = () => any;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
import { RNode } from './RNode';
|
import { RNode } from './RNode';
|
||||||
import { RProxyNode } from './RProxyNode';
|
import { RProxyNode } from './RProxyNode';
|
||||||
|
import { Fn } from './Types';
|
||||||
|
|
||||||
export function isReactiveObj(obj: any) {
|
export function isReactiveObj(obj: any) {
|
||||||
return obj instanceof RNode || obj instanceof RProxyNode;
|
return obj instanceof RNode || obj instanceof RProxyNode;
|
||||||
|
@ -30,7 +31,7 @@ export function isPrimitive(obj: unknown): boolean {
|
||||||
return obj != null && type !== 'object' && type !== 'function';
|
return obj != null && type !== 'object' && type !== 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFunction<T extends (...prev: any) => any>(obj: unknown): obj is Function {
|
export function isFunction(obj: unknown): obj is Fn {
|
||||||
return typeof obj === 'function';
|
return typeof obj === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { getOrCreateChildProxy } from '../RNodeCreator';
|
||||||
import { getRNodeVal } from '../RNodeAccessor';
|
import { getRNodeVal } from '../RNodeAccessor';
|
||||||
import { isArray } from '../Utils';
|
import { isArray } from '../Utils';
|
||||||
import { RNode } from '../RNode';
|
import { RNode } from '../RNode';
|
||||||
import {RProxyNode} from "../RProxyNode";
|
import {Proxy, RProxyNode} from '../RProxyNode';
|
||||||
|
|
||||||
const GET = 'get';
|
const GET = 'get';
|
||||||
const SET = 'set';
|
const SET = 'set';
|
||||||
|
@ -44,7 +44,7 @@ const MODIFY_ARR_FNS = new Set<string | symbol>([
|
||||||
// 数组的遍历方法
|
// 数组的遍历方法
|
||||||
const LOOP_ARR_FNS = new Set<string | symbol>(['forEach', 'map', 'every', 'some', 'filter', 'join']);
|
const LOOP_ARR_FNS = new Set<string | symbol>(['forEach', 'map', 'every', 'some', 'filter', 'join']);
|
||||||
|
|
||||||
export function createProxy<T extends any>(proxyNode: RNode) {
|
export function createProxy<T extends RProxyNode>(proxyNode: T): Proxy<T> {
|
||||||
return new Proxy(proxyNode, {
|
return new Proxy(proxyNode, {
|
||||||
get,
|
get,
|
||||||
set,
|
set,
|
||||||
|
|
Loading…
Reference in New Issue