我都使用过element plus 或者ant design Vue这些UI组件库,像element的全局message组件是如何通过一个ElMessage.success(),.warning(),.error()方法调用UI弹框显示,我们明明没有在template区域注入组件,这是为什么呢?答案就在下面!
这里因个人习惯,我使用的是typescript。
一、先定义message类型和实例类型。
            
            
              typescript
              
              
            
          
          import { createVNode } from 'vue';
import type { VNode } from 'vue'
import type { App } from 'vue';
// 定义通知类型
export type NotificationType = 'info' | 'success' | 'warning' | 'error';
// 定义通知选项
export interface NotificationOptions {
  message: string;
  type?: NotificationType;
  duration?: number;
  onClose?: () => void;
}
// 定义通知实例
interface NotificationInstance {
  id: number;
  vnode: VNode;
  container: HTMLDivElement;
}二、创建容器实例和通知列表、id计数器,每一个通知实例都有一个唯一的id。
            
            
              ini
              
              
            
          
          // 通知容器
let notificationContainer: HTMLElement | null = null;
// 通知实例列表
const notifications: NotificationInstance[] = [];
// ID计数器
let seed = 0;三、创建容器,自定义样式模板
            
            
              css
              
              
            
          
          // 创建通知容器
const createNotificationContainer = () => {
  if (notificationContainer) return;
  notificationContainer = document.createElement('div');
  notificationContainer.className = 'global-notification-container';
  document.body.appendChild(notificationContainer);
  // 添加样式
  const style = document.createElement('style');
  style.textContent = `
    .global-notification-container {
      position: fixed;
      top: 20px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 9999;
      width: fit-content;
      max-width: 1000px;
    }
    
    .global-notification {
      margin-bottom: 16px;
      padding: 12px 20px;
      border-radius: 4px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      display: flex;
      align-items: center;
      transform: translateY(-100%);
      opacity: 0;
      transition: all 0.3s ease;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      min-width: 300px;
      justify-content: center;
    }
    
    .global-notification.show {
      transform: translateY(0);
      opacity: 1;
    }
    
    .global-notification.info {
      background-color: #e6f7ff;
      border: 1px solid #91d5ff;
      color: #333;
    }
    
    .global-notification.success {
      background-color: #f6ffed;
      border: 1px solid #b7eb8f;
      color: #333;
    }
    
    .global-notification.warning {
      background-color: #fffbe6;
      border: 1px solid #ffe58f;
      color: #333;
    }
    
    .global-notification.error {
      background-color: #fff2f0;
      border: 1px solid #ffccc7;
      color: #333;
    }
    
    .global-notification-icon {
      margin-right: 12px;
      font-size: 16px;
      line-height: 20px;
    }
    
    .global-notification-content {
      flex: 1;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
    }
    
    .global-notification-close {
      margin-left: 12px;
      cursor: pointer;
      font-size: 14px;
      line-height: 20px;
      color: #999;
    }
    
    .global-notification-close:hover {
      color: #333;
    }
    
    .global-notification.info .global-notification-icon {
      color: #1890ff;
    }
    
    .global-notification.success .global-notification-icon {
      color: #52c41a;
    }
    
    .global-notification.warning .global-notification-icon {
      color: #faad14;
    }
    
    .global-notification.error .global-notification-icon {
      color: #ff4d4f;
    }
  `;
  document.head.appendChild(style);
};四、创建单个通知,自定义HTML模板
            
            
              ini
              
              
            
          
          // 创建单个通知
const createNotification = (options: NotificationOptions) => {
  const id = seed++;
  const type = options.type || 'info';
  const duration = options.duration === undefined ? 4500 : options.duration;
  // 创建容器元素
  const container = document.createElement('div');
  container.className = 'global-notification';
  // 创建通知内容
  const icons = {
    info: 'ℹ️',
    success: '✅',
    warning: '⚠️',
    error: '❌'
  };
  container.innerHTML = `
    <div class="global-notification-icon">${icons[type]}</div>
    <div class="global-notification-content">${options.message}</div>
    <div class="global-notification-close">×</div>
  `;
  container.classList.add(type);
  // 添加到容器
  notificationContainer?.appendChild(container);
  // 触发进入动画
  setTimeout(() => {
    container.classList.add('show');
  }, 10);
  // 绑定关闭事件
  const closeBtn = container.querySelector('.global-notification-close');
  const close = () => {
    container.classList.remove('show');
    setTimeout(() => {
      container.remove();
      options.onClose?.();
    }, 300);
  };
  closeBtn?.addEventListener('click', close);
  // 自动关闭
  if (duration > 0) {
    setTimeout(close, duration);
  }
  // 保存实例
  const instance: NotificationInstance = {
    id,
    vnode: createVNode('div'),
    container
  };
  notifications.push(instance);
  return instance;
};五、导出方法
            
            
              typescript
              
              
            
          
          // 提供不同类型的通知方法
export const notification = {
  open: (options: NotificationOptions) => {
    createNotificationContainer();
    return createNotification(options);
  },
  info: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'info',
      ...options
    });
  },
  success: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'success',
      ...options
    });
  },
  warning: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'warning',
      ...options
    });
  },
  error: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'error',
      ...options
    });
  },
  // 关闭所有通知
  closeAll: () => {
    notifications.forEach(instance => {
      instance.container.remove();
    });
    notifications.length = 0;
  }
};六、导出自定义插件
            
            
              javascript
              
              
            
          
          // Vue插件安装方法
