效果如下:

关闭message后前后message的衔接非常丝滑,这部分是我比较感兴趣的。带着这个问题先了解下DOM结构,顺便整理下作者的思路。

从DOM里我们可以看到所有的message都在一个容器里,而这个容器做了绝对定位实现了可视窗口的水平居中,新增的message只要在容器里append对应的元素就会在页面上显示出来。

接下来我们看下每个message元素的秘密~

我们可以看到这里设置了height和padding属性的动画,那上文中的动画大概率是在关闭时设置height和padding为0,因为bfc的规则在动画期间前后的message也会挤占其空间,所以看起来比较丝滑。动画执行完成后再将元素remove掉。

这里注意到一个不太常用的css属性:will-change,援引MDN上的表述,这个属性会根据开发者指定的要改变的值提前做优化准备。will-change

其他内部的元素是常见的根据弹框类型和消息进行渲染,不再进行细究。接下来关注点放到js上。

引入方式很简单,只有一个js文件

代码示例如下:

  1. 1 // 配置全局默认参数
  2. 2 cocoMessage.config({
  3. 3 duration: 10000,
  4. 4 });
  5. 5 // 普通消息,可传入自动关闭时间、提示信息、关闭回调
  6. 6 cocoMessage.info(3000, "请先登录!", function () {
  7. 7 console.log("close");
  8. 8 });
  9. 9 // 成功消息,可传入element元素
  10. 10 var div1 = document.createElement("div");
  11. 11 div1.innerText = "修改成功!";
  12. 12 cocoMessage.success(div1);
  13. 13 // 警告消息,时间设置0不会自动关闭
  14. 14 cocoMessage.warning("需要手动关闭", 0);
  15. 15 // 失败消息
  16. 16 cocoMessage.error("修改失败!", 3000);
  17. 17 // loading消息
  18. 18 var closeMsg = cocoMessage.loading(true);
  19. 19 setTimeout(function () {
  20. 20 closeMsg();
  21. 21 }, 4000);
  22. 22 // 关闭所有消息
  23. 23 cocoMessage.destroyAll();

这里我们可看到支持的类型有info、success、warning、error、loading五种,基本场景都覆盖到了。传参比较灵活,可以一个两个三个,而且类型也不固定。带着这些疑问开始了真正的代码走读,解开它神秘的面纱:

首先看下代码结构:

定义了一个兼容的_typeof方法(原谅我没看懂这个兼容逻辑,有明白的兄弟可以在评论区留言);然后是一个常见的立即执行函数,函数前的!和用括号包裹起来的效果一样,常见的还有+-。

传进去两个参数,第一个是void 0(也就是undefined,个人感觉用this也没问题,感兴趣的可以了解下),另一个参数是主体函数,后面会做详细介绍,我们先看下这个立即执行函数都做了什么:

先检查环境中是否有module.exports再检查define.amd,最后才用全局变量。显然这个是兼容CommonJs规范、AMD/CMD规范和直接引用的写法。其中用global = global || self 的写法而不是window因为可以兼容服务器端(global全局变量)和浏览器端(window全局变量)。这样会在浏览器window变量下暴露cocoMessage变量。另外提一下使用立即执行函数是可以避免污染全局变量的。在进入方法内部前,建议把这部分代码收藏一波

  1. 1 !function (global, factory) {
  2. 2 (typeof exports === "undefined" ? "undefined" : _typeof(exports)) === "object" && typeof module !== "undefined" ?
  3. 3 module.exports = factory() :
  4. 4 typeof define === "function" && define.amd ?
  5. 5 define(factory) :
  6. 6 (global = global || self, global.cocoMessage = factory());
  7. 7 }(void 0, function () {
  8. 8 // code here
  9. 9 });

刚进入方法会创建msgWrapper变量保存消息父元素,定义默认配置initArgs,暴露cocoMessage变量并在页面元素加载完毕后添加style标签。上图中白色备注是比较通用的方法,下文会将重点放在红色备注的方法上。

首先关注下创建msgWrapper元素的c方法

第一个参数传输对象,key可以是className来给元素添加class属性,另一种可以是以_开头可以给元素绑定相应的事件。

