Toast轻提示组件的实现原理与常见坑点总结

Dev · 悦轩 组件 阅读 606
赞 15 收藏
二维码
手机扫码查看
反馈

项目需求逼出来的Toast组件

最近做的那个电商项目,用户操作反馈这块要求还挺细的。之前都是简单的alert一下,产品经理说不行,用户体验太差了。需要那种不会打断用户操作的轻提示,就是Toast。说实话,这玩意儿看起来简单,做起来还真有不少坑。

Toast轻提示组件的实现原理与常见坑点总结

开始我想着直接找个现成的UI库,但项目已经够重了,不想再加太多依赖。所以决定自己写一个,既能满足需求,又能控制体积。事实证明这个选择是对的,毕竟定制化程度高,而且后期维护也方便。

核心实现其实不复杂

Toast的核心逻辑其实就几行代码,主要是创建DOM元素、设置样式、自动消失。我这里用的是Vue 3 + TypeScript的组合:

// toast.js
let toastInstance = null;

function showToast(message, type = 'info', duration = 3000) {
  // 如果已经有实例了,先清除
  if (toastInstance) {
    document.body.removeChild(toastInstance);
  }

  const toast = document.createElement('div');
  toast.className = toast toast-${type};
  toast.textContent = message;
  
  // 添加到页面
  document.body.appendChild(toast);
  toastInstance = toast;

  // 触发显示动画
  setTimeout(() => {
    toast.classList.add('show');
  }, 10);

  // 自动移除
  setTimeout(() => {
    toast.classList.remove('show');
    setTimeout(() => {
      if (toastInstance === toast && document.contains(toast)) {
        document.body.removeChild(toast);
        if (toastInstance === toast) {
          toastInstance = null;
        }
      }
    }, 300);
  }, duration);
}

export { showToast };

CSS样式也很简单,主要是定位和动画效果:

/* toast.css */
.toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.8);
  background-color: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 12px 20px;
  border-radius: 6px;
  z-index: 9999;
  font-size: 14px;
  min-width: 120px;
  text-align: center;
  opacity: 0;
  transition: all 0.3s ease;
  pointer-events: none;
}

.toast.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.toast-success {
  background-color: rgba(46, 204, 113, 0.9);
}

.toast-error {
  background-color: rgba(231, 76, 60, 0.9);
}

.toast-warning {
  background-color: rgba(243, 156, 18, 0.9);
}

最大坑:并发请求导致的混乱

项目中遇到了个头疼的问题,就是当用户连续点击按钮触发多个Toast时,会出现叠加和闪烁的情况。开始没想到这个问题,测试的时候才发现,用户快速点击几下,界面上会同时出现好几个提示框,非常难看。

折腾了半天,最终的解决方案是在toast函数里加了个全局实例引用,确保同一时间只存在一个Toast。具体就是在创建新Toast之前,先判断是否已有实例,如果有就先移除掉。这个方案虽然简单,但在大部分场景下都够用了。

不过这里还遇到个小问题,就是如果前一个Toast还没完全消失,后一个就来了,可能会出现闪动。后来在移除时加了个延时,让消失动画能完整执行完,这样视觉效果就好多了。

移动端适配也不算难

在移动端测试的时候发现,Toast的位置不太合适,特别是在iPhone的刘海屏上,会被状态栏遮挡一部分。这个问题好解决,主要是调整了top值,改成动态计算居中位置。

还有个问题是字体大小,在不同设备上看起来差异挺大的。后来改成用rem单位,配合媒体查询做了一些微调。大部分情况下默认样式就够了,只有在极端情况下才需要特殊处理。

性能方面主要考虑了内存泄漏的问题。每次Toast消失后,都要确保DOM节点被正确清理,避免造成内存堆积。这个问题在长时间运行的页面中尤其需要注意。

API设计尽量简单

为了让其他同事也能轻松使用,API设计得尽可能简单。就三个参数:消息内容、类型(info、success、error、warning)、持续时间。大部分时候用默认参数就行,少数情况需要自定义持续时间。

实际使用起来就很方便了:

import { showToast } from './utils/toast';

// 简单使用
showToast('操作成功');

// 自定义类型和时间
showToast('网络错误', 'error', 2000);

// 成功提示
showToast('数据保存成功', 'success');

这种设计既满足了基本需求,又保持了足够的灵活性。后来发现确实很好用,项目组的人都说比以前的alert友好太多了。

还有些细节待完善

目前这个Toast组件在大多数场景下都工作得很好,但还有几个小问题没有完美解决。比如当页面有fixed定位的元素时,层级关系有时候会有点乱。不过这个问题出现频率不高,影响也不大,暂时就先这样了。

另外就是国际化支持,现在写死的是中文,后面可能会扩展一下,但这不是当前的重点。总的来说,这个组件达到了预期目标,既简单又实用。

以上是我踩坑后的总结,希望对你有帮助。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论