export default {
  install(app: App) {
    app.config.globalProperties.$notification = notification;
    app.provide('notification', notification);
  }
};七、注册插件
            
            
              javascript
              
              
            
          
          import globalMessage from './utils/global-message'
app.use(globalMessage)八、所有代码
            
            
              css
              
              
            
          
          // src/utils/global-message.ts
import { createVNode } from 'vue';
import type { VNode } from 'vue'
import type { App } from 'vue';
// 定义通知类型
export type NotificationType = 'info' | 'success' | 'warning' | 'error';
// 定义通知选项
export interface NotificationOptions {
  message: string;
  type?: NotificationType;
  duration?: number;
  onClose?: () => void;
}
// 定义通知实例
interface NotificationInstance {
  id: number;
  vnode: VNode;
  container: HTMLDivElement;
}
// 通知容器
let notificationContainer: HTMLElement | null = null;
// 通知实例列表
const notifications: NotificationInstance[] = [];
// ID计数器
let seed = 0;
// 创建通知容器
const createNotificationContainer = () => {
  if (notificationContainer) return;
  notificationContainer = document.createElement('div');
  notificationContainer.className = 'global-notification-container';
  document.body.appendChild(notificationContainer);
  // 添加样式
  const style = document.createElement('style');
  style.textContent = `
    .global-notification-container {
      position: fixed;
      top: 20px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 9999;
      width: fit-content;
      max-width: 1000px;
    }
    
    .global-notification {
      margin-bottom: 16px;
      padding: 12px 20px;
      border-radius: 4px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      display: flex;
      align-items: center;
      transform: translateY(-100%);
      opacity: 0;
      transition: all 0.3s ease;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      min-width: 300px;
      justify-content: center;
    }
    
    .global-notification.show {
      transform: translateY(0);
      opacity: 1;
    }
    
    .global-notification.info {
      background-color: #e6f7ff;
      border: 1px solid #91d5ff;
      color: #333;
    }
    
    .global-notification.success {
      background-color: #f6ffed;
      border: 1px solid #b7eb8f;
      color: #333;
    }
    
    .global-notification.warning {
      background-color: #fffbe6;
      border: 1px solid #ffe58f;
      color: #333;
    }
    
    .global-notification.error {
      background-color: #fff2f0;
      border: 1px solid #ffccc7;
      color: #333;
    }
    
    .global-notification-icon {
      margin-right: 12px;
      font-size: 16px;
      line-height: 20px;
    }
    
    .global-notification-content {
      flex: 1;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
    }
    
    .global-notification-close {
      margin-left: 12px;
      cursor: pointer;
      font-size: 14px;
      line-height: 20px;
      color: #999;
    }
    
    .global-notification-close:hover {
      color: #333;
    }
    
    .global-notification.info .global-notification-icon {
      color: #1890ff;
    }
    
    .global-notification.success .global-notification-icon {
      color: #52c41a;
    }
    
    .global-notification.warning .global-notification-icon {
      color: #faad14;
    }
    
    .global-notification.error .global-notification-icon {
      color: #ff4d4f;
    }
  `;
  document.head.appendChild(style);
};
// 创建单个通知
const createNotification = (options: NotificationOptions) => {
  const id = seed++;
  const type = options.type || 'info';
  const duration = options.duration === undefined ? 4500 : options.duration;
  // 创建容器元素
  const container = document.createElement('div');
  container.className = 'global-notification';
  // 创建通知内容
  const icons = {
    info: 'ℹ️',
    success: '✅',
    warning: '⚠️',
    error: '❌'
  };
  container.innerHTML = `
    <div class="global-notification-icon">${icons[type]}</div>
    <div class="global-notification-content">${options.message}</div>
    <div class="global-notification-close">×</div>
  `;
  container.classList.add(type);
  // 添加到容器
  notificationContainer?.appendChild(container);
  // 触发进入动画
  setTimeout(() => {
    container.classList.add('show');
  }, 10);
  // 绑定关闭事件
  const closeBtn = container.querySelector('.global-notification-close');
  const close = () => {
    container.classList.remove('show');
    setTimeout(() => {
      container.remove();
      options.onClose?.();
    }, 300);
  };
  closeBtn?.addEventListener('click', close);
  // 自动关闭
  if (duration > 0) {
    setTimeout(close, duration);
  }
  // 保存实例
  const instance: NotificationInstance = {
    id,
    vnode: createVNode('div'),
    container
  };
  notifications.push(instance);
  return instance;
};
// 提供不同类型的通知方法
export const notification = {
  open: (options: NotificationOptions) => {
    createNotificationContainer();
    return createNotification(options);
  },
  info: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'info',
      ...options
    });
  },
  success: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'success',
      ...options
    });
  },
  warning: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'warning',
      ...options
    });
  },
  error: (message: string, options?: Omit<NotificationOptions, 'message' | 'type'>) => {
    return notification.open({
      message,
      type: 'error',
      ...options
    });
  },
  // 关闭所有通知
  closeAll: () => {
    notifications.forEach(instance => {
      instance.container.remove();
    });
    notifications.length = 0;
  }
};
// Vue插件安装方法
export default {
  install(app: App) {
    app.config.globalProperties.$notification = notification;
    app.provide('notification', notification);
  }
};效果展示
 编辑
编辑
 编辑
编辑