做项目总是会遇到性能优化的要求,今天抽时间学习一下,并总结一下可以从哪几个方面入手,怎么去做好优化?这能帮助我们更好的完善项目。
主要可以从三个大方面来入手,三个大方面又可以分为多个小方面。
JS编写优化
1 、编写符合规范的js代码
-
尽量避免使用全局变量
全局变量会导致一些可维护性难题,如命名冲突、测试困难 -
不要在函数内进行过深的嵌套判断
-
当需要多次访问数组长度时,可以用变量保存起来,避免每次都会去进行属性查找
-
尽量减少使用闭包
常驻内存中,会增大内存的使用量,使用不当会造成内存泄露;另外js对闭包的处理速度会低于普通函 数,过度使用闭包也会降低脚本性能。
-
缓存DOM节点访问
2 、延迟加载js
-
js加载的几种方式
1、同步加载 会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作。 2、异步加载 又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。 3、异步加载方法 在 <script> 标签中加上defer或async属性。但defer 和async还是有区别的。 如果多个 script 设置了 defer 属性,这几个 script 的执行顺序和声明顺序相同,即最前面的 脚本先执行。async的执行,并不会按着script在页面中的顺序来执行,而是谁先加载完谁执行。 也就是说,async的执行是加载完成就会去执行,而不像defer那样要等待所有的脚本加载完后按照顺序执行。
-
js 延迟加载的方式有哪些?
js 延迟加载,也就是等页面加载完成之后再加载 JavaScript 文件。 js 延迟加载有助于提高页面加载速度。1、将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。 2、给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步执行,然后在文档解析完成 后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来 说是顺序执行的,但是在一些浏览器中可能不是这样。 3、第三种方式是给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程, 当脚本加载完成后立即执行 js 脚本。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照 代码的顺序依次执行。 4、第四种方式是动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后 再动态的创建 script 标签来引入 js 脚本。
-
什么时候用defer,什么时候用async?
两者之间的选择则是看脚本之间是否有依赖关系,有依赖的话应当要保证执行顺序,应当 使用defer没有依赖的话使用async,同时使用的话defer失效。
3 、使用节流和防抖
- 函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
- 函数节流:使得一定时间内触发一次函数。原理是通过判断是否到达一定时间来触发函数。
- 区别:函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。而函数防抖只是在最后一次事件后才触发一次函数
- 防抖的应用场景:
window触发resize的时候和鼠标滚动触发scroll的时候,不断的调整浏览器窗口大小页面不断的滚动会不断的触发这个事件,用防抖来让其只触发一次。
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好) - 函数节流的应用场景:
鼠标不断点击触发,mousedown(单位时间内只触发一次),
监听计算滚动条的位置,使用节流按一定时间的频率获取。
浏览器的渲染机制原理进行优化
4 、css样式表放在顶部
- 浏览器的渲染过程
解析 HTML 构建 DOM(DOM 树),并行请求 css/image/js;
CSS 文件下载完成,开始构建 CSSOM(CSS 树);
CSSOM 构建结束后,和 DOM 一起生成 Render Tree(渲染树)
布局(Layout):计算出每个节点在屏幕中的位置
显示(Painting):通过显卡把页面画到屏幕上 - CSS会阻塞DOM解析吗?
对于一个HTML文档来说,不管是内联还是外链的css,都会阻碍后续的dom渲染,但是不会阻碍后续dom的解析。
当css文件放在中时,虽然css解析也会阻塞后续dom的渲染,但是在解析css的同时也在解析dom,所以等到css解析完毕就会逐步的渲染页面了。
网页上的资源加载时从上网下顺序加载的,所以css放在页面的顶部能够优先渲染页面,让用户感觉页面加载很快。
5 、减少重绘repaint和重排reflow
- 重绘(repaint或redraw):当渲染树中的元素外观(如:颜色)发生改变,不影响布局时,产生重绘。
触发重绘的条件:改变元素外观属性。如:color,background-color等。 - 重排(重构/回流/reflow):当渲染树中的元素的布局(如:尺寸、位置、隐藏/状态状态)发生改变时,产生回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
JS 获取 Layout 属性值(如:offsetLeft、scrollTop、getComputedStyle 等)也会引起回流。因为浏览器需要通过回流计算最新值。
重绘和重排的关系:所以,重排必定会引发重绘,但重绘不一定会引发重排。
- 如何最小化重绘(repaint)和回流(reflow)?
避免逐条改变样式,使用类名去合并样式;
避免用 table 布局(table 元素一旦触发回流就会导致 table 里所有的其它元素回流)
需要创建多个 DOM 节点时,使用 DocumentFragment 创建完后一次性的加入 document,或使用字符串拼接方式构建好对应HTML后再使用innerHTML来修改页面
缓存 Layout 属性值,如:var left = elem.offsetLeft; 这样,多次使用 left 只产生一次回流
避免使用 css 表达式(expression),因为每次调用都会重新计算值(包括加载页面),影响页面性能
6、优化文件大小
- HTML和CSS的加载和解析都会阻塞渲染树的生成,从而影响首屏展示速度,因此我们可以通过优化文件大小、减少CSS文件层级的方法来加快首屏速度
网络优化
7、合并资源文件,减少HTTP请求
- 浏览器并发的HTTP请求是有数量限制的(比如桌面浏览器并发请求可能是8个,手机浏览器是6个),如果一下子并发的几十个请求那么会有很多请求会停下来等,等前面的请求好了下一个再进去,这样就延长了整个页面的加载时间
8、压缩资源文件减小请求大小
- 可对代码进行压缩,去掉空格、注释、变量替换,在传输时,使用gzip等压缩方式也可以降低资源文件的大小。(Gzip可以减少60%-80%的文件大小,只要在服务器上配置一个选项即可比较简单)
9、避免HTTP重定向
- HTTP重定向极其耗时,特别是把客户端定向到一个完全不同的域名的情况下,还会导致额外的DNS查询、TCP握手,还有其他延迟
10、使用CDN
- 从地理上把数据放到接近客户端的地方可以显著减少每次TCP连接的网络延迟,增加吞吐量
11、预加载
- 在开发中,可能会遇到这样的情况。有些资源不需要马上用到,但是希望尽早获取,这时候就可以使用预加载。预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。预加载其实是声明式的 fetch ,强制浏览器请求资源,并且不会阻塞 onload 事件,可以使用以下代码开启预加载。
<link rel="preload" href="http://example.com">
- 什么是按需加载?
当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是JS、图片、CSS、HTML等。
12、图片优化
- 将多个图标文件整合到一张图中(雪碧图/精灵图)
优点:减少HTTP的请求次数,提高加载的性能 - 懒加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:(1)图片进入可视区域之后请求资源,对于电商,页面很长的场景,减少无效的资源加载(单页应用,观看的时候才去加载),(2)并发加载的资源过多会影响网站的正常使用。
具体实现懒加载:图片的src的地址被改变的时候被加载。监听滑动的事件,当图片进入可视区域的时候才去加载。 - 小图标等用iconfont代替
作为单个DOM节点使用,可以设置大小、颜色等,非常便利。\
总结:
前端优化主要可分为:JS优化、浏览器渲染优化、网络请求优化这三个部分,三个部分下面又有各自的许多优化方法,从三个大方向入手更有利于记忆。