Match-id-e03cd1c3dfea5cf3911509808c3bea7d0a42cb68

This commit is contained in:
* 2021-12-23 09:07:30 +08:00 committed by *
parent 8847bf3cb2
commit e9be87bb80
13 changed files with 609 additions and 0 deletions

View File

@ -0,0 +1,25 @@
/**
* Component的api setState和forceUpdate在实例生成阶段实现
*/
class Component {
props;
context;
constructor(props, context) {
this.props = props;
this.context = context;
}
}
// 兼容三方件 react-lifecycles-compat它会读取 isReactComponent 属性值,不添加会导致 eview-ui 官网白屏
Component.prototype.isReactComponent = true;
/**
* PureComponent
*/
class PureComponent extends Component {
constructor(props, context) {
super(props, context);
}
}
export { Component, PureComponent };

View File

@ -0,0 +1,15 @@
import {TYPE_PORTAL} from '../utils/elementType';
import type {PortalType} from '../Types';
export function createPortal(
children: any,
outerDom: any,
key: string = null,
): PortalType {
return {
vtype: TYPE_PORTAL,
key: key == null ? null : '' + key,
children,
outerDom,
};
}

View File

@ -0,0 +1,7 @@
import type {RefType} from '../Types';
export function createRef(): RefType {
return {
current: null,
};
}

View File

@ -0,0 +1,8 @@
import {TYPE_FORWARD_REF} from '../utils/elementType';
export function forwardRef(render: Function) {
return {
vtype: TYPE_FORWARD_REF,
render,
};
}

View File

@ -0,0 +1,66 @@
import type {PromiseType} from '../Types';
import {TYPE_LAZY} from '../utils/elementType';
enum LayStatus {
UnProcessed = 'UnProcessed',
Pending = 'Pending',
Fulfilled = 'Fulfilled',
Rejected = 'Rejected',
}
type LazyContent<T> = {
_status: string,
_value: () => PromiseType<{default: T}> | PromiseType<T> | T | any
};
export type LazyComponent<T, P> = {
vtype: Symbol | number,
_content: P,
_load: (content: P) => T,
};
// lazyContent随着阶段改变_value改变:
// 1. 未初始化 -> promiseCtor: () => promise
// 2. pending -> promise
// 3. fulfilled -> module
// 4. rejected -> error
function lazyLoader<T>(lazyContent: LazyContent<T>): any {
if (lazyContent._status === LayStatus.UnProcessed) {
// 执行动态导入组件import
const promise = lazyContent._value();
lazyContent._status = LayStatus.Pending;
lazyContent._value = promise;
promise.then(
module => {
if (lazyContent._status === LayStatus.Pending) {
const defaultExport = module.default;
lazyContent._status = LayStatus.Fulfilled;
lazyContent._value = defaultExport;
}
},
error => {
if (lazyContent._status === LayStatus.Pending) {
lazyContent._status = LayStatus.Rejected;
lazyContent._value = error;
}
},
);
}
if (lazyContent._status === LayStatus.Fulfilled) {
return lazyContent._value;
} else {
throw lazyContent._value;
}
}
export function lazy<T>(promiseCtor: () => PromiseType<{default: T}>): LazyComponent<T, LazyContent<T>> {
return {
vtype: TYPE_LAZY,
_content: {
_status: LayStatus.UnProcessed,
_value: promiseCtor,
},
_load: lazyLoader,
};
}

View File

@ -0,0 +1,9 @@
import {TYPE_MEMO} from '../utils/elementType';
export function memo<Props>(type, compare?: (oldProps: Props, newProps: Props) => boolean) {
return {
vtype: TYPE_MEMO,
type: type,
compare: compare === undefined ? null : compare,
};
}

View File

