A. 前端性能优化总结(一)-js、css优化
移动互联网时代,用户对于网页的打开速度要求越来越高。首屏作为直面用户的第一屏,其重要性不言而喻。优化用户体验更是我们前端开发非常需要 focus 的东西之一。
从用户的角度而言,当打开一个网页,往往关心的是从输入完网页地址后到最后展现完整页面这个过程需要的时间,这个时间越短,用户体验越好。所以作为网页的开发者,就从输入url到页面渲染呈现这个过程中去提升网页的性能。
所以输入URL后发生了什么呢?在浏览器中输入url会经历域名解析、建立TCP连接、发送http请求、资源解析等步骤。
http缓存优化是网页性能优化的重要一环,这一部分我会在后续笔记中做一个详细总结,所以本文暂不多做详细整理。本文主要从网页渲染过程、网页交互以及Vue应用优化三个角度对性能优化做一个小结。
首先谈谈拿到服务端资源后浏览器渲染的流程:
关键渲染路径是浏览器将 HTML、CSS、JavaScript 转换为在屏幕上呈现的像素内容所经历的一系列步骤。也就是我们刚刚提到的的的浏览器渲染流程。
为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素:
首先,DOM 和 CSSOM 通常是并行构建的,所以 CSS 加载不会阻塞 DOM 的解析。
然而,由于 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,
所以他必须等待到 CSSOM Tree 构建完成,也就是 CSS 资源加载完成(或者 CSS 资源加载失败)后,才能开始渲染。因此,CSS 加载会阻塞 Dom 的渲染。
由此可见,对于 CSSOM 缩小、压缩以及缓存同样重要,我们可以从这方面考虑去优化。
当浏览器遇到 script 标记时,会阻止解析器继续操作,直到 CSSOM 构建完毕,JavaScript 才会运行并继续完成 DOM 构建过程。
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流(Reflow)
当 Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。现代浏览器会对频繁的回流或重绘操作进行优化:浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。
当你访问以下属性或方法时,浏览器会立刻清空队列:
因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。
避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
图片懒加载在一些图片密集型的网站中运用比较多,通过图片懒加载可以让一些不可视的图片不去加载,避免一次性加载过多的图片导致请求阻塞(浏览器一般对同一域名下的并发请求的连接数有限制),这样就可以提高网站的加载速度,提高用户体验。
将页面中的img标签src指向一张小图片或者src为空,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading的地址。注意,图片要指定宽高。
当载入页面时,先把可视区域内的img标签的data-src属性值负给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。
事件委托其实就是利用JS事件冒泡机制把原本需要绑定在子元素的响应事件(click、keydown……)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
优点:
例如有一个列表需要绑定点击事件,每一个列表项的点击都需要返回不同的结果。
传统写法:
传统方法会利用for循环遍历列表为每一个列表元素绑定点击事件,当列表中元素数量非常庞大时,需要绑定大量的点击事件,这种方式就会产生性能问题。这种情况下利用事件委托就能很好的解决这个问题。
改用事件委托:
输入搜索时,可以用防抖debounce等优化方式,减少http请求;
这里以滚动条事件举例:防抖函数 onscroll 结束时触发一次,延迟执行
节流函数:只允许一个函数在N秒内执行一次。滚动条调用接口时,可以用节流throttle等优化方式,减少http请求;
下面还是一个简单的滚动条事件节流函数:节流函数 onscroll 时,每隔一段时间触发一次,像水滴一样
参考链接: https://zhuanlan.hu.com/p/113864878?from_voters_page=true
B. 前端通用组件设计
调用组件库的API相信很多人都会用,但是如何封装一个高复用的组件并不是每个人都能做到,而这也是检验一个前端开发人员的一个标准。
说到开发组件,首先需要考虑一个问题,一个可复用的组件都需要具备哪些必要条件。
1. 细粒度考量
看过设计模式的朋友应该有了解过很多设计原则,其中一个就是 单一职责原则 ,这个原则放在开发组件中也同样适用。在原则上一个组件就只负责一件事,这就是单一原则所带来的好处也非常明显,就是可以更大可能的复用组件。但如果职责过于单一,也会造成组件碎片化严重,过于抽象。
因此我们需要考虑,所谓的单一职责原则是建立在可复用的基础上的。否则,可以做为独立组件的内部组件进行使用。
2. 组件通用性考量
组件设计之初是为了当时的页面设计进行封装设计的,那么之后的页面设计极大可能是与之前不同的,那么之前设计的组件就不能用了。
而一旦发生这样的情况,就说明我们之前所设计的组件是不通用的,需要重新设计了。就像Antd组件库那样,预留了dropdownRender进行组件渲染。
通用性的设计就代表着将放弃对DOM的操作权,暴露给开发者进行操作,组件开发者本身只负责底层逻辑和基本的DOM结构。这也是开发通用型组件的秘诀之一。
3. 技术选型
css存在着许多的弊端,例如样式易冲突(没有作用域的概念)、书写繁琐(不支持嵌套)、缺少变量(不便于一件更换主题)等等。而解决这些问题的方案也是层出不穷,从最早的css预处理,到后来的Postcss,再到后来的styled-compontent,各种方式任君选择。
4. 打包工具
产品的设计思想固然是核心,但是也需要一堆辅助工具来来帮助我们开发,例如编译工具、测试工具、打包工具。
说到打包工具,就不得不提一下如今非常火爆的,需要配置工程师专门配置的webpack了。但是他也有一个强大的竞争对手 rollup。
rollup更适合用于组件库的打包,优势如下:
设计一个轮播图组件
学以致用,学了就肯定要实践一下。轮播图是一个比较常见的组件,每个组件库中都封装的有,接下来我们也来介绍一下如何设计一个轮播图组件。
1. 轮播图原理
通常情况下我们使用轮播图是这样编写的
那么为什么放入了四个div盒子却只显示一个呢,这是因为可视区域是固定的,只需要移动div盒子就可以显示出后面的盒子,这样就达到了轮播的效果。
为了是观看效果更好,我们都会隐藏掉可视区域之外的内容,这样就是大家经常看到的轮播图了。组件就可以这样设计:
可以通过transform: translateX()不断改变SlideList的位置,就可以达到轮播的效果了。
2. 轮播图的基础实现
知道了基础原理就可以进行组件的实现了,这里以移动端轮播图为例。
首先,获取移动端可视窗口的宽度。
------- 未完待续 --------
C. 这种前端页面效果是如何实现的请大神指教。
用CSS3和JS,或者单用JS也可以,原理就是监测元素到窗口的距离,当元素距窗口达到指定距离时,添加或删除相应的CSS,让元素移动,当然,JS也可以
D. 手机前端页面尺寸
iPhone手机网页的设计尺寸
iPhone5尺寸是640x1136px分辨率是326PPI
iPhone4和iPhone4S尺寸是640x960px分辨率是326PPI
iPhone和iPodTouch第一代、第二代、第三代尺寸是320x480px分辨率是163PPI
安卓网页的设计尺寸
320dp:一个普通的手机屏幕(240X320,320×480,480X800)
480dp:一个中间平板电脑像(480×800)
600dp:7寸平板电脑(600×1024)
720dp:10寸平板电脑(720×1280,800×1280)
ipad网页的设计尺寸
iPad第三代第四代尺寸是2048x1536px分辨率是264PPI
iPad第一代第二代尺寸是1024x768px分辨率是132PPI
iPad Mini尺寸是1024x768px分辨率是163PPI
以上是移动端的尺寸,单独的如果某一个移动的网站的尺寸是没有的,因为移动网站的一半情况是按照%比的尺寸 来自动适应屏幕的大小,或者也通过设置,iphone执行什么尺寸,安卓情况执行什么尺寸的。
E. 手机前端页面尺寸
iPhone4/iPhone4s 320 * 372 / 320 * 441 (已隐藏URL与状态栏)
iPhone5/iPhone5s 320 * 460 / 320 * 529 (已隐藏URL与状态栏)
Note2 360 * 567 (未隐藏URL与状态栏)
iPad 3/4 768*928 (未隐藏URL与状态栏)
GALAXY SIII 360 * 567 (未隐藏URL与状态栏)
小米2A 360 *567 (未隐藏URL与状态栏)
HTC G10 320 * 460
<meta name="viewport"
网页手机wap2.0网页的head里加入下面这条元标签,在iPhone的浏览器中页面将以原始大小显示,并不允许缩放。
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0,
maximum-scale=1.0,
user-scalable=no">
width - viewport的宽度 height -
viewport的高度
initial-scale - 初始的缩放比例
minimum-scale -
允许用户缩放到的最小比例
maximum-scale -
允许用户缩放到的最大比例
user-scalable - 用户是否可以手动缩放
参考:
一、网页手机wap2.0网页的head里加入下面这条元标签,在iPhone的浏览器中页面将以原始大小显示,并不允许缩放。
<meta name="viewport"
content="width=device-width,
initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,
user-scalable=no">
其中: width - viewport的宽度 height - viewport的高度 initial-scale -
初始的缩放比例
minimum-scale - 允许用户缩放到的最小比例 maximum-scale - 允许用户缩放到的最大比例
user-scalable -
用户是否可以手动缩放c
二、关于meta的详细介绍请参考
三、下文是关于Meta的例子的详细介绍 原文地址
3. Meta元素可视区
默认情况下,iPhone上的Safari会象在大屏幕的
桌面浏览器那样显示你的页面,宽度达到了980像素,然后缩小内容以适应iPhone的小屏幕,因此用户在iPhone看这个页面时感觉字体就比较小了,
也比较模糊,必须要放大才能看得真切,对于一个普通的Web页面似乎可以接受,但对于一个常用的应用程序就没几个人能够受得了。
幸运的是可以使用特殊的Meta元素可视区进行纠正:
<meta
name="viewport" content="width=device-width"/>
这个元素通知浏览器使用设备的宽度作为可视区的宽度,而不是默认的980像素了,我们可以看看两个不同的例子。
例3(链接:
viewport.html)显示了一个简单的段落元素,没有Meta元素可视区,字体有点模糊。在iPhone中运行的实际情况如下图所示。
图 1 无可视区的显示效果
例4(链接:
/viewport.html)包括了一个可视区元素,现在设备宽度只有320像素,字体也比前一个例子更清晰了。在iPhone中运行的实际情况如下图
所示。
图 2 有可视区的显示效果
另外,你还可以手动设置device-width的值,例如,假设你的博客页面的宽度是750像素,那么桌面屏幕最佳大小就是800x600像素,例
5(链接:
/fixed750.html)显示了一个删减版本,如果你在iPhone中浏览它,你会看到980像素剩下的空间都填充了白色。
为了消除额外的空间,可以使用meta元素可视区将宽度设为780像素:
<meta
name="viewport" content="width=780"/>
例6(链接:/fixed750-viewport.html)显示了meta元素可视区布局被固定后的效果。
Meta元素可视区的内容可以包括多个逗号分隔的值,如initial-scale –
用户最初看到页面时的放大级别,对于Web应用程序,一个常见的设置是:
<meta
name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"/>
这个元素设置宽度为设备的最大宽度,禁止用户放大和缩小
F. JS如何获取页面可见区域高度
window.document.body.clientHeight就可以
window.screen.availWidth 返回当前屏幕宽度(空白空间)
window.screen.availHeight 返回当前屏幕高度(空白空间)
window.screen.width 返回当前屏幕宽度(分辨率值)
window.screen.height 返回当前屏幕高度(分辨率值)
window.document.body.offsetHeight; 返回当前网页高度
window.document.body.offsetWidth; 返回当前网页宽度
我们这里说说四种浏览器对 document.body 的 clientHeight、offsetHeight 和 scrollHeight 的解释。
这四种浏览器分别为IE(Internet Explorer)、NS(Netscape)、Opera、FF(FireFox)。
clientHeight
大家对 clientHeight 都没有什么异议,都认为是内容可视区域的高度,也就是说页面浏览器中可以看到内容的这个区域的高度,一般是最后一个工具条以下到状态栏以上的这个区域,与页面内容无关。
offsetHeight
IE、Opera 认为 offsetHeight = clientHeight + 滚动条 + 边框。
NS、FF 认为 offsetHeight 是网页内容实际高度,可以小于 clientHeight。
scrollHeight
IE、Opera 认为 scrollHeight 是网页内容实际高度,可以小于 clientHeight。
NS、FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。
简单地说
clientHeight 就是透过浏览器看内容的这个区域高度。
NS、FF 认为 offsetHeight 和 scrollHeight 都是网页内容高度,只不过当网页内容高度小于等于 clientHeight 时,scrollHeight 的值是 clientHeight,而 offsetHeight 可以小于 clientHeight。
IE、Opera 认为 offsetHeight 是可视区域 clientHeight 滚动条加边框。scrollHeight 则是网页内容实际高度。
同理
clientWidth、offsetWidth 和 scrollWidth 的解释与上面相同,只是把高度换成宽度即可。
=======================================================================
clientHeight与offsetHeight的区别
许多文章已经介绍了clientHeight和offsetHeight的区别,就是clientHeight的值不包括scrollbar的高度,而offsetHeight的值包括了scrollbar的高度。然而,clientHeight和offsetHeight的值到底由什么组成的呢?如何计算这两个数的值?
1. clientHeight和offsetHeight的值由什么决定?
假如我们有以下的DIV,主要显示的文字为"This is the main body of DIV"。
如上图所示,clientHeight的值由DIV内容的实际高度和CSS中的padding值决定,而offsetHeight的值由DIV内容的实际高度,CSS中的padding值,scrollbar的高度和DIV的border值决定;至于CSS中的margin值,则不会影响clientHeight和offsetHeight的值。
2. CSS中的Height值对clientHeight和offsetHeight有什么影响?
首先,我们看一下CSS中Height定义的是什么的高度。如在本文最后部分“APPENDIX示例代码”(注:以下称为“示例代码”)中,innerDIVClass的Height值设定为50px,在IE下计算出来的值如下所示。也就是说,在IE里面,CSS中的Height值定义了DIV包括padding在内的高度(即offsetHeight的值);在Firefox里面,CSS中的Height值只定义的DIV实际内容的高度,padding并没有包括在这个值里面(70 = 50 + 10 * 2)。
in IE:
innerDiv.clientHeight: 46
innerDiv.offsetHeight: 50
outerDiv.clientHeight: 0
outerDiv.offsetHeight: 264
in Firfox:
innerDiv.clientHeight: 70
innerDiv.offsetHeight: 74
outerDiv.clientHeight: 348
outerDiv.offsetHeight: 362
在上面的示例中,也许你会很奇怪,为什么在IE里面outerDiv.clientHeight的值为0。那是因为示例代码中,没有定义outerDIVClass的Height值,这时,在IE里面,则clientHeight的值是无法计算的。同样,在示例代码中,如果将innerDIVClass中的Height值去年,则innerDIV.clientHeight的值也为0。(注:在Firefox下不存在这种情况)。
如果CSS中Height值小于DIV要显示内容的高度的时候呢(当CSS中没有定义overflow的行为时)?在IE里面,整个clientHeight(或者offsetHeight)的值并没有影响,DIV会自动被撑大;而在Firefox里面,DIV是不会被撑开的。如在示例代码中,将innerDivClass的Height值设为0,则计算结果如下所示。IE里面的DIV被撑开,其clientHeight值等于内容的高度与padding*2的和;而Firefox里面,文字将溢出DIV的边界,其clientHeight值正好是padding值的两倍。
In IE:
innerDiv.clientHeight: 38
innerDiv.offsetHeight: 42
outerDiv.clientHeight: 0
outerDiv.offsetHeight: 256
In Firefox:
innerDiv.clientHeight: 20
innerDiv.offsetHeight: 24
outerDiv.clientHeight: 298
outerDiv.offsetHeight: 312
G. 前端优化-LCP
LCP是最大内容绘制的简称。LCP是用来测量感知加载速度。感知加载速度是以用户为中心的重要指标。因为该项指标会在页面的主要内容基本加载完成时,在页面加载时间轴中标记出相应的点,迅捷的LCP有助于让用户确信页面时有效的。以前的指标测量比如load(加载)或者DOMContentLoad(DOM内容加载完毕)并不是很好,因为这些指标并不一定与用户看到的内容相对应。而向First Contentful Paint 首次内容的绘制(FCP)这类以用户为中心的新指标只会捕获加载最开始的部分。如果某个页面显示时一段启动动画或者加载之时,那么这些时刻与用户的关联性并不大。First Meaningful Paint 首次有效绘制(FMP)和Speed Index速度指数(SI),这些指标能够捕获到更多初始后的加载速度,但是这些指标复杂,难以理解,而且容易出错。
LCP指标会根据页面首次开始加载的时间点来报告可视区域可见的最大图像或者文本完成渲染的相对时间.良好的LCP的时间时2.5,较差的值为4.0s.最大内容的绘制考量的元素类型为
报告给最大内容绘制的元素大小通常时用户在可视区域可见的大小,如果元素延伸到可视区域之外,或者任何元素被剪裁或者包含不可见的溢出,那么这部分不计入元素的带线啊哦。对于在原始尺寸之上经过调整的图像元素,报告给指标的元素大小为可见尺寸或者原始尺寸。
网页通常时分阶段加载的,因此,页面上最大元素也可能会发生变化。为了应对这种潜在的变化,浏览器会绘制第一帧立即发送一个largest-contentful-paint类型的PerformanceEntry,用于识别最大内容元素,但是,在渲染后续帧之后,浏览器会在最大内容元素发生变化时候分发另一个PerfornacneFanceEntry。需要主要的是,一个元素只能在渲染完成并且对用户可见之后才视为最大元素,尚未加载点额元素不能视为渲染完成。在字体阻塞期使用网页字体的文本节点也是这样。在这种情况下,较小的元素可能会被报告称最大元素,但一旦更大的元素完成渲染,就会通过另外一个PerformanceEntry对象进行报告
为了使计算和分发新性能条目的性能开销保持在较低的水平,对元素大小或者位置的更改不会生成新的LCP候选对象,只有元素在可视区域的初始化大小和位置会纳入考量范围。也就是说,最初在屏幕外完成渲染,然后过滤到屏幕上的图像可能不会得到报告,这也意味着最初可视区域内进行渲染,然后被推出到可视区域外的元素人讲报告其在可视区域的初始化大小
LCP主要受四个因素影响