本文主要是介绍掘金小册《前端性能优化原理与实践》读书笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前端性能优化:
以下文章内容是根据掘金小册《前端性能优化原理与实践》整理的
网络层面的优化
- 1、减少请求次数;
- 2、减少单次请求花费的时间;
webpack相关优化;
- 1、按需加载关键:require.ensure(dependencies, callback, chunkName)
- 2、Gzip压缩原理:在一个文本文件中找出一些重复出现的字符串、临时替换它们,从而使整个文件变小。根据这个原理,文件中代码的重复率越高,那么压缩的效率就越高,使用 Gzip 的收益也就越大。反之亦然。
图片优化
1、常用的图片格式介绍:
- 1)jpg图片:有损压缩、体积小、加载快、不支持透明
- 常用于色彩丰富的图片;经常作为大的banner图,背景图,轮播图;既可以保住图片质量,又降低了图片体积;
(把图片体积压缩至原有体积的 50% 以下时,JPG 仍然可以保持住 60% 的品质) - 缺点:不支持透明度处理;
- 常用于色彩丰富的图片;经常作为大的banner图,背景图,轮播图;既可以保住图片质量,又降低了图片体积;
- 2)PNG-8,PNG-24:无损压缩,质量高,体积大,支持透明;
- 常用于呈现小的logo,颜色简单且对比强烈的图片或背景等;
- 3) SVG:体积小,不失真,兼容性好,文本文件;
- 缺点:渲染成本较高;因为是文本文件,可编程所以存在学习成本;
- 在线阿里矢量图形库
- 4)base64:文本文件,依赖编码,小图标解决方案;
-
base64并非是一种图片格式,而是一种编码方式;和雪碧图一样,作为小图标的解决方案而存在;
-
减少加载网页图片时对服务器的请求次数;作为雪碧图的补充方式而存在;
-
Base64 是一种用于传输 8Bit 字节码的编码方式,通过对图片进行 Base64 编码,可以直接将编码结果写入 HTML 或者写入 CSS,从而减少HTTP 请求的次数
-
雪碧图(CSS Sprites)介绍:
- 一种将小图标和背景图像合并到一张图片上,然后利用 CSS 的背景定位来显示其中的每一部分的技术
-
为什么大图片不使用base64?
- 因为经过base64编码后,图片大小会膨胀为原文件的4/3;(由base64编码原理决定);所以大文件编码后直接放在html/css文件中,体积明显增大,与节省的http请求开销相比不划算,所以不这么干;
-
base64使用场景?
- 图片尺寸很小,(如<2kb);
- 图片无法以雪碧图的形式与其它小图结合(优先使用雪碧图减少http请求,base64作为补充方案;)
- 图片的更新频率较低;(不需要我们重复编码或修改文件内容,维护成本低)
-
- 5)webp:2010年被提出,集多种图片格式的优点于一身;
- 缺点:新生事物,兼容性一般;增加服务器负担,占用更多的计算资源;
- 使用场景:由于兼容性差,所以使用时需考虑不支持webp格式浏览器(如safari)中的显示方案;
浏览器缓存机制介绍和缓存策略剖析
- 1)http缓存;分为强缓存,协商缓存;
- 2)memory cache:内存中的缓存;是浏览器最先尝试的一种缓存,响应速度最快;和渲染进程生死相依,tab关闭,缓存就不在了;
- 3)service wroker cache(离线缓存):
- service worker:独立于主线程之外的JS线程,脱离浏览器窗口,因此无法直接访问DOM; 可实现离线缓存,消息推送,网络代理等功能;
- service wroker 必须以https协议为前提
- 4) Push Cache:http2在server push阶段存在的缓存;
- 更多内容查看博客:https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/
本地存储;Cookie,Web Storage,indexDB
- 1)Cookie: 本职工作并非本地存储,而是维持状态;(因为http是无状态协议,服务端在接收到客户端的请求返回响应后就结束了,不会记录客户端的相关信息;cookie可携带用户信息在客户端和服务端之间飞来飞去)
- 只能存储少量信息,上限为 4kb;
- Cookie紧跟域名,同一个域名下的所有请求,都会携带Cookie值;
- 2)Web Storage,分为LocalStorage,SessionStorage,
- localStorage和sessionStorage的区别??
- 生命周期
- localStorage:持久化的本地存储,存储在其中的数据永不会过期,除非手动删除;
- sessionStorage:临时性的本地存储,会话级,会话结束(即浏览器窗口关闭)存储的内容也随之清空;
- 作用域
- 都遵守同源策略,但sessionStorage即便是在同一个域名下的两个页面,只要是在不同的浏览器窗口打开,那么它们存储的内容就不共享;
- 生命周期
- 存储上限根据浏览器的不同,容量可达5M-10M,通常不超过5M;
- 只存在于浏览器端,不与服务端进行通信;
- 只能存储字符串;
3)indexDB:运行在浏览器上的非关系型数据库,理论上说,没有存储上限(一般不小于250M),不仅可存储字符串,还可存储二进制数据;
- localStorage和sessionStorage的区别??
CDN的缓存与回源机制解析
-
CDN:(内容分发网络)指的是一组分布在各个地区的服务器;这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户的距离最近,来满足数据的请求;(提供高速服务,较少受到高流量影响)
- 提升首次请求的响应力,
- 核心功能点:缓存,回源
- 缓存:把资源copy一份到CDN服务器的过程;
- 回源:CDN服务器上没有这个资源(一般是缓存的资源过期了),转头向根服务器(或它上一级服务器)去要这个资源的过程
- 静态资源走CDN,可提高加载速度;(在大公司中是默认规定)
- 以淘宝网首页为例,业务服务器和CDN服务器的域名是不同的,这样就可以避免加载CDN服务器上的静态资源时也需要将Cookie传来传去,节省开销;(因为同一个域名下所有的请求都会携带该域名下的Cookie值;)
- 怎样在浏览器中查看该请求是否是从CDN服务器上获取的资源??
- 在响应头中查看是否有以下字段:
- X-Cache:CDN是否支持缓存;
- X-Swift-CacheTime:资源在cdn上的可以缓存多久,即资源在CDN上缓存的总时间;
- X-Swift-SaveTime:资源在是什么时间缓存到CDN节点的;(时间点)
- Age: 文件资源在CDN上缓存的时间,(单位是秒),如果值为0说明缓存的资源过期了,需要回源;
- MISS:CDN节点上无该文件的缓存,回源请求。
- HIT:表示已缓存。
- 在响应头中查看是否有以下字段:
-
浏览器缓存,本地缓存解决的都是第二次请求时响应慢的问题,对于首次请求这些招式是无效的;
服务端渲染的探索与实践
- 1)运行机制:当用户第一次请求页面时,服务器把需要的页面或组件渲染成html字符串,然后把它返回给客户端;客户端接收到响应内容后可以直接渲染然后呈现给用户的html内容,不需要为了生成dom内容再去跑一遍JS代码;
- 2)解决了什么问题:SEO搜索不到;首拼加载速度过慢;
- 3)应用场景:
- 因为用户客户端数量可以有很多,但是服务器资源是稀少宝贵的,所以把很多台浏览器的渲染压力集中起来,分散给相比之下数量并不多的服务器,服务器肯定是承受不住的;
- 先用低成本的首屏渲染和SEO的优化方案,除非网页对性能要求太高,以至于所有招式都用完了,性能还是不满意,这时可以考虑申请多台服务器,使用服务端渲染;
浏览器背后的运行机制
1、浏览器内核
- 1)浏览器的心即浏览器的内核;不同浏览器的差异性正是因为内核不同而导致的,内核决定了浏览器解析网页语法的方式;
- 2)内核可分为两部分:渲染引擎(HTML解析器,CSS解析器,布局,网络,存储,图形,音视频,图片解码器等零部件) + JS引擎;
- a、HTML解析器:将html文档经过词法分析输出浏览器可以理解的DOM树;
- b、CSS解析器:解析CSS文档,生成规则样式,输出CSSOM树;(计算dom数中每个节点的具体样式)
- 开始解析html后,解析到link标签或者style标签时,CSSOM的构建才开始,(CSS解析和DOM的解析过程是并行的;)
- c、图层布局计算模块:布局计算每个对象的精确位置和大小;(计算dom树中可见节点的几何位置信息,生成可见元素的布局树)
- d、视图绘制模块:进行具体节点的图像绘制,将像素渲染到屏幕上;
- e、JS引擎:编译执行JS代码;
- 3)常见的浏览器内核:Trident(IE),Gecko(火狐),Blink(Chrome,Opera),Webkit(Safari)
- Chrome2013年以前使用的是Webkit内核;13年以后使用的是Blink内核;(但Blink其实也是基于Webkit衍生来的一个分支)
2、浏览器的渲染过程
- 1)渲染过程:渲染引擎根据html的文件描述构建相应的数学模型,调用浏览器的各个零部件,将网页资源代码转为图像结构;
- 2)渲染过程中性能提升的方案:
- CSS选择器书写习惯;
- a、避免使用通配符*,只对用到的元素进行选择;
- b、少用标签选择器,如果可以使用class选择器代替;
- c、id和class选择器不应该被多余的标签选择器拖后腿;
- d、减少嵌套;
- 尽早(将CSS放在head标签中)尽快(使用CDN实现静态资源加载)的加载CSS资源,因为CSS是阻塞渲染的资源;
- JS的执行会阻塞CSS和html的解析(因为JS引擎是独立于渲染引擎存在的,在解析html的过程中,当遇到了script标签就会暂停渲染过程,将控制权交给JS引擎,JS引擎对外部的JS需要先下载再执行对内联的JS直接执行,等JS引擎执行完毕会将控制权交给渲染引擎,继续CSSOM和DOM的构建;)
- JS的加载方式:
- 正常模式(会阻塞浏览器)
- 异步加载模式(async,defer):不会阻塞浏览器做其他的事情,
- 当脚本文件与DOM元素和其他脚本之间依赖关系不强时可选用async;当脚本文件依赖DOM元素和其他脚本的执行结果时选用defer;
- JS的加载方式:
- CSS选择器书写习惯;
DOM优化原理与基本实践;
- 为什么DOM慢?
- 1)在JS世界里一切都是简单快速的,但是DOM操作是两个模块间的协作,JS引擎和渲染引擎"跨界交流"时,会依赖桥接接口作为桥梁,产生开销;(“收过桥费”)
- 2)JS对DOM的修改会引发样式更迭(重排即回流,重绘)
- 如何使DOM变快?
- 1)使用缓存变量对访问的dom节点进行缓存(少交过桥费)
- 2)减少不必要的DOM更改,可将插入节点的dom内容先准备好,然后一次性插入父节点中(减少回流/重绘)
- 3)使用Dom Fragment缓存批量化的DOM操作;(Dom Fragment本质是脱离真实DOM树的容器,它的变化不会触发dom树的重新渲染,且不会对性能产生影响)
事件循环与异步更新策略
- 事件循环的执行顺序:宏任务-微任务-UI渲染;(当我们需要在异步任务中实现DOM修改时,把它包装成微任务是较好的;)
- 常见的宏任务:setImmediate,setInterval,setTimeout,script(整体代码)
- 常见的微任务:process.nextTick,Promise,MutationObserver
- 异步更新可以避免过度渲染;
回流与重绘
- 1)基本概念:
- 回流:对dom的修改引起元素几何尺寸的变化(比如修改元素的宽,高或隐藏元素等)
- 重绘:对dom的修改导致了样式的变化,但是并不影响其几何属性;(比如修改颜色,背景色,可见性(visibility: hidden) 等)
- (重绘不一定导致回流,但是回流一定会导致重绘)
- 2)什么情况下会触发回流?
- a、改变DOM的几何树形;
- b、改变dom树的结构,(即对节点的增减,移动等操作)
- c、获取一些特定的属性值,因为这些值需要通过即时计算得到,所以浏览器为了准备,即时的拿到这些值,也会触发回流操作;
- 如:offsetTop,offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
- 3)如何避免回流?
- a、使用JS变量将节点缓存起来,避免频繁改动;
- b、避免逐条修改样式,使用类名合并样式;
- c、将DOM离线;(即对dom进行操作前,先将dom节点display: none掉,然后再将其display: block出来)
// 不推荐const container = document.getElementById('container')container.style.width = '100px' // 回流container.style.height = '200px' // 回流container.style.border = '10px solid red' // 回流container.style.color = 'red' // 重绘// 推荐.container_box {width: 100px;height: 200px;border: 10px solid red;color: red}<script>const container = document.getElementById('container')container.classList.add('container_box')</script>
优化首屏体验——Lazy-Load初探
- 懒加载(延迟加载):针对图片加载时机的优化,在页面打开时只把首屏的图片资源加载出来,等用户下拉的瞬间再即时去请求下面的图片;
// 图片懒加载,即只显示可视屏幕的图片let imgs = document.getElementsByTagName('img')let count = 0// 获取可视屏幕的高度const clientHg = window.innerHeight || document.documentElement.clientHeight// 定义懒加载函数function lazyFn() {for(let i=count; i<imgs.length;i++) {// i从count开始,可以避免查找已经渲染了真实图片的img节点let distance = clientHg - imgs[i].getBoundingClientRect().topif (distance >= 0) {// 如果可视区域的高度 >= 元素顶部距离可视区域顶部的高度时,说明元素露出imgs[i].src = imgs[i].getAttribute('data-src')count++}}}window.onload = () => {// 页面加载完成先执行一次lazyFn()}window.addEventListener('scroll', lazyFn, false)
事件节流(throttle)与事件防抖(debounce)
- throttle: “第一个人说了算”;在一定的时间范围内,只执行一次事件回调,如果在这段时间内,事件被触发了则直接忽略;
- debounce: “最后一个人说了算”,在某段时间内,不管触发了多少次回调,都只认最后一次;如果在这段时间内,时间被再次触发了,则重新开始计时;
// 使用throttle优化debounce
// fn是我们需要包装的事件回调, delay是时间间隔的阈值
function throttle(fn, delay) {// last为上一次触发回调的时间, timer是定时器let last = 0, timer = null// 将throttle处理结果当作函数返回return function () { // 保留调用时的this上下文let context = this// 保留调用时传入的参数let args = arguments// 记录本次触发回调的时间let now = +new Date()// 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值if (now - last < delay) {// 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器clearTimeout(timer)timer = setTimeout(function () {last = nowfn.apply(context, args)}, delay)} else {// 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应last = nowfn.apply(context, args)}}
}
性能检测: Performance 和 LightHouse
- Chrome浏览器中performance性能面板;
- Performance API对象:可以获取当前页面与性能相关的信息;
- Performance 官方文档
- 使用 Lighthouse 审查网络应用
- MDN Performance API 介绍
前端内存泄漏排查——Chrome浏览器的Performance面板使用
- 前端性能优化(一)内存泄漏排查之Chorme浏览器的Performance使用
- 内存泄漏:指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行缓慢甚至崩溃等后果;
这篇关于掘金小册《前端性能优化原理与实践》读书笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!