前端性能优化小结

吉穆
• 阅读 3234
  1. http优化,加大并发,减少请求数量以及传输量
  • domain hash技术突破并发限制。http1.x浏览器对于发起的连接有并发限制,这个限制是针对域名的,所以将静态资源放在多个不同的域名下,也可以突破这个限制。但是也不宜使用太多域名。会增加额外的dns解析成本。
  • 合理使用http头。使用expires,cache-control,Etags等http头缓存静态资源。这篇文章介绍的很详细:https://zhuanlan.zhihu.com/p/...
  • Connection:keep-alive。保持tcp连接。避免三次握手以及tcp的慢启动开销。
  • 合理利用空闲时间做一些预操作。预测用户的大概率行为,在页面空闲时加载后续所需要的资源。dns预解析。TCP预连接。页面预渲染。有一些标签属性已经可以很好的做到这些,如prefetch & preload & dns-prefetch。
  • 合并css,js文件。
  • 图片压缩以及页面需要多大的图片就请求多大的图片。比如页面只显示20*20的图片,就不要返回一个500*500的图片。
  • cdn加速。
  • gzip压缩。
  • 减少不必要的通信量,比如合并发送上报数据。
  • 按需加载模块资源。比如js,css。
  • 使用http2。
  1. 缓存请求,并保持缓存内容大小与性能之间的平衡。比如某个请求涉及大量的数据库操作,耗时很长,并且有一定概率多次请求。在这个数据的实时性和重要性不是那么强的情况下,我们可以将请求结果缓存到变量中或浏览器的一些别的存储机制中。但是缓存内容过多也会对性能有影响,所以我们也要根据一定的规则(比如访问频率,访问先后时间,缓存总数量等)及时地清除部分缓存。如果不想改动代码,那么http响应可以返回Expires http请求头,在过期之前就不再发请求。
  2. cookie优化,减少cookie传输量
  • 避免cookie太庞大。不要什么都往cookie上放。cookie的主要作用在于身份识别,而不是信息存储。因为每个请求都会带着cookie,无形中会加大很多传输量。前端的话可以使用一些其他的替代存储方式。比如localStorage,sessionStorage。
  • cookie free技术。将一些静态资源放在与主域不同域名的服务器上,浏览器请求的时候就不会带上主域的cookie了,从而减少传输量。
  1. Bigpipe技术。产生于Facebook公司的前端加载技术,它的提出主要是为了解决重数据页面的加载速度问题,是一种数据渐进式预加载方案,基于HTTP Chunk。
  2. PWA技术。service worker。
  3. 避免空的src和href。
  4. 图片懒加载。lazy load。
  5. 脚本加载优化
  • script标签放到页面最后,</body>标签前面,避免阻塞页面渲染。一个讨论:https://www.zhihu.com/questio...
  • 合并,压缩脚本。减少连接数和数据传输大小。
  • 无阻塞的脚本

    • script标签添加defer,async属性。
    • 动态生成script标签。使用onload事件或onreadystatechange事件来检测脚本加载完从而执行加载完之后的回调。
    • 使用ajax方式加载js内容。插入一个script标签中。好处是加载完之后不会立即执行。
  1. JS数据存取
  • 减少作用域查询。作用域链的查询,变量的位置越深,查询速度越慢。而全局变量在作用域链最深。所以,对于使用一次以上的跨作用域变量我们应该把它用局部变量存起来。
  • 避免内存泄露。

    • 循环引用
    • IE,闭包中有dom对象。
  • 减少嵌套成员的查找。可缓存在局部变量中。类似var Dom = YAHOO.util.Dom;
  • 减少原型链查找。
  1. dom优化
  • 减少dom数量。如果页面dom数量太多,对性能是有影响的。
  • 减少dom访问与修改。dom与JavaScript相当于两个独立的部分以功能接口连接,会带来性能损耗。
  • 尽量不要在循环中更新页面内容。一个更有效率的版本将使用局部变量存储更新后的内容,在循环结束时一次性写入。
  • 不建议用数组的 length 属性做循环判断条件。访问集合的 length 比数组的length 还要慢,因为它意味着每次都要重新运行查询过程。
  • 使用快的API。比如document.querySelector()。
  • 事件绑定。

    • 多使用事件代理,而不是每个dom节点上去都绑定事件。
    • 考虑使用自已定义的事件管理器,一个dom上不要反复绑定事件。而是维护一个事件回调数组,像jQuery做的那样。
  • 减少回流与重绘。

    • 少使用.style一个属性一个属性地去改。而是合并到一起一起修改。比如用class来控制样式,或者cssText来批量修改。
    • 让要操作的元素进行"离线处理",处理完后一起更新。
    • 回流必将引起重绘,而重绘不一定会引起回流。
    • 减少对位置信息的属性读取以及getComputedStyle与currentStyle的使用。浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。如果代码中频繁读取实时位置属性,会导致浏览器多次重排。引起性能问题。
  1. 异步优化任务。分割任务异步执行,让出线程。
  • 如果用户的操作100ms得不到响应,用户就会感觉到与应用失去联系。如果我们的代码执行时间太长,用户其他的操作得不到响应。所以如果我们无法减少脚本执行时间,我们可能考虑主动地让出线程。分解任务,异步执行。