第二个参数可以传输文本、元素、包含多个元素的数组(或者伪数组)。

  1. // 创建class为coco-msg-stage的div元素
  2. var msgWrapper = c({
  3. className: "coco-msg-stage"
  4. }, "默认消息");
  5. // 创建class为coco-msg-wait并在元素上绑定click事件
  6. c({
  7. className: "coco-msg-wait " ,
  8. _click: function _click () {
  9. if (closable) {
  10. closeMsg(el, onClose);
  11. }
  12. }
  13. }
  14. // 多次调用创建复杂元素
  15. c({
  16. className: "coco-msg-stage"
  17. }, c({
  18. className: "coco-msg-loading",
  19. _click: function(){
  20. console.log("loading")
  21. }
  22. })
  23. );
然后看下initArgs和cocoMessage这两个变量

共有info、success、warning、error、loading、destoryAll、config这七个方法,这也正是这个插件想要暴露给用户的。默认参数中消息是空字符串,关闭时间是2s,不会显示关闭按钮,当然这些都可以通过config方法修改全局的默认配置,从中也可以看到随时可以修改配置,即时生效。除去destoryAll方法我们先关注下消息的5中类型,创建方法大同小异:都是通过initConfig方法创建的。其中arguments是伪数组,也就是我们调用info等方法传递的所有参数,可以通过通数组一样角标方式取值。loading方法是特殊的,把initConfig方法的结果返回了,通过demo我们知道返回值是个方法,执行后会关闭loading,下文我们再关注下loading类型的消息有什么特殊处理,开始进入initConfig方法

这里仅是对参数进行统一,上文中有个疑问,为什么参数可以随便传,而且顺序不一致也不影响?答案就在这个方法里,之前传的参数有提示信息:字符串(string类型)或元素(Element或object类型)、延迟时间:数字(number类型)、关闭后的回调:方法(function类型)、是否显示关闭按钮(boolean类型)。到这里应该发现这里的玄机了,每个参数都有唯一的类型而且还不会冲突,这样就可以根据传参类型的不同识别传的值了。封装后的对象大致如下:

  1. {
  2. msg: "提示消息",
  3. type: "info",
  4. showClose: false,
  5. duration: 2000,
  6. onClose: function(){
  7. console.log("closed")
  8. }
  9. }

虽然以上方法设计得很巧妙,但是健壮性要差一些,如果要扩展设置文字是否居中、自定义类名、自定义图标等功能时不免会要进行重构。所以调用方法改成这样会便于扩展:

  1. cocoMessage.info({
  2. msg: '请先登录',
  3. duracion: '2000',
  4. showClose: true,
  5. onClose: function(){
  6. console.log('closed')
  7. }
  8. });

到这一步需要的参数封装完成了,接下来会调用createMsgEl方法创建消息元素。

方法较长分为两个部分,完成了根据传入的参数创建元素并添加到body中显示出来,并绑定关闭按钮的点击事件和触发自动关闭的条件。图中画问好的正是上文中我们存疑的问题,正因为这里返回了关闭消息的方法就可以实现执行后关闭loading。

到这里还剩closeMsg方法destoryAll方法,我们先看closeMsg:

首先会设置padding和height为0,进而实现开头说的动画效果,然后执行自定义的关闭回调方法。最后再删除消息元素,如果没有消息也会把父元素一并删除。需要注意的是这里还会判断消息元素是否存在,这并不是冗余的代码,而是考虑到点击按钮关闭和执行全部关闭一起执行时后触发的会报错的问题,因为这里有300ms的延迟。

最后是destoryAll方法,关闭所有消息。

获取父元素所有的消息元素再循环调用closeMsg方法进行删除。

 到这里原生的消息提示插件已经解读完毕,这是款很轻量和优秀的插件。
 
总结:
首先通过立即执行函数避免全局变量污染,只对外暴露七个方法。通过变量检测实现对CommonJs规范、AMD/CMD规范和直接引用的兼容。通过巧妙的参数设计,实现了对参数先后顺序没有要求,通过设置padding和height值来实现动画效果。
亮点:
设置will-change的css属性提高性能和用户体验;
通过兼容监听元素animationEnd事件来执行回调,解决了setTimeout不准确和性能问题;
通过c方法可以很方便得创建元素,有点react的味道;
给svg内元素设置动画;
 
插件预览地址:https://www.jq22.com/jquery-info23645

【源码解读】js原生消息提示插件的更多相关文章

  1. fastclick.js源码解读分析

    阅读优秀的js插件和库源码,可以加深我们对web开发的理解和提高js能力,本人能力有限,只能粗略读懂一些小型插件,这里带来对fastclick源码的解读,望各位大神不吝指教~! fastclick诞生 ...

  2. 消息提示插件toastr.js与Messenger组件

    Toastr是一款基于jQuery的通知插件,可以灵活的自定义样式和拓展其功能! toastr是一个基于Jquery简单.漂亮的消息提示插件,使用简单.方便,可以根据设置的超时时间自动消失. cdn最 ...

  3. js便签笔记(10) - 分享:json2.js源码解读笔记

    1. 如何理解“json” 首先应该意识到,json是一种数据转换格式,既然是个“格式”,就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转 ...

  4. js便签笔记(10) - 分享:json.js源码解读笔记

    1. 如何理解“json” 首先应该意识到,json是一种数据转换格式,既然是个“格式”,就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转 ...

  5. Mybatis源码解读-插件

    插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...

  6. php-msf 源码解读【转】

    php-msf: https://github.com/pinguo/php-msf 百度脑图 - php-msf 源码解读: http://naotu.baidu.com/file/cc7b5a49 ...

  7. 从koa-session源码解读session本质

    前言 Session,又称为"会话控制",存储特定用户会话所需的属性及配置信息.存于服务器,在整个用户会话中一直存在. 然而: session 到底是什么? session 是存在 ...

  8. React16源码解读:开篇带你搞懂几个面试考点

    引言 如今,主流的前端框架React,Vue和Angular在前端领域已成三足鼎立之势,基于前端技术栈的发展现状,大大小小的公司或多或少也会使用其中某一项或者多项技术栈,那么掌握并熟练使用其中至少一种 ...

  9. Vue 源码解读(8)—— 编译器 之 解析(上)

    特殊说明 由于文章篇幅限制,所以将 Vue 源码解读(8)-- 编译器 之 解析 拆成了上下两篇,所以在阅读本篇文章时请同时打开 Vue 源码解读(8)-- 编译器 之 解析(下)一起阅读. 前言 V ...

随机推荐

  1. C#数据结构-赫夫曼树

    什么是赫夫曼树? 赫夫曼树(Huffman Tree)是指给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小.哈夫曼树(也称为最优二叉树)是带权路径长度最短的树,权值较大的结点 ...

  2. 记MSSQL和MYSQL

    简单的说就是mssql是asp和asp.net是黄金搭档mysql是PHP是黄金搭档他们相互结合比较好用,速度也比较快!!!MSSQL就是SQLSERVER,MS是微软的缩写MYSQL是一套免费的数据 ...

  3. Dubbo SPI源码解析①

    目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 ​ SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...

  4. 【Hadoop】:Windows下使用IDEA搭建Hadoop开发环境

    笔者鼓弄了两个星期,终于把所有有关hadoop的环境配置好了,一是虚拟机上的完全分布式集群,但是为了平时写代码的方便,则在windows上也配置了hadoop的伪分布式集群,同时在IDEA上就可以编写 ...

  5. 徐汉彬:Web系统大规模并发——电商秒杀与抢购

    摘要:电商的秒杀和抢购,从技术的角度来说,会对Web系统产生巨大的考验.本期<问底>,徐汉彬将带大家关注秒杀和抢购的技术实现和优化,同时,从技术层面揭开,为什么我们总是不容易抢到火车票的原 ...

  6. docker配置mysql实现主从同步问题

    主从同步遇到 Got fatal error 1236 from master when reading data from binary log: 'Could not find first log ...

  7. MySql Docker 主主配置

    MySql 主主 准备2台Linux服务器,并且在两台服务器上,同时安装docker,国内的同学可以使用aliyun的镜像安装. curl -fsSL https://get.docker.com - ...

  8. spark的运行指标监控

    sparkUi的4040界面已经有了运行监控指标,为什么我们还要自定义存入redis? 1.结合自己的业务,可以将监控页面集成到自己的数据平台内,方便问题查找,邮件告警 2.可以在sparkUi的基础 ...

  9. 如何将项目推到github上面

    1.先查看是否安装git. 2.如果没有安装git ,下载之后别忘了配置环境变量.(右击此电脑 --属性--高级系统设置--环境变量--系统变量中的path) 3.推代码 查看状态(可查可不查) gi ...

  10. MySQL中的全局锁和表级锁

    全局锁和表锁 数据库锁设计的初衷是解决并发出现的一些问题.当出现并发访问的时候,数据库需要合理的控制资源的访问规则.而锁就是访问规则的重要数据结构. 根据锁的范围,分为全局锁.表级锁和行级锁三类. 全 ...