@ -0,0 +1,101 @@
import type {VNode} from '../../Types';
import {
setOldContextCtx,
setContextChangeCtx,
getOldContextCtx,
resetOldContextCtx,
resetContextChangeCtx,
setOldPreviousContextCtx,
getOldPreviousContextCtx,
setVNodeOldContext,
getVNodeOldContext,
setVNodeOldPreviousContext,
getVNodeOldPreviousContext,
} from '../../ContextSaver';
const emptyObject = {};
// 判断是否是过时的context的提供者
export function isOldProvider(comp: Function): boolean {
// @ts-ignore
const childContextTypes = comp.childContextTypes;
return childContextTypes !== null && childContextTypes !== undefined;
}
// 判断是否是过时的context的消费者
export function isOldConsumer(comp: Function): boolean {
// @ts-ignore
const contextTypes = comp.contextTypes;
return contextTypes !== null && contextTypes !== undefined;
}
// 如果是旧版context提供者则缓存两个全局变量上一个提供者提供的context和当前提供者提供的context
export function cacheOldCtx(processing: VNode, hasOldContext: any): void {
// 每一个context提供者都会更新ctxOldContext
if (hasOldContext) {
setOldPreviousContextCtx(getOldContextCtx());
const vNodeContext = getVNodeOldContext(processing) || emptyObject;
setOldContextCtx(processing, vNodeContext);
}
}
// 获取当前组件可以消费的context
export function getOldContext(processing: VNode, clazz: Function, ifProvider: boolean) {
const type = processing.type;
// 不是context消费者 则直接返回空对象
if (!isOldConsumer(type)) {
return emptyObject;
}
// 当组件既是提供者也是消费者时取上一个context不能直接取最新context因为已经被更新为当前组件的context
// 当组件只是消费者时则取最新context
const parentContext = ((ifProvider && isOldProvider(clazz))) ?
getOldPreviousContextCtx() :
getOldContextCtx();
// 除非父级context更改否则不需要重新创建子context直接取对应节点上存的。
if (getVNodeOldPreviousContext(processing) === parentContext) {
return getVNodeOldContext(processing);
}
// 从父的context中取出子定义的context
const context = {};
for (const key in type.contextTypes) {
context[key] = parentContext[key];
}
// 缓存当前组件的context最近祖先传递下来context当前可消费的context
setVNodeOldPreviousContext(processing, parentContext);
setVNodeOldContext(processing, context);
return context;
}
// 重置context
export function resetOldCtx(vNode: VNode): void {
resetOldContextCtx(vNode);
resetContextChangeCtx(vNode);
}
// 当前组件是提供者则需要合并祖先context和当前组件提供的context
function handleContext(vNode: VNode, parentContext: Object): Object {
const instance = vNode.realNode;
if (typeof instance.getChildContext !== 'function') {
return parentContext;
}
// 合并祖先提供的context和当前组件提供的context
return {...parentContext, ...instance.getChildContext()};
}
// 当前组件是context提供者更新时需要合并祖先context和当前组件提供的context
export function updateOldContext(vNode: VNode): void {
const ctx = handleContext(vNode, getOldPreviousContextCtx());
// 更新context给子组件用的context
setOldContextCtx(vNode, ctx);
// 标记更改
setContextChangeCtx(vNode, true);
}

View File

@ -0,0 +1,31 @@
import type {VNode, ContextType} from '../../Types';
import {getHookStage} from '../../hooks/HookStage';
import {throwNotInFuncError} from '../../hooks/BaseHook';
// 重置依赖
export function resetDepContexts(vNode: VNode): void {
vNode.depContexts = [];
}
// 收集依赖
function collectDeps<T>(vNode: VNode, context: ContextType<T>) {
const depContexts = vNode.depContexts;
if (!depContexts.length) {
vNode.isDepContextChange = false;
}
if (!depContexts.includes(context)) {
depContexts.push(context);
}
}
export function getNewContext<T>(vNode: VNode, ctx: ContextType<T>, isUseContext: boolean = false): T {
// 如果来自于useContext则需要在函数组件中调用
if (isUseContext && getHookStage() === null) {
throwNotInFuncError();
}
// 调用到这个方法说明当前vNode依赖了这个context所以需要收集起来
collectDeps(vNode, ctx);
return ctx.value;
}

View File

@ -0,0 +1,20 @@
import type {ContextType} from '../../Types';
import {TYPE_PROVIDER, TYPE_CONTEXT} from '../../utils/elementType';
export function createContext<T>(val: T): ContextType<T> {
const context: ContextType<T> = {
vtype: TYPE_CONTEXT,
value: val,
Provider: null,
Consumer: null,
};
context.Provider = {
vtype: TYPE_PROVIDER,
_context: context,
};
context.Consumer = context;
return context;
}

View File

@ -0,0 +1,66 @@
/**
*
*/
export let now;
if (typeof performance === 'object' && typeof performance.now === 'function') {
const localPerformance = performance;
now = () => localPerformance.now();
} else {
const localDate = Date;
const initialTime = localDate.now();
now = () => localDate.now() - initialTime;
}
let isMessageLoopRunning = false;
let browserCallback = null;
// 默认每次只运行5ms
const runTime = 5;
let deadline = 0;
export function isOverTime() {
return now() >= deadline;
}
// 1、设置deadline2、回调TaskExecutor传过来的browserCallback
const callRenderTasks = () => {
if (browserCallback == null) {
return;
}
const currentTime = now();
// 计算deadline
deadline = currentTime + runTime;
try {
// 执行callback
const hasMoreTask = browserCallback(
currentTime,
);
if (!hasMoreTask) { // 没有更多task
isMessageLoopRunning = false;
browserCallback = null;
} else {
// 还有task继续调用
port.postMessage(null);
}
} catch (error) {
port.postMessage(null);
throw error;
}
};
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = callRenderTasks;
export function requestBrowserCallback(callback) {
browserCallback = callback;
if (!isMessageLoopRunning) {
isMessageLoopRunning = true;
port.postMessage(null);
}
}

View File