比如分解成多个任务使用setTimeout或setInterval来异步执行。

  • 但是如果我们任务分得太细,比如每个循环体算成一个任务,每个任务结束就让出线程,效率就很低了,因为setTimeout 和 setInterval 本来就设计的慢吞吞的,即使延时时间为0,浏览器环境下每秒也最多执行几百次。而换成while循环,每秒能执行几百万次。 所以我们每个异步任务中应该多处理一些任务,比如我们让它执行50ms。在每个异步中检测一下执行时间,加入while循环,时间如果小于50ms就继续执行,超过50ms就让出线程。这样既保证了不阻塞线程,也让我们的任务能尽快地完成。
  • 使用Web Workers。
  1. ajax的优化。目前一些比较成熟的库的ajax都是在ajax完全接收完响应之后才执行成功的回调。这里其实有很大的优化空间。
  • 一般的ajax封装是在XMLHttpRequest的readyState==4(整个请求过程已经完毕)的时候进行成功回调处理。而其实在readyState==3的时候(响应体下载中,responseText中已经获取了部分数据.)已经可以对已经接收到的部分内容进行处理了。比如几十万条数据从后端传过来,要插入dom。如果我们等到所有数据接收完毕,再一次性插入dom,可能会有很大的性能问题。但是如果我们在后台将数据以一定的方式拼装,然后前端接收到一部分处理一部分,就有两方面的性能提升,一方面是提前处理了数据,让用户可以更早地看到数据效果,另一方面是分解了任务。
  • 合并请求。比如多个图片base64格式加分割符一起发送过来。前端再把结果分割,分发到多个img标签上去。
  • 数据传递格式。不一定非要是json格式。其实可以很灵活。自定义的格式一方面可以减少数据传输量,另一方面更方便前端边接收边处理。
  1. 循环与递归
  • 尾调用优化。会将从内存中清除前面的调用栈,将调用栈清零。一方面是内存释放,另一方面是避免了调用栈溢出引起的错误。
  • 减小循环次数。每个循环体中多执行几个循环内容。
  • 减少循环体开销。比如使用倒序循环。
  • 缓存计算结果。使用Memoization技术来避免重复计算。
  1. 函数的节流与防抖,限制函数主体执行频率。频繁执行某些函数会严重影响性能,比如一些常见的触发频率很高的浏览器事件,如果每次触发都去执行回调甚至操作dom,性能影响很大,并且我们肉眼对dom变化的实时性要求并没有那么高。所以需要限制主体内容的执行频率。工具库underscore中提供了对应的_.throttle和_.debounce方法。
  • window对象的resize、scroll事件
  • 拖拽时的mousemove事件
  • mousedown、keydown事件
  • 文字输入、自动完成的keyup事件
  1. requestAnimationFrame方法。
  2. 逻辑优化,减少耗时操作。很多功能并不是只有一种途径去实现。如果一种操作特别耗时。也许可以优化一些逻辑就减少这样的操作。
  3. 定时器的控制。

应用中存在过多的定时器会影响性能。特别是单页应用中,如果我们定义了一些定时器而没有随着场景消失而清掉定时器,还可能会产生很多逻辑上的问题。

  1. 正则表达式优化。
  2. css gpu加速。

欢迎补充交流。github地址:https://github.com/liusaint/l...

参考:
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
灯灯灯灯 灯灯灯灯
4年前
Java并发之Semaphore源码解析
Semaphore前情提要在学习本章前,需要先了解ReentrantLock源码解析,ReentrantLock源码解析里介绍的方法有很多是本章的铺垫。下面,我们进入本章正题Semaphore。从概念上来讲,信号量(Semaphore)会维护一组许可证用于限制线程对资源的访问,当我们有一资源允许线程并发访问,但我们希望能限制访问量,就可以用信号量对访问线程
Stella981 Stella981
3年前
Redis 集群(11)
为什么需要集群?1、性能Redis本身的QPS已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响。这个时候我们希望有更多的Redis服务来完成工作。2、扩展第二个是出于存储的考虑。因为Redis所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有
Wesley13 Wesley13
3年前
JavaEE从服务器端解决Ajax跨域问题
1、Ajax跨域简介  1、指的是浏览器不能执行其他网站的脚本。是浏览器施加的安全限制。js本身不跨域,使用form表单和iframe直接请求,是不会跨域的;  2、只要两个url的协议、域名、端口其中有一个不同,从其中一个url中使用ajax请求另一个url,则属于Ajax跨域;  3、ajax请求接口,只是不能进入回调函数,接口还是可以正常请
Stella981 Stella981
3年前
Js中的跨域问题
一、什么是跨域?1.定义:跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com页面去请求www.google.com的资源。但是一般情况下不能这么做,它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。跨域的严格一点的定义是:只要协议,域名,端口有任何一
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
ZOS对象存储跨域资源访问的实现和使用
跨域指的是从一个域名去请求另外一个域名的资源,即跨域名请求。跨域时,浏览器不能执行其他域名网站的脚本,这是由浏览器的同源策略造成的,是浏览器施加的安全限制。同源策略是一种约定,是浏览器最核心也最基本的安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,这是一个用于隔离潜在恶意文件的重要安全机制。
高性能且低成本的 Goroutine 池库-Ants
本文分享自天翼云开发者社区《》,作者:李佳在Go语言中,虽然原生支持并发的Goroutine提供了强大的并发能力,但在高并发场景下,无限制地创建Goroutine会导致系统资源耗尽。因此,合理管理Goroutine的生命周期和数量变得尤为重要。Ants是一