前端性能优化

309 阅读11分钟

前端资源比较庞大,包括HTMLCSSJavaScriptImage、Flash、Media、Font、Doc等等,前端优化相对比较复杂,对于各种资源的优化都有不同的方式,按粒度大致可以分为两类,第一类是页面级别的优化,例如减小HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等,第二类则是代码级别的优化,例如JavaScript中的DOM操作优化、图片优化以及HTML结构优化等等。在用户角度前端优化可以让页面加载得更快,对用户的操作响应得更及时,能够给用户提供更为友好的体验,在服务商角度前端优化能够减少页面请求数,减小请求所占带宽,能够节省服务器资源

减少HTTP请求

加载前端的大部分时间在于下载各种资源, 浏览器对于同一个服务器的HTTP请求连接池数量也是有限的, 对于过多的请求需要排队等候, 最小化HTTP请求减少请求次数可以防止HTTP连接池被占满, 同时也能避免过多HTTP连接时的TCP握手造成的时间消耗

  • CSS Sprite CSS Sprite也叫雪碧图, 通过将多张图片合并到一张图片中, 可以减少图片的数量, 此外由于合并之后的图片相对分开的图片减少了存储信息的开销如颜色表和格式信息等, 合并图片后的大小比分开的图片的大小的总和要趋于更小, 当然如果合并图片时有大量空白来分隔原来的单个图片, 那么其大小会趋于更大. 使用雪碧图, 需要使用CSSbackgroud-imagebackground-position属性显示所需的图像段

  • Image maps 假如网站有很多带链接的图片例如地图应用等, 那么图片映射image maps将是一个很好的选择, image maps允许在单张图片上有很多带链接的图片, 通过<map><area>来将一张完整的图片映射分割为多个区域来制作不同的链接, 同样是可以减少图片的HTTP请求链接数量

  • Inline images 通过使用data:URL方案来直接将图像数据嵌入到页面或者CSS中, 虽然这会增加文档或者是CSS文件的大小, 但同样这确实是一个减少HTTP请求数量的方案, 对于data:URL的格式为data:[<mediatype>][;base64],<data>

  • Font icon 使用字体图标来代替图标, 将多个图标合成为字体图标不仅可以减少对于图片的HTTP请求数量与图标大小, 还作为矢量图对于放大缩小等操作不会失真, 此外字体图标的优点还包括其很容易改变颜色, 产生阴影, 透明效果等, 可以得到CSS很好的支持从而各种样式, 旋转和动画效果等

  • Combined files Combined files也就是合并文件, 将多个CSS文件或者JavaScript文件合并成一个CSS文件或者JavaScript文件, 可以有效减少HTTP请求数量, 并且可以通过压缩算法减小文件的大小. 当脚本和样式表在页面之间变化时, 组合文件可能会变得难以阅读和修改, 但是将其作为发布过程的一部分可以缩短时间

利用缓存机制

  • 缓存控制 通过服务器端设置响应头的ExpiresCache-Control来设置资源组件过期时间以及过期策略, 对于静态资源可以通过设置Expires为一个长期时间来实现永不过期策略, 对于动态组件通过Cache-Control指定缓存机制来辅助浏览器处理条件i请求

  • 外部引用 将JavaScriptCSS设置为外部文件引入而不是直接嵌入到HTML中, 由于浏览器的缓存机制, 外部文件可以通过浏览器的缓存引入而不需要每次请求重复请求同一个资源文件, 这样就使得浏览器在第二次打开页面的速度会快得多, 当然全部由外部文件引入的方式会增加HTTP请求数量, 所以外部引入的关键问题在于如何权衡相对于HTML文档数量而言, 缓存外部JavaScriptCSS文件的数量, 尽管难以量化, 但可以使用各种度量标准来衡量此因素, 网站上的用户每个会话具有多个页面视图, 并且许多页面都使用相同的脚本和样式表, 则缓存的外部文件会带来更大的潜在利益

优化资源加载

  • 样式表位置 根据浏览器渲染的顺序, 将CSS<head>中引入或嵌入, 相对于将CSS放到<body>或者页面底部来说, 可以使页面渲染速度加快, 这对于页面内容比较丰富的网站或者网络连接较慢时相当重要. 假如将样式表放置于底部, 就会导致浏览器还未加载样式表就开始渲染页面, 无法渐进式渲染页面而直接从无样式状态立即跳转到有样式状态, 用户体验较差; 此外有些浏览器可能会在CSS下载完成后才开始渲染页面,导致页面渲染推迟

  • 脚本位置 浏览器是可以并发请求的, 这一特点使得其能够更快的加载资源, 然而外部引入JavaScript脚本在加载时却会阻塞其他资源, 例如在脚本加载完成之前, 它后面的图片, 样式以及其他脚本都处于阻塞状态, 直到脚本加载完成后才会开始加载, 原因之一是Js可能会改变页面或者改变Js间的依赖关系, 例如A.js中用document.write改变页面, B.js依赖A.js. 因此要严格保证顺序, 不能并行下载. 如果将脚本放在比较考前的位置, 则会影响整个页面的加载速度从而影响用户体验. 此外当浏览器发现Js脚本时, 浏览器会立即开始解析脚本并停止解析文档, 因为脚本有可能会改动DOMCSS, 继续解析会浪费资源. 解决这些问题的方法有很多如异步加载脚本等, 而最简单可依赖的方法就是将脚本尽可能的往后挪, 减少对并发下载与页面渲染的影响