@ -0,0 +1,68 @@
/**
* TaskExecutor的异步任务renderQueue来执行同步的渲染callback
*/
import {runAsync, cancelTask, ImmediatePriority} from './TaskExecutor';
type RenderCallback = () => RenderCallback | null;
let renderQueue: Array<RenderCallback> | null = null;
// 保存正在等待的异步Task可以用于取消
let callingQueueTask: any | null = null;
// 防止重入
let isCallingRenderQueue = false;
export function pushRenderCallback(callback: RenderCallback) {
if (renderQueue === null) {
renderQueue = [callback];
// 高优先级的异步调度
callingQueueTask = runAsync(callRenderQueue, ImmediatePriority);
} else {
// 不需要调度在syncQueue创建的时候已经调度了
renderQueue.push(callback);
}
// 返回一个空对象用于区别null
return {};
}
export function callRenderQueueImmediate() {
if (callingQueueTask !== null) {
// 取消异步调度
cancelTask(callingQueueTask);
callingQueueTask = null;
}
callRenderQueue();
}
// 执行render回调
function callRenderQueue() {
if (!isCallingRenderQueue && renderQueue !== null) {
// 防止重入
isCallingRenderQueue = true;
let i = 0;
try {
for (; i < renderQueue.length; i++) {
let callback = renderQueue[i];
do {
callback = callback();
} while (callback !== null);
}
renderQueue = null;
} catch (error) {
// 如果有异常抛出,请将剩余的回调留在队列中
if (renderQueue !== null) {
renderQueue = renderQueue.slice(i + 1);
}
// 在下一个异步中再调用
runAsync(callRenderQueueImmediate, ImmediatePriority);
throw error;
} finally {
isCallingRenderQueue = false;
}
}
}

View File

@ -0,0 +1,135 @@
/**
*
*/
import {
requestBrowserCallback,
isOverTime,
now,
} from './BrowserAsync';
import {add, shift, first} from './TaskQueue';
const ImmediatePriority = 1;
const NormalPriority = 10;
// 用于控制插入任务的顺序
let idCounter = 1;
let currentPriorityLevel = NormalPriority;
// 正在执行task
let isProcessing = false;
// 调度中,等待浏览器回调
let isScheduling = false;
function runSync(callback, priorityLevel = NormalPriority) {
const previousPriorityLevel = currentPriorityLevel;
currentPriorityLevel = priorityLevel;
try {
return callback();
} finally {
currentPriorityLevel = previousPriorityLevel;
}
}
function runAsync(callback, priorityLevel= NormalPriority ) {
let timeout;
switch (priorityLevel) {
case ImmediatePriority:
timeout = -1;
break;
case NormalPriority:
default:
timeout = 5000;
break;
}
const task = {
id: idCounter++,
callback,
priorityLevel,
expirationTime: now() + timeout,
};
add(task);
if (!isScheduling && !isProcessing) {
isScheduling = true;
requestBrowserCallback(callTasks);
}
return task;
}
function callTasks(initialTime) {
isScheduling = false;
isProcessing = true;
let task = null;
const previousPriorityLevel = currentPriorityLevel;
try {
let currentTime = initialTime;
task = first();
// 循环执行task
while (task !== null) {
if (
task.expirationTime > currentTime &&
isOverTime()
) {
// 没到deadline
break;
}
const callback = task.callback;
if (typeof callback === 'function') {
task.callback = null;
currentPriorityLevel = task.priorityLevel;
const didUserCallbackTimeout = task.expirationTime <= currentTime;
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = now();
// 执行callback返回函数重置callback
if (typeof continuationCallback === 'function') {
task.callback = continuationCallback;
} else {
if (task === first()) {
shift();
}
}
} else {
shift();
}
task = first();
}
// 返回是否还有任务,如果有,说明是被中断了
return task !== null;
} finally {
task = null;
currentPriorityLevel = previousPriorityLevel;
isProcessing = false;
}
}
function cancelTask(task) {
task.callback = null;
}
function getCurrentPriorityLevel() {
return currentPriorityLevel;
}
export {
ImmediatePriority,
NormalPriority,
runSync,
runAsync,
cancelTask,
getCurrentPriorityLevel,
now,
};

View File

@ -0,0 +1,58 @@
/**
*
*/
type Queue = Array<Node>;
type Node = {
id: number;
expirationTime: number;
};
// 任务队列
const taskQueue: Queue = [];
export function add(node: Node): void {
// 查找第一个大于等于 value 的下标,都比 value 小则返回 -1
const idx = getBiggerIdx(node);
if (idx === 0) {
taskQueue.unshift(node);
} else if (idx === -1) {
taskQueue.push(node);
} else {
taskQueue.splice(idx, 0, node);
}
}
// 二分法查找第一个大于等于 value 的下标,都比 value 小则返回 -1时间复杂度O(logn)
function getBiggerIdx(node: Node) {
let left = 0;
let right = taskQueue.length - 1;
while (left <= right) {
const middle = left + ((right - left) >> 1);
if (compare(taskQueue[middle], node) > 0)
right = middle - 1;
else
left = middle + 1;
}
return (left < taskQueue.length) ? left : -1;
}
export function first(): Node | null {
const val = taskQueue[0];
return val !== undefined ? val : null;
}
export function shift(): Node | null {
const val = taskQueue.shift();
return val !== undefined ? val : null;
}
function compare(a: Node, b: Node) {
// 优先先用index排序其次用id
const diff = a.expirationTime - b.expirationTime;
return diff !== 0 ? diff : a.id - b.id;
}