antd message.info弹出消息源码阅读

290 阅读1分钟

antd的message能通过方法调用,底层是如何实现的呢?

概览

  1. 导出支持调用的方法
  2. 管理消息队列
  3. 批量渲染

GIT地址:github.com/ant-design/…

源码解读

  • 导出支持调用的方法
// 定义支持调用的方法,作为default导出
const methods: (keyof MessageMethods)[] = ['success', 'info', 'warning', 'error', 'loading'];

const staticMethods = baseStaticMethods as MessageMethods & BaseMethods;

methods.forEach((type: keyof MessageMethods) => {
  staticMethods[type] = (...args: Parameters<TypeOpen>) => typeOpen(type, args);
});
export default staticMethods;
  • 管理队列
function typeOpen(type: NoticeType, args: Parameters<TypeOpen>): MessageType {
  // 管理队列
  const result = wrapPromiseFn((resolve) => {
    let closeFn: VoidFunction;

    const task: OpenTask = {
      type: 'open',
      config,
      resolve,
      setCloseFn: (fn) => {
        closeFn = fn;
      },
    };
    taskQueue.push(task);

    return () => {
      if (closeFn) {
        act(() => {
          closeFn();
        });
      } else {
        task.skipped = true;
      }
    };
  });
  flushNotice();
  return result;
}
  • 批量渲染
import { render } from 'rc-util/lib/React/render';

function flushNotice() {
  // 定义实例
  if (!message) {
    const holderFragment = document.createDocumentFragment();
    act(() => {
      render(
        <GlobalHolderWrapper 
          ref={(node) => {
            const { instance, sync } = node || {};

            // React 18 test env will throw if call immediately in ref
            Promise.resolve().then(() => {
              if (!newMessage.instance && instance) {
                newMessage.instance = instance;
                newMessage.sync = sync;
                flushNotice();
              }
            });
          }}
        />,
        holderFragment,
      );
    });

    return;
  }
  // Notification not ready
  if (!message.instance) {
    return;
  }

  // >>> Execute task
  // 批量渲染
  taskQueue.forEach((task) => {
    const { type, skipped } = task;

    // Only `skipped` when user call notice but cancel it immediately
    // and instance not ready
    if (!skipped) {
      switch (type) {
        case 'open': {
          act(() => {
            const closeFn = message!.instance!.open({
              ...defaultGlobalConfig,
              ...task.config,
            });

            closeFn?.then(task.resolve);
            task.setCloseFn(closeFn);
          });
          break;
        }

        case 'destroy':
          act(() => {
            message?.instance!.destroy(task.key);
          });
          break;

        // Other type open
        default: {
          act(() => {
            const closeFn = message!.instance![type](...task.args);

            closeFn?.then(task.resolve);
            task.setCloseFn(closeFn);
          });
        }
      }
    }
  });

  // Clean up
  taskQueue = [];
}