乐趣区

组件设计一款Notice组件

前言

最近在使用 Vue+TypeScript 鼓捣自己的组件库,期间参考不少(抄????)elementiview的源码。发现了一些常用的功能的背后,往往是复杂的实现。于是准备写一系列文章,介绍这些组件背后的原理。今天是第一篇,手把手带你实现 Notice 组件。

API 设计

通常我们在使用 iview 或者 element 的 Notice 组件的时候,都是通过调用挂载到 Vue 原型链上的方法的形式。如下图所示。

我们的组件的调用方式,也参考类似的设计, 不同的是我们新添加了一个 APIsetLen。代码如下

这是因为楼主曾经接过一个需求,在做公司一款 toC 的时候,产品不希望屏幕上出现太多的通知框,而是希望一次最多只出现 3 个。所有楼主在设计组件的时候,将这个定制化的需求也添加了上去。

因为我们需要实现,屏幕上只显示指定数量的 Notice 通知框,所以我们使用两个数组,保存 Notice 的实例。queue 队列用来存储全部的 Notice 实例,showQueue 用来屏幕上显示的 Notice 实例。

$Notice,方法用来想 queue 添加了一个 Notice 的实例; processQueue 方法则用来处理 queue 队列; remove 方法删除特定的 Notice; clear 方法则用来清除全部的 Notice。setLen 用来设定同屏显示 Notice 的数量。len 属性则是同屏的最大数量。

模版设计

模版设计没啥好说的,常规布局。其中 Icon 组件,是我之前写的图标组件。showClose,控制是否显示 Icon 图标。visible 控制 Notice 的显隐。

方法设计

$Notice

使用 Vue.extend 方法构建 NoticeConstructor,NoticeConstructor 是 Vue 的子类。NoticeConstructor 的实例,可以使用 $mount 方法生成 DOM,然后手动或者指定 $mount 的参数,将 DOM 渲染到页面之中。

在 $Notice 方法的内部,使用 uuid, 生成一个唯一 id,这个唯一的标记,将会帮助我们查找队列中指定的 notice 对象。紧接着我们会对 onClose 方法进行一层包装。**onClose 将会在每次关闭 notice 的时候调用,onClose 在内部调用 $Notice.remove 方法,$Notice.remove 方法会将指定的 id 所对应的 notice 对象移除出队列 **。

接着我们将创建 notice 的实例,并将其 push 到 queue 队列中,接着调用 $Notice.processQueue 方法处理 queue 队列。

关于 uuid 这个方法,uuid 这个方法生成的并不是真的唯一 id,而是一个重复概率很低的 id。重复概率大概是 1ms 内,1 亿多分之 1 吧。这个是我在 stackoverflow 上找的方法,代码如下。

$Notice.processQueue

在 processQueue 方法中,我们首先判断 showQueue 队列是不是 的。如果不是,我们将会从 queue 队列的头部截取一个 notice 对象。使用 $mount 方法,生成 DOM 并 append 到 body 中。

因为 notice 在页面上的样式,是自上而下的,所以我们将会计算 notice 的相对顶部的偏移量,每一个 notice 对象的自身高度和 15px 的间距。

同时,我们会将 notice 的 visible 属性设置为 true,这会触发我们的 transition 动画,并将这个 notice 对象 push 到 showQueue 队列中。

生命周期 mounted

接着我们将目光转移到 Notice 组件内部,我们将 notice,append 到 DOM 中后。我们会在 mounted 函数中起一个定时器,定时器将会等待指定的 duration 毫秒,duration 是我们指定 notice 存在的时间,如果 duration 为 0,notice 将会永远存在。

duration 毫秒之后,将会执行 notice 组件内部的 close 方法。

close

在 close 方法中,我们会为当前组件添加 transitionend,事件。这个事件将会在 css 动画( 离场动画)结束后触发。我们将 visible 设置为 false 这会触发,组件的离场动画。接着我们调用 onClose 方法,这会处理我们的队列。

$Notice.remove

之前我们对 onClose 进行了一层包装,调用 onClose 方法,会调用我们的 $Notice.remove 方法。

在 $Notice.remove 方法中,我们将会通过 id 找到需要移除的 notice 对象,将其移除出 showQueue 队列,接着循环剩下的 showQueue 队列,将它们 style.top 向上移动。最后我们继续调用 $Notice.processQueue 方法,从 queue 队列中,拉取新的 notice 对象,push 到 showQueue 队列中。

destroy

当离场动画执行完毕后,transitionend 回调会调用 destroy 方法。

destroy 将会主动卸载我们的组件,并从 DOM 中移除我们的元素。notice 对象的生命周期至此结束。

$Notice.clear & $Notice.setLen

clear 和 setLen 相对而言比较简单,这里就不再赘述了。

后续

  1. 「组件」设计一款 Input 组件
  2. 「组件」设计一款 Grid 组件
  3. 「组件」设计一款 Button 组件
  4. 「组件」设计一款 Collapse 组件
  5. 「组件」设计一款 Icon 组件
  6. 「组件」设计一款 Select 组件
  7. 「组件」设计一款 Autocomplete 组件

…..

本系列的文章,尽量做到短小精悍。Select,Table,DatePicker 组件将会难点。

明天可能会更新一篇 React Hook 的学习文章。因为报名了周末晚上的公开课介绍 Hook 的原理。下周想重新阅读下 preact 的源码,学习学习 preact 中 hook 的实现原理。

参考

  • iview 源码
  • element 源码
退出移动版