iView 源码解析 - Util 篇
React、Angular、Vue 前端三大框架中,目前工作接触最多的就是 Vue 了。对于 Vue 的几个有名的 UI 组件:Element、iView,我都使用来搭建过几个后台系统。相对来说,因为 iView 最早支持 render
函数的语法,与 TypeScript 配合较好;iView 团队对组件的更新、bug 的修复更积极;再加上我个人更喜欢 iView 的组件样式。因此,在我自己的项目中,我更偏向于使用 iView。
对于自学框架的时期,我觉得应该多去学习学习其生态中的优秀组件,通过学习他的源码来了解其他人是怎么使用这个框架的,理解一些高级特性的使用。针对我目前的工作,我准备从 iView 的源码入手,了解一些平时没怎么用到的 Vue 高级语法特性,然后再去尝试阅读 Vue 的源码。
本系列“iView 源码解析”,纯粹是记录我阅读 iView 源码时的一些心得感受,还谈不上从更高的角度来完成“解析“二字。本篇从 iView 的公共通用方法 Util 开始进入 iView 的世界。)
(iView 版本: 2.8; Vue 版本:2.5.13)
utils
utils 文件夹下有五个文件:
- assist.js
- calcTextareaHeight.js
- csv.js
- date.js
- dom.js
其中 csv.js 主要和 csv 文件的处理有关,本文将不对其做介绍讨论。
assist.js
类型检查
一般看某个文档的源码,我个人比较喜欢先去找类型检查部分的函数,往往越底层的代码越能学到一些新的东西。目前标准的类型检查方式都是利用方法 Object.prototype.toString.call(obj)
得到的值去判断(比如:underscore
)。iView 通过封装了一个 typeof
函数用于类型检查的处理,而判断的实质依旧是通过上述的方法来实现的:
1 | function typeOf(obj) { |
获取元素样式
我们知道 element.style
只能获取元素标签内通过 style
属性声明的样式,无法获取该元素的 CSS
样式。之前需要兼容低版本 IE 浏览器时,一般设计的获取样式的函数如下:
1 | function getStyle(obj, styleName) { |
来分析下 iView 的获取样式函数:
1 | export function getStyle(element, styleName) { |
iView 中增加了一些安全性检测,camelCase
是其编写的将字符串转换为“驼峰形式”的函数。document.defaultView.getComputed
实际就是 window.getComputed
的写法,然而前者的写法可以避免 Firefox 3.6 上无法获取 iframe
样式的问题(参考链接)。
iView 是从 IE 9 版本开始进行支持的,而 getComputed
刚好可以兼容到 IE9,因此没有再考虑通过 currentStyle
对 IE8 及以下版本的浏览器进行支持了。
深拷贝
iView 的深拷贝函数其实很简洁、易理解,通过判断需要拷贝的元素类型,如果是数组、对象则再对其子元素进行递归处理即可;如果不是,则直接返回。
1 | function deepCopy(data) { |
获取滚动条宽度
每个浏览器的滚动条样式都是不同的,此函数是用于获取当前浏览器的滚动条宽度,并将其用一个变量存储起来,可以用于需要全屏遮罩时的一些样式处理上。
该函数的思路是生成一个外层的定宽、定高的 outer
元素,为防止破坏页面,令其绝对定位、visibility = 'hidden'
。在生成一个 outer
的子元素 inner
,令其高度比父元素高,宽度为 100%。然后通过调整父元素的 overflow
属性,测量有、无滚动条时子元素的 offsetWidth
,再通过差值计算即可。
1 | let cached; |
这里请注意第 28 行 widthContained === widthScroll
,这部分比较的是有无滚动条两个情况下 inner.offsetWidth
值。作者特意判断了一下相等的情况,那就说明有可能是存在兼容性问题的。我一开始是以为 offsetWidth
的兼容性问题,以为在某种情况下会将滚动条的宽度也计入。但自己写了一个 demo 后,测试了 IE8~11、Edge、Chrome、Firefox、Safari 几款浏览器,最终竟然在 Safari 上找到了问题的所在。该兼容性导致的原因是 width: 100%
这个属性,在其他浏览器中,子元素声明该属性后,获取的宽度值是父元素下除去滚动条以外的宽度;而在 Safari 中,子元素声明该属性后,它获取的宽度就是父元素的宽度值,因此会产生上述值相等的情况。
class 类的处理
iView 中封装了三个处理 class 的方法:hasClass
、addClass
、removeClass
。这里值得一提的是,这三个函数都是优先对元素的 classList
属性进行检测,如果存在,直接调用 classList
的方法即可实现相应的函数。
classList
是 W3C 提供的用于获取元素类属性的实时 DOMTokenList
集合,封装了五个操作 class 的方法,基本可以实现对 class 操作的所有需求。但是在浏览器中,IE 10 以上是不兼容的,在使用用还需进行兼容性判断。
函数中其中有这么一行引起了我的注意:
1 | // hasClass(el, cls) |
以上是函数 hasClass
在判断 classList
不存在时采取的判断方法,我开始有疑惑的部分是为什么在类名前后各加一个空格。后来才意识到这是为了完成对整个字符串的检验,比如:el 中存在类 backgroundColor
,传参过来需要检验的类是 background
。如果不加空格,上述的返回结果将是正确的,而与实际需求不符。在两个类名前后各加一个空格即可避免这种情况,实现字符的全判断。
其它
assist.js
中还封装了几个函数,但个人认为都是一眼能看懂的,因此就不再此表述了。
date.js
封装了一些对时间处理的函数,此部分结合后续的日期组件进行讨论。
dom.js
该文件内主要封装了两个跟事件有关的函数:on
、off
。在兼容性判断上也是比较了两对函数:addEventListener
和 attachEvent
,removeEventListener
和 detachEvent
。这两个方法比较常见了,故不在此进行讨论了。