性能优化几乎是面试的时候非常高频的问题,因此总结了这么几条特别常见的优化方案送给大家。本文专业词汇较多,基础或者接触比较少的同学可能概念理解起来比较复杂。但还是请硬着头皮把这些概念搞懂,因为特别高频。
1.重绘和重排(回流)
描述
当元素的位置或尺寸发生变化的时候会导致页面的重排,当元素的信息改变但是不改变位置和尺寸的时候会发生重绘。重绘不一定重排,但是重排一定会发生重绘。而且重排耗费的资源要比重绘大的多
理解为什么重排和重绘会提高性能?
打个比方,一个静态网页。没有任何交互,从浏览器渲染出来之后就不需要改变了,是不是浏览器对这个页面的计算量就特别小。
假如这是一个动态页面呢,上面有个轮播图。那是不是需要浏览器事实的进行新的页面绘制工作,那必然是需要消耗资源的
知道了重排和重绘我可以这样提高性能
1.减少重绘重排的次数
常见解决方式1:
有的时候需要往 UI 标签中添加多个 LI 标签。这个时候不要一个一个的添加 LI 标签。而是在本地创建好了 LI 标签组一次性加载进去
常见解决方式2:
将元素属性变为 none,然后在对其进行修改工作
2.能重绘别重排
给元素设置某些属性的时候元素会脱离文档流,这样元素的改变就不会造成大面积的重排了,例如绝对定位,浏览器定位,使用 CSS3 的某些特性。可以参考这个网站查看具体哪些属性会导致重排重绘csstriggers.com/
我可以这样知道页面哪些地方重绘重排严重
通常性能优化都不是有遇见性的刚开始就对其进行了优化,而是项目遇到性能瓶颈了在对其进行优化,所以知道哪些地方进行了大量的重绘重排是极其重要的。
打开谷歌控制台 -> More tools -> Rendering -> 选中 Paint flashing
2.防抖和节流
描述
防抖:法师发技能的时候要读条,技能读条没完再按技能就会重新读条。
节流:法师普攻是需要时间的。不管点的多快,一秒钟攻击的次数是有限制的。
实用场景
搜索框搜索内容时请求后端接口
不防抖的方式:输入内容:12345 , 内容发生改变就请求接口。这样会请求五次接口
防抖的方式:同样输入内容:12345,防抖时间设置为 500ms,500ms 内用户输入新的内容重新读条。正常情况下只会请求一次接口
图片进行懒加载
不节流的方式:滚动条一滚动就监听页面元素显示情况,显示的元素就进行图片加载。滚动条滚动事件是一个非常频繁的操作,滚动一丢丢就会执行几十次。
节流的方式:还是同样的操作,但是执行代码进行节流。设置节流时间 30ms 。这就不管这 30ms 滚动事件出发了几次,我的执行代码都只执行一次
3.CSS 选择器优化
描述
.parent .children p span {}
复制代码
css 的匹配规则是从右往左,而不是从左往右的。道理也很简单,从右往左是孩子找父亲,找曾祖父,从左往右是父亲找孩子,找孙子。因为一个孩子只有一个父亲,所以只要往上一找发现他父亲不符合条件,就说名这个孩子规则不匹配就可以排除掉。反过来就麻烦了,一个父亲有多个孩子,这个孩子不是,换下一个孩子。要排除掉一个父亲是非常麻烦的,需要多次来回寻找
优化方法
最右侧命名的唯一性
描述说了匹配规则是从右往左匹配,也就是说最右侧的名字唯一性越强,首次符合要求的元素也就越少,需要查找的次数就少。你如果用 * 那完蛋了,所有的元素都要跑一边,不仅仅是 * 。像标签名的匹配方式能少用就少用
减少层级
虽然多世同堂是一件蛮幸福的事情,老者可以看到自己的孙子,曾孙子。。。但是这在代码中确是比较费事的事情。因此少的嵌套会更快速的匹配
4.关于文件的优化
描述
文件的优化需要考虑数量、大小。换言之文件越小、数量越少加载的顺序是更快的。
优化
减少代码体积
最常见的几种方式就是,对代码进行压缩、重复代码抽取成公共代码,但是这里特别强调一下代码压缩。现在浏览器支持 Gzip 压缩格式。他是在的将空格、变量名之类的压缩了之后还能压缩的一种格式。
减少文件数量
同样 1M 的大小的文件。一个拆分成了 5份,一个拆分成了 100 份。5份的是会优先于 100 份的加载出来的,因为这一块牵扯到了浏览器策略和 http 协议。
- http 协议:100 份文件会请求 100 次。http 协议请求也是包含内容的,像请求头,响应头。这个内容 * 100 也是很大的
- 浏览器策略:同一个域名下面并发请求的个数是有限制的,根据浏览器的不同这个数量也不固定,100 份文件会请求 100 次。但是他肯定不是同时请求的
谈一谈典型场景
图标合并成一个。下面是一张图片带有六个图表,这样使用图标的时候请求一次就可以了。
5.使用 CDN 进行优化
描述
CDN的全称是Content Delivery Network,即内容分发网络。他简单说有俩个优势:
- 他在很多地区有服务器,用户下载文件的时候可以就近下载。比如你在深圳,你可以直接用深圳的服务器下载文件。而不用千里迢迢的从北京下载文件。专业术语解释就是:中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率
- 上文提到浏览器对于一个域名并发请求有数量限制,放到 CDN 上他就是另外一个域名。跟公司本身的域名不冲突就可以提高并发数量
6.利用好存储技术
描述
首先明确一点是啥能存储,我这里想到了三个点:文件存储、请求存储、计算存储
文件存储
一个 web 项目是由很多的 CSS、JS、HTML 文件组成的,这些文件基本上只有在版本迭代的时候才会有改动,因此将他们存储在本地对性能的提升是很有帮助的
请求存储
存在交互的项目请求必然分为俩种,稳定数据请求和非稳定数据请求。稳定数据请求存储起来可以减少 ajax 交互。当然了稳定跟不稳定之间的界限是不好界定的,因此需要你自己去进行判定是永久存储还是短暂存储,比较有代表性的方法就是 localStorage 和 sessionStorage
计算存储
很多数据并不是拿来就能用的,都需要进行处理。像分跟元之间单位换算、求和、求平均值。其实数据计算存在这么一种情况,计算的过程复杂,但是结果可以用多次。就可以存储起来。这里没有上面俩种方式通俗,举个简单例子:
function storageFn () {
let obj = {}
return function (val) {
if (obj[val]) {
return obj[val]
} else {
// 大量计算,假设结果为 1
obj[val] = 1
return obj[val]
}
}
}
let a = storageFn()
a('b')
复制代码
从上面的例子可以看出来如果是已经计算过得结果可以不需要计算直接用,这就是计算存储。(这段代码看不懂的可以了解一下闭包)
7.冒泡和捕获
描述
界面元素都是一层套一层的,当你给元素添加事件的时候,比如单击事件。那么到底是单击的哪个元素呢?针对这个问题就有了冒泡和捕获俩种选择:
- 冒泡,从单击的元素往外扩散,这里也可以设置 event.stopPropagation()阻止事件冒泡
- 捕获,从根元素一直寻到目标元素
利用这个如何进行优化? -> 事件代理
给元素添加事件是很耗费资源的事情,普通情况下也不会大量添加事件。但是当使用到列表的时候可能就会跟很多重复的元素循环添加事件了。其实你可以利用冒泡的特性只给父元素添加事件。这样不管你点了哪个子元素最终父元素都会接收到,最后用 event.target 来确认目标元素就可以了。
8.SPA 开发首屏优化
将一些依赖放到 CDN 上
SPA 打包的文件很大就会造成首屏加载过慢,如果把常用的一些第三方包单独拿出来放到 CDN 上就会大大提高首屏速度。例如 UI 框架, echarts 库。这里面单独拿出来放到 index.html 里面并不复杂,复杂的是你有可能项目检查的时候缺少某个变量报错
非首屏内容懒加载
这个你看着懒加载好像十分复杂一样,其实就是一个语法球。掌握这个语法球就可以轻松实现懒加载了。
终极方案
首页不放到 SPA 应用里面,单独写一个 html 页面。这个我刚开始做的时候感觉比较麻烦的是因为 VUE 全家桶给提供了开箱即用的脚手架,所以我竟然不知道如何单独使用 CSS 预加载,现学习这个花了一点时间。
总结
自古评论出人才,个人的知识毕竟是有限的,希望你能够评论留言补充。如果觉得评论交流不过瘾可以扫描下面的公众号二维码加群交流讨论