From 8542452932ba3d26c3a23e18f750a7a50a2496f6 Mon Sep 17 00:00:00 2001
From: qun <778157949@qq.com>
Date: Sun, 6 Aug 2023 15:55:46 +0800
Subject: [PATCH 1/3] feat: react lazy
---
demos/suspense-lazy/Cpn.tsx | 3 +
demos/suspense-lazy/index.html | 14 ++++
demos/suspense-lazy/main.tsx | 22 ++++++
packages/react-reconciler/src/beginWork.ts | 17 ++++-
packages/react-reconciler/src/fiber.ts | 12 ++-
packages/react-reconciler/src/workLoop.ts | 21 ++++-
packages/react-reconciler/src/workTags.ts | 7 +-
packages/react/index.ts | 1 +
packages/react/src/lazy.ts | 89 ++++++++++++++++++++++
packages/shared/ReactSymbols.ts | 4 +
10 files changed, 183 insertions(+), 7 deletions(-)
create mode 100644 demos/suspense-lazy/Cpn.tsx
create mode 100644 demos/suspense-lazy/index.html
create mode 100644 demos/suspense-lazy/main.tsx
create mode 100644 packages/react/src/lazy.ts
diff --git a/demos/suspense-lazy/Cpn.tsx b/demos/suspense-lazy/Cpn.tsx
new file mode 100644
index 0000000..7e73c30
--- /dev/null
+++ b/demos/suspense-lazy/Cpn.tsx
@@ -0,0 +1,3 @@
+export default function Cpn() {
+ return
Cpn
;
+}
diff --git a/demos/suspense-lazy/index.html b/demos/suspense-lazy/index.html
new file mode 100644
index 0000000..35449a3
--- /dev/null
+++ b/demos/suspense-lazy/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Suspense
+
+
+
+
+
+
+
diff --git a/demos/suspense-lazy/main.tsx b/demos/suspense-lazy/main.tsx
new file mode 100644
index 0000000..0a3e02a
--- /dev/null
+++ b/demos/suspense-lazy/main.tsx
@@ -0,0 +1,22 @@
+import { Suspense, lazy } from 'react';
+import ReactDOM from 'react-dom/client';
+
+function delay(promise) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(promise);
+ }, 2000);
+ });
+}
+
+const Cpn = lazy(() => import('./Cpn').then((res) => delay(res)));
+
+function App() {
+ return (
+ loading}>
+
+
+ );
+}
+
+ReactDOM.createRoot(document.getElementById('root')).render();
diff --git a/packages/react-reconciler/src/beginWork.ts b/packages/react-reconciler/src/beginWork.ts
index 11a21ce..0860faf 100644
--- a/packages/react-reconciler/src/beginWork.ts
+++ b/packages/react-reconciler/src/beginWork.ts
@@ -18,7 +18,9 @@ import {
HostRoot,
HostText,
OffscreenComponent,
- SuspenseComponent
+ SuspenseComponent,
+ LazyComponent,
+ IndeterminateComponent
} from './workTags';
import {
Ref,
@@ -50,6 +52,8 @@ export const beginWork = (wip: FiberNode, renderLane: Lane) => {
return updateSuspenseComponent(wip);
case OffscreenComponent:
return updateOffscreenComponent(wip);
+ case LazyComponent:
+ return mountLazyComponent(wip, renderLane);
default:
if (__DEV__) {
console.warn('beginWork未实现的类型');
@@ -59,6 +63,17 @@ export const beginWork = (wip: FiberNode, renderLane: Lane) => {
return null;
};
+function mountLazyComponent(wip: FiberNode, renderLane: Lane) {
+ const LazyType = wip.type;
+ const payload = LazyType._payload;
+ const init = LazyType._init;
+ const Component = init(payload);
+ wip.type = Component;
+ wip.tag = FunctionComponent;
+ const child = updateFunctionComponent(wip, renderLane);
+ return child;
+}
+
function updateContextProvider(wip: FiberNode) {
const providerType = wip.type;
const context = providerType._context;
diff --git a/packages/react-reconciler/src/fiber.ts b/packages/react-reconciler/src/fiber.ts
index 2a4ccee..4e24845 100644
--- a/packages/react-reconciler/src/fiber.ts
+++ b/packages/react-reconciler/src/fiber.ts
@@ -6,14 +6,20 @@ import {
HostComponent,
WorkTag,
SuspenseComponent,
- OffscreenComponent
+ OffscreenComponent,
+ LazyComponent,
+ IndeterminateComponent
} from './workTags';
import { Flags, NoFlags } from './fiberFlags';
import { Container } from 'hostConfig';
import { Lane, Lanes, NoLane, NoLanes } from './fiberLanes';
import { Effect } from './fiberHooks';
import { CallbackNode } from 'scheduler';
-import { REACT_PROVIDER_TYPE, REACT_SUSPENSE_TYPE } from 'shared/ReactSymbols';
+import {
+ REACT_PROVIDER_TYPE,
+ REACT_SUSPENSE_TYPE,
+ REACT_LAZY_TYPE
+} from 'shared/ReactSymbols';
export class FiberNode {
type: any;
@@ -152,6 +158,8 @@ export function createFiberFromElement(element: ReactElementType): FiberNode {
fiberTag = ContextProvider;
} else if (type === REACT_SUSPENSE_TYPE) {
fiberTag = SuspenseComponent;
+ } else if (typeof type === 'object' && type.$$typeof === REACT_LAZY_TYPE) {
+ fiberTag = LazyComponent;
} else if (typeof type !== 'function' && __DEV__) {
console.warn('为定义的type类型', element);
}
diff --git a/packages/react-reconciler/src/workLoop.ts b/packages/react-reconciler/src/workLoop.ts
index 00329cc..3447027 100644
--- a/packages/react-reconciler/src/workLoop.ts
+++ b/packages/react-reconciler/src/workLoop.ts
@@ -61,9 +61,16 @@ const RootDidNotComplete = 3;
let workInProgressRootExitStatus: number = RootInProgress;
// Suspense
-type SuspendedReason = typeof NotSuspended | typeof SuspendedOnData;
+type SuspendedReason =
+ | typeof NotSuspended
+ | typeof SuspendedOnError
+ | typeof SuspendedOnData
+ | typeof SuspendedOnDeprecatedThrowPromise;
const NotSuspended = 0;
-const SuspendedOnData = 6;
+const SuspendedOnError = 1;
+const SuspendedOnData = 2;
+const SuspendedOnDeprecatedThrowPromise = 4;
+
let workInProgressSuspendedReason: SuspendedReason = NotSuspended;
let workInProgressThrownValue: any = null;
@@ -416,7 +423,15 @@ function handleThrow(root: FiberRootNode, thrownValue: any): void {
workInProgressSuspendedReason = SuspendedOnData;
thrownValue = getSuspenseThenable();
} else {
- // TODO Error Boundary
+ const isWakeable =
+ thrownValue !== null &&
+ typeof thrownValue === 'object' &&
+ typeof thrownValue.then === 'function';
+
+ workInProgressThrownValue = thrownValue;
+ workInProgressSuspendedReason = isWakeable
+ ? SuspendedOnDeprecatedThrowPromise
+ : SuspendedOnError;
}
workInProgressThrownValue = thrownValue;
}
diff --git a/packages/react-reconciler/src/workTags.ts b/packages/react-reconciler/src/workTags.ts
index 0b42462..269e85f 100644
--- a/packages/react-reconciler/src/workTags.ts
+++ b/packages/react-reconciler/src/workTags.ts
@@ -1,14 +1,17 @@
export type WorkTag =
| typeof FunctionComponent
+ | typeof IndeterminateComponent
| typeof HostRoot
| typeof HostComponent
| typeof HostText
| typeof Fragment
| typeof ContextProvider
| typeof SuspenseComponent
- | typeof OffscreenComponent;
+ | typeof OffscreenComponent
+ | typeof LazyComponent;
export const FunctionComponent = 0;
+export const IndeterminateComponent = 2;
export const HostRoot = 3;
export const HostComponent = 5;
@@ -19,3 +22,5 @@ export const ContextProvider = 8;
export const SuspenseComponent = 13;
export const OffscreenComponent = 14;
+
+export const LazyComponent = 16;
diff --git a/packages/react/index.ts b/packages/react/index.ts
index 8916935..520b904 100644
--- a/packages/react/index.ts
+++ b/packages/react/index.ts
@@ -7,6 +7,7 @@ import {
} from './src/jsx';
export { REACT_FRAGMENT_TYPE as Fragment } from 'shared/ReactSymbols';
export { createContext } from './src/context';
+export { lazy } from './src/lazy';
export { REACT_SUSPENSE_TYPE as Suspense } from 'shared/ReactSymbols';
// React
diff --git a/packages/react/src/lazy.ts b/packages/react/src/lazy.ts
new file mode 100644
index 0000000..7ff33da
--- /dev/null
+++ b/packages/react/src/lazy.ts
@@ -0,0 +1,89 @@
+import { Thenable, Wakeable } from 'shared/ReactTypes';
+import { REACT_LAZY_TYPE } from 'shared/ReactSymbols';
+
+const Uninitialized = -1;
+const Pending = 0;
+const Resolved = 1;
+const Rejected = 2;
+
+type UninitializedPayload = {
+ _status: typeof Uninitialized;
+ _result: () => Thenable<{ default: T }>;
+};
+
+type PendingPayload = {
+ _status: typeof Pending;
+ _result: Wakeable;
+};
+
+type ResolvedPayload = {
+ _status: typeof Resolved;
+ _result: { default: T };
+};
+
+type RejectedPayload = {
+ _status: typeof Rejected;
+ _result: any;
+};
+
+type Payload =
+ | UninitializedPayload
+ | PendingPayload
+ | ResolvedPayload
+ | RejectedPayload;
+
+export type LazyComponent = {
+ $$typeof: symbol | number;
+ _payload: P;
+ _init: (payload: P) => T;
+};
+
+function lazyInitializer(payload: Payload): T {
+ if (payload._status === Uninitialized) {
+ const ctor = payload._result;
+ const thenable = ctor();
+ thenable.then(
+ (moduleObject) => {
+ // @ts-ignore
+ const resolved: ResolvedPayload = payload;
+ resolved._status = Resolved;
+ resolved._result = moduleObject;
+ },
+ (error) => {
+ // @ts-ignore
+ const rejected: RejectedPayload = payload;
+ rejected._status = Rejected;
+ rejected._result = error;
+ }
+ );
+ if (payload._status === Uninitialized) {
+ // @ts-ignore
+ const pending: PendingPayload = payload;
+ pending._status = Pending;
+ pending._result = thenable;
+ }
+ }
+ if (payload._status === Resolved) {
+ const moduleObject = payload._result;
+ return moduleObject.default;
+ } else {
+ throw payload._result;
+ }
+}
+
+export function lazy(
+ ctor: () => Thenable<{ default: T }>
+): LazyComponent> {
+ const payload: Payload = {
+ _status: Uninitialized,
+ _result: ctor
+ };
+
+ const lazyType: LazyComponent> = {
+ $$typeof: REACT_LAZY_TYPE,
+ _payload: payload,
+ _init: lazyInitializer
+ };
+
+ return lazyType;
+}
diff --git a/packages/shared/ReactSymbols.ts b/packages/shared/ReactSymbols.ts
index c469bbc..c92b2a7 100644
--- a/packages/shared/ReactSymbols.ts
+++ b/packages/shared/ReactSymbols.ts
@@ -19,3 +19,7 @@ export const REACT_PROVIDER_TYPE = supportSymbol
export const REACT_SUSPENSE_TYPE = supportSymbol
? Symbol.for('react.suspense')
: 0xead1;
+
+export const REACT_LAZY_TYPE = supportSymbol
+ ? Symbol.for('react.lazy')
+ : 0xead4;
From 0f68b83eec77effd0cce9352fdee1655cc8a6f95 Mon Sep 17 00:00:00 2001
From: qun <778157949@qq.com>
Date: Tue, 8 Aug 2023 23:08:01 +0800
Subject: [PATCH 2/3] feat: react lazy
---
packages/react-reconciler/src/beginWork.ts | 3 +--
packages/react-reconciler/src/fiber.ts | 4 ++--
packages/react-reconciler/src/workTags.ts | 2 --
3 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/packages/react-reconciler/src/beginWork.ts b/packages/react-reconciler/src/beginWork.ts
index 0860faf..216413c 100644
--- a/packages/react-reconciler/src/beginWork.ts
+++ b/packages/react-reconciler/src/beginWork.ts
@@ -19,8 +19,7 @@ import {
HostText,
OffscreenComponent,
SuspenseComponent,
- LazyComponent,
- IndeterminateComponent
+ LazyComponent
} from './workTags';
import {
Ref,
diff --git a/packages/react-reconciler/src/fiber.ts b/packages/react-reconciler/src/fiber.ts
index 4e24845..83c16e9 100644
--- a/packages/react-reconciler/src/fiber.ts
+++ b/packages/react-reconciler/src/fiber.ts
@@ -7,8 +7,7 @@ import {
WorkTag,
SuspenseComponent,
OffscreenComponent,
- LazyComponent,
- IndeterminateComponent
+ LazyComponent
} from './workTags';
import { Flags, NoFlags } from './fiberFlags';
import { Container } from 'hostConfig';
@@ -159,6 +158,7 @@ export function createFiberFromElement(element: ReactElementType): FiberNode {
} else if (type === REACT_SUSPENSE_TYPE) {
fiberTag = SuspenseComponent;
} else if (typeof type === 'object' && type.$$typeof === REACT_LAZY_TYPE) {
+ console.log('element', element);
fiberTag = LazyComponent;
} else if (typeof type !== 'function' && __DEV__) {
console.warn('为定义的type类型', element);
diff --git a/packages/react-reconciler/src/workTags.ts b/packages/react-reconciler/src/workTags.ts
index 269e85f..4b9bf06 100644
--- a/packages/react-reconciler/src/workTags.ts
+++ b/packages/react-reconciler/src/workTags.ts
@@ -1,6 +1,5 @@
export type WorkTag =
| typeof FunctionComponent
- | typeof IndeterminateComponent
| typeof HostRoot
| typeof HostComponent
| typeof HostText
@@ -11,7 +10,6 @@ export type WorkTag =
| typeof LazyComponent;
export const FunctionComponent = 0;
-export const IndeterminateComponent = 2;
export const HostRoot = 3;
export const HostComponent = 5;
From fe29178ac72c35f4ccb89ba934672f1c7790bcba Mon Sep 17 00:00:00 2001
From: qun <68707557+L-Qun@users.noreply.github.com>
Date: Wed, 9 Aug 2023 11:53:29 +0800
Subject: [PATCH 3/3] Update fiber.ts
---
packages/react-reconciler/src/fiber.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/react-reconciler/src/fiber.ts b/packages/react-reconciler/src/fiber.ts
index 83c16e9..c580a59 100644
--- a/packages/react-reconciler/src/fiber.ts
+++ b/packages/react-reconciler/src/fiber.ts
@@ -158,7 +158,6 @@ export function createFiberFromElement(element: ReactElementType): FiberNode {
} else if (type === REACT_SUSPENSE_TYPE) {
fiberTag = SuspenseComponent;
} else if (typeof type === 'object' && type.$$typeof === REACT_LAZY_TYPE) {
- console.log('element', element);
fiberTag = LazyComponent;
} else if (typeof type !== 'function' && __DEV__) {
console.warn('为定义的type类型', element);