优化代码方案

  • 避免CSS表达式 CSS表达式通过expression方法来接受JavaScript表达式, 是一种动态设置CSS的强大方式, 但同时也是非常危险的方式, CSS表达式的问题在于其会进行频繁的计算, CSS计算的频率要远远超出我们的想象, 不仅在页面显示和缩进时会进行计算, 在页面滚动或者移动鼠标都会重新计算一次, 从而影响到页面的性能. 可以通过使用Js将属性进行计算并赋值给样式属性, 也就是一次性表达式, 如果必须在页面的整个生命周期中动态设置样式属性, 则可以使用事件处理程序代替CSS表达式. 如果必须使用CSS表达式, 需要注意它们可能会被计算数千次, 并且很可能影响页面的性能

  • 避免重定向 尽量避免使用重定向, 当页面发生了重定向, 就会延迟整个HTML文档的传输. 在HTML文档到达之前, 页面中不会呈现任何东西, 也没有任何组件会被下载, 降低了用户体验. 如果一定要使用重定向, 如http重定向到https, 要使用301永久重定向, 而不是302临时重定向. 因为如果使用302, 则每一次访问http, 都会被重定向到https的页面, 而永久重定向, 在第一次从http重定向到http之后就会被浏览器记住, 每次访问http, 会直接返回https的页面

  • 最小化操作DOM JavaScript操作DOM无可避免的会触发浏览器的重绘或者回流, 由于重绘和回流可能代价比较昂贵, 因此最好就是可以减少它的发送次数, 为了减少发送次数, 我们可以合并多次对DOM和样式的修改, 然后一次处理掉, 或者将样式实现设计好, 动态去改变class. 或者采用离线修改DOM的方案, 使用documentFragment对象在内存里操作DOM, 在内存中的DOM修改就是让元素脱离文档流, 不会触发重绘, 修改完后将节点再放入文档流中, 只触发一次回流

压缩资源文件

  • Gzip 从HTTP/1.1开始, 客户端可以通过使用HTTP请求中的Accept-Encoding:gzip, deflate来指示对压缩的支持. 如果服务器在请求中看到此标头, 则可以使用客户端列出的方法之一压缩响应, 服务器通过响应中的Content-Encoding:gzip通知客户端采用gzip压缩. Gzip的压缩率很高, 是目前最流行, 最有效的压缩方法, 它由GNU项目开发, 并由RFC 1952标准化

  • 压缩外部文件 压缩JavaScriptCSS文件, 从代码中删除不必要的字符以减小其大小, 从而缩短加载时间, 当代码最小化时, 所有注释以及不需要的空白字符都将被删除, 由于减小了下载文件的大小, 因此可以提高响应时间性能

优化网络请求

  • CDN CDN的全称是Content Delivery Network, 即内容分发网络, CDN是构建在现有网络基础之上的智能虚拟网络, 依靠部署在各地的边缘服务器, 通过中心平台的负载均衡, 内容分发, 调度等功能模块, 使用户就近获取所需内容, 降低网络拥塞, 提高用户访问响应速度和命中率. 当用户处于跨地域的多个位置时, 对于服务器响应速度的感知是有差别的, 用户访问网站的绝大部分时间都是处于下载静态资源状态的, 将这些静态资源首先分发到CDN各服务器, 可以大大缩短响应时间, CDN可以根据用户网络状态信息来选择网络跳数最少的服务器或响应最快的服务器来就近加载用户所需要的资源

  • DNS预解析 当浏览器访问一个域名的时候, 需要解析一次DNS, 获得对应域名的IP地址. 在解析过程中, 按照浏览器缓存, 系统缓存, 路由器缓存, ISP(运营商)``````DNS缓存, 根域名服务器, 顶级域名服务器, 主域名服务器的顺序, 逐步读取缓存, 直到拿到IP地址, DNS Prefetch, 即DNS预解析就是根据浏览器定义的规则, 提前解析之后可能会用到的域名, 使解析结果缓存到系统缓存中, 缩短DNS解析时间, 来提高网站的访问速度

    <link rel="dns-prefecth" href="https://www.google.com">
    <link rel="dns-prefech" href="https://www.google-analytics.com">
    

    提前解析DNS, 由于它是并行的, 不会堵塞页面渲染, 这样可以缩短资源加载的时间

  • 配置ETag 实体标签ETagWeb服务器和浏览器用来确定浏览器缓存中的资源是否与原始服务器上的资源匹配的一种机制, 添加了ETag, 以提供一种比上次修改日期更灵活的验证实体的机制. Etag是唯一标识组件特定版本的字符串, 唯一的格式限制是用引号引起来, 原始服务器使用ETag响应头指定组件的ETag

  • 尽早释放缓冲 当用户请求页面时, 后端服务器将HTML页面拼接在一起可能需要200500毫秒的时间, 在这段时间内, 浏览器在等待数据到达时处于空闲状态, 这段时间则可以将服务端部分已处理好的数据发送到前端, 使浏览器先开始加载外部资源. 例如使用PHP, 则可以使用函数flush()将部分就绪的HTML响应发送到浏览器, 以便浏览器可以在后端忙于处理HTML页面的其余部分时开始获取资源, 好处主要体现在繁忙的后端或轻量级前端