前端性能优化总结(一)-js、css优化

JavaScript028

前端性能优化总结(一)-js、css优化,第1张

移动互联网时代,用户对于网页的打开速度要求越来越高。首屏作为直面用户的第一屏,其重要性不言而喻。优化用户体验更是我们前端开发非常需要 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.zhihu.com/p/113864878?from_voters_page=true

最近自己做一些threejs项目,总结了一些性能优化的关键点,跟大家分享一下。

1、尽量使用clone方法;

2、不需要的时候一定要dispose;

3、优先使用BufferGeometry;

4、注意图片压缩;

5、重点优化requestAnimationFrame内的方法(描述一);

6、如果有大量外部模型,一定要结合使用gltf-pipeline与Draco(描述二)。

如果涉及到需要使用animate,尽量在需要渲染的时候来执行内容:

首先,建议使用gltf或者glb来加载模型。如果有大量外部模型,一定要结合使用gltf-pipeline与Draco。

这很重要,有时候我们获得的gltf模型文件后,我们可以轻易的压缩图片;但有些scene.bin文件可以达到100M或200M以上,模型多了之后会很大程度影响使用体验。

结合使用gltf-pipeline与Draco,可以有效的压缩文件甚至在10M以下!

1、首先你要将three/examples/js/libs/draco文件夹放到public目录下!

2、进入需要压缩的gltf模型文件,执行gltf-pipeline -i scene.gltf -o sceneDraco.gltf -d

执行成功后删除原先的.gltf .bin文件

如果没有安装过gltf-pipeline,使用npm全局安装即可

https://github.com/aisriver

一、 让代码简洁:一些简略的表达方式也会产生很好的优化\x0d\x0aeg:x=x+1在不影响功能的情况下可以简写为x++\x0d\x0a二、 变量名方法名尽量在不影响语意的情况下简单。(可以选择首字母命名)\x0d\x0aeg:定义数组的长度可以取名为:ArrLen而不需要取为ArrayLength。\x0d\x0a三、 关于JS的循环,循环是一种常用的流程控制。\x0d\x0aJS提供了三种循环:for()、while()、for(in)。在这三种循环中for(in)的效率最差,因为它需要查询Hash键,因此应尽量少用for(in)循环,for()、while()循环的性能基本持平。当然,推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++或_运算符。\x0d\x0a四、 如果需要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免多次查询数组长度。\x0d\x0a因为我们常常要根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询a.length,就要额外进行一个操作,而预先把var len=a.length,则就少了一次查询。\x0d\x0a五、 尽量选用局部变量而不是全局变量。\x0d\x0a局部变量的访问速度要比全局变量的访问速度更快,因为全局变量其实是window对象的成员,而局部变量是放在函数的栈里的。\x0d\x0a六、 尽量少使用eval。\x0d\x0a每次使用eval需要消耗大量时间,这时候使用JS所支持的闭包可以实现函数模板。\x0d\x0a七、 减少对象查找\x0d\x0a因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。\x0d\x0a八、 字符串连接。\x0d\x0a如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr。\x0d\x0a如果要连接多个字符串,应该少使用+=,如s+=as+=bs+=c应该写成s+=a + b + c;\x0d\x0a而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存。怎么用呢?使用JavaScript数组来收集,最后使用join方法连接起来,如下\x0d\x0avar buf = new Array()for(var i = 0i String() >.toString() >new String() \x0d\x0a尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。\x0d\x0aString()属于内部函数,所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。\x0d\x0a2. 浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()。Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。\x0d\x0a3. 对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高\x0d\x0a十、 尽量作用JSON格式来创建对象,而不是var obj=new Object()方法。\x0d\x0a因为前者是直接复制,而后者需要调用构造器,因而前者的性能更好。\x0d\x0a十一、 当需要使用数组时,也尽量使用JSON格式的语法,\x0d\x0a使用JSON格式的语法即直接使用如下语法定义数组:[parrm,param,param...],而不是采用new Array(parrm,param,param...)这种语法。因为使用JSON格式的语法是引擎直接解释的。而后者则需要调用Array的构造器。\x0d\x0a十二、 对字符串进行循环操作,例如替换、查找,就使用正则表达式。\x0d\x0a因为JS的循环速度比较慢,而正则表达式的操作是用C写成的API,性能比较好。\x0d\x0a十三、 插入HTML\x0d\x0a很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。\x0d\x0a十四、 对象查询\x0d\x0a使用[“”]查询要比.items()更快\x0d\x0a十五、 定时器\x0d\x0a如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval。setTimeout每次要重新设置一个定时器。\x0d\x0a十六、 尽量减少DOM调用\x0d\x0a在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作。可是对DOM的操作是非常昂贵的,因为这会导致浏览器执行回流 (reflow)操作。我们应该尽可能的减少DOM操作。