Match-id-7d89b46de7de3e8d1600a3be62dfef976edfa78a
This commit is contained in:
commit
8b9bf9383f
|
@ -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 };
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import type {RefType} from '../Types';
|
||||
|
||||
export function createRef(): RefType {
|
||||
return {
|
||||
current: null,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import {TYPE_FORWARD_REF} from '../utils/elementType';
|
||||
|
||||
export function forwardRef(render: Function) {
|
||||
return {
|
||||
vtype: TYPE_FORWARD_REF,
|
||||
render,
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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、设置deadline;2、回调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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue