关于前端优化的一些方案

1,379 阅读16分钟

前言

对于前端的性能话题,这绝对是老生常谈的话题,不管是在工作中换是生活中,多多少少都会提到关于前端性能优化的问题,作为一个前端开发者,性能是我们关注的指标。公司的大佬也会关注这方面的发展,毕竟它直接影响着我们的用户,同时也影响着产品本身。前端发展以来,优化方式,因此,准备一篇常用的优化方式进行总结,或许,并不全面,不足的地方希望路过的大佬指点迷津。欢迎关注我的博客~

一、减小资源大小

一、压缩

  • HTML压缩HTML代码压缩就是压缩在文本文件中有意义,但是在HTML中不显示的字符,包括空格制表符换行符等.
  • CSS压缩CSS压缩包括无效代码删除与CSS语义合并以及空格制表符换行符
  • JS压缩与混淆JS压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码可读性,实现代码保护
  • 图片压缩针对真实图片情况,舍弃一些相对无关紧要的色彩信息
  • gzip压缩HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来。一般对纯文本内容可压缩到原大小的40%.

二、减少请求数量

2-1、合并文件

  • 公共库合并
  • 不同页面单独合并

2-2、图片处理

  1、雪碧图

  CSS雪碧图是以前非常流行的技术,把网站上的一些图片整合到一张单独的图片中,可以减少网站的HTTP请求数量,但是当整合图片比较大时,一次加载比较慢。随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台

  2、Base64

  将图片的内容以Base64格式内嵌到HTML中,可以减少HTTP请求数量。但是,由于Base64编码用8位字符表示信息中的6个位,所以编码后大小大约比原始值扩大了 33%

  3、使用字体图标来代替图片

2-3、减少重定向

  尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验

  如果一定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302临时重定向。因为,如果使用302,则每一次访问http,都会被重定向到https的页面。而永久重定向,在第一次从http重定向到https之后 ,每次访问http,会直接返回https的页面

2-4、使用缓存

由于浏览器会在DNS解析步骤中消耗一定的时间,所以,对于一些高访问量网站来说,做好DNS的缓存工作,就会一定程度上提升网站效率。

  • CDN缓存,CDN作为静态资源文件的分发网络,本身就已经提升了,网站静态资源的获取速度,加快网站的加载速度,同时也给静态资源做好缓存工作,有效的利用已缓存的静态资源,加快获取速度。
  • http缓存,也是给资源设定缓存时间,防止在有效的缓存时间内对资源进行重复的下载,从而提升整体网页的加载速度。

使用Cache-controlexpires这类强缓存时,缓存不过期的情况下,不向服务器发送请求。强缓存过期时,会使用last-modifiedetag这类协商缓存,向服务器发送请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应.

为了避免用户每次访问网站都得请求文件,我们可以通过添加 Expiresmax-age 来控制这一行为。Expires 设置了一个时间,只要在这个时间之前,浏览器都不会请求文件,而是直接使用缓存。而 max-age 是一个相对时间,建议使用 max-age 代替 Expires 。不过这样会产生一个问题,当文件更新了怎么办?怎么通知浏览器重新请求文件?可以通过更新页面中引用的资源链接地址,让浏览器主动放弃缓存,加载新资源。具体做法是把资源地址 URL 的修改与文件内容关联起来,也就是说,只有文件内容变化,才会导致相应 URL 的变更,从而实现文件级别的精确缓存控制。什么东西与文件内容相关呢?我们会很自然的联想到利用数据摘要要算法对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种可以精确到单个文件粒度的缓存控制依据了。参考资料:

2-5、不使用CSS @import避免使用空的src和href

  • CSS的@import会造成额外的请求
  • a标签设置空的href,会重定向到当前的页面地址
  • form设置空的method,会提交表单到当前的页面地址

三、网络连接的优化

  • 【使用CND】CDN全称是Content Delivery Network,即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
  • 【使用DNS预解析】当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。在解析过程中,按照浏览器缓存、系统缓存、路由器缓存、ISP(运营商)DNS缓存、根域名服务器、顶级域名服务器、主域名服务器的顺序,逐步读取缓存,直到拿到IP地址

DNS Prefetch,即DNS预解析就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,来提高网站的访问速度.

方法是在 <head> 标签里面写上几个 <link> 标签

<link rel="dns-prefecth" href="xxxx.css">
<link rel="dns-prefecth" href="xxxxxxxx.css">

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

四、优化资源加载

(一)、资源加载位置

通过优化资源加载位置,更改资源加载时机,使尽可能快地展示出页面内容,尽可能快地使功能可用

  • CSS文件放在head中,先外链,后本页
  • JS文件放在body底部,先外链,后本页
  • 处理页面、处理页面布局的JS文件放在head中,如babel-polyfill.js文件、flexible.js文件
  • body中间尽量不写style标签和script标签

css文件 放在「head中」,可以保证解析DOM的同时,解析css文件。因为,CSS(外链或内联)会阻塞整个DOM的渲染,然而DOM解析会正常进行,所以将css文件放在头部进行解析,可以加快网页的构建速度。

js文件,将js文件放在尾部或者异步加载的原因是JS(外链或内联)会阻塞后续DOM的解析,后续DOM的渲染也将被阻塞,而且一旦js中遇到DOM元素的操作,很可能会影响。

(二)、资源加载时机

1、异步script标签

  • defer: 异步加载,在HTML解析完成后执行。defer的实际效果与将代码放在body底部类似
  • async: 异步加载,加载完成后立即执行

2、模块按需加载

在SPA等业务逻辑比较复杂的系统中,需要根据路由来加载当前页面需要的业务模块

  按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载

  webpack 提供了两个类似的技术,优先选择的方式是使用符合 ECMAScript 提案的import() 语法。第二种则是使用 webpack 特定的 require.ensure

3、使用资源预加载preload和资源预读取prefetch

  preload让浏览器提前加载指定资源,需要执行时再执行,可以加速本页面的加载速度

  prefetch告诉浏览器加载下一页面可能会用到的资源,可以加速下一个页面的加载速度

4、资源懒加载与资源预加载

  • 资源延迟加载也称为懒加载,延迟加载资源或符合某些条件时才加载某些资源.
  • 资源预加载是提前加载用户所需的资源,保证良好的用户体验.
  • 资源懒加载和资源预加载都是一种错峰操作,在浏览器忙碌的时候不做操作,浏览器空间时,再加载资源,优化了网络性能.

五、减少重绘与回流

5-1、DOM操作优化

  • 尽量减少css表达式的使用
  • 适当使用canvas
  • 使用class代替大量的内联样式修改
  • 尽量使用css动画
  • HTML 中标签元素越多,标签的层级越深,浏览器解析DOM并绘制到浏览器中所花的时间就越长,所以应尽可能保持 DOM 元素简洁和层级较少
  • 避免在document上直接进行频繁的DOM操作
  • 由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
  • 使用requestAnimationFrame代替setInterval操作
  • 对于复杂的UI元素,设置position为absolute或fixed
  • 使用事件代理
  • 函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
  • 及时消除对象引用,清除定时器,清除事件监听器,创建最小作用域变量,可以及时回收内存

5-2、css样式操作优化

  • 避免使用层级较深的选择器,或其他一些复杂的选择器,以提高CSS渲染效率
  • 2、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不仅仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要重新计算一次
  • 3、元素适当地定义高度或最小高度,否则元素的动态内容载入时,会出现页面元素的晃动或位置,造成回流
  • 4、给图片设置尺寸。如果图片不设置尺寸,首次载入时,占据空间会从0到完全出现,上下左右都可能位移,发生回流
  • 5、不要使用table布局,因为一个小改动可能会造成整个table重新布局。而且table渲染通常要3倍于同等元素时间
  • 6、能够使用CSS实现的效果,尽量使用CSS而不使用JS实现

这篇文章对你还是有帮助的CSS书写规范和顺序

5-3、性能更好的API   

1、用对选择器

选择器的性能排序如下所示,尽量选择性能更好的选择器

id选择器(#myid)
类选择器(.myclassname)
标签选择器(div,h1,p)
相邻选择器(h1+p)
子选择器(ul > li)
后代选择器(li a)
通配符选择器(*)
属性选择器(a[rel="external"])
伪类选择器(a:hover,li:nth-child

2、使用requestAnimationFrame来替代setTimeoutsetInterval

希望在每一帧刚开始的时候对页面进行更改,目前只有使用 requestAnimationFrame 能够保证这一点。使用 setTimeout 或者 setInterval 来触发更新页面的函数,该函数可能在一帧的中间或者结束的时间点上调用,进而导致该帧后面需要进行的事情没有完成,引发丢帧.

3、使用IntersectionObserver来实现图片可视区域的懒加载

传统的做法中,需要使用scroll事件,并调用getBoundingClientRect方法,来实现可视区域的判断,即使使用了函数节流,也会造成页面回流。使用IntersectionObserver,则没有上述问题

4、使用web worker

客户端javascript一个基本的特性是单线程:比如,浏览器无法同时运行两个事件处理程序,它也无法在一个事件处理程序运行的时候触发一个计时器。Web WorkerHTML5提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行,从而避免阻塞用户界面,在执行复杂计算和数据处理时,这个API非常有用

但是,使用一些新的API的同时,也要注意其浏览器兼容性.

六、数据方面

数据,也可以说是前端优化方面比较重要的一块内容。页面与用户的交互响应,往往伴随着数据交互,处理,以及ajax的异步请求等内容。

  • 「图片预加载」,预加载的寓意就是提前加载内容。而图片的预加载往往会被用在图片资源比较大,即时加载时会导致很长的等待过程时,才会被使用的。常见场景:图片漫画展示时。往往会预加载一张到两张的图片。
  • 「图片懒加载」,懒加载或许你是第一次听说,但是,这种方式在开发中会被经常使用。首先,我们需要明白一个道理:往往只有看到的资源是必须的,其他资源是可以随着用户的滚动,随即显示的。所以,特别是对于图片资源特别多的网站来说,做好图片的懒加载是可以大大提升网页的载入速度的。

常见的图片懒加载的方式就是:在最初给图片的src设置一个比较简单的图片,然后将图片的真实地址设置给自定义的属性,做一个占位,然后给图片设置监听事件,一旦图片到达视口范围,从图片的自定义属性中获取出真是地址,然后赋值给src,让其进行加载。

  • 「首屏loading的显示」:往往对于首屏优化后的数据量并不满意的话,同时也不能进一步缩短首屏包的长度了,就可以使用进度条的方式,来提醒用户进行等待。

  • 「JSON交互」,JSON的数据格式轻巧,结构简单,往往可以大大优化前后端的数据通信。

  • 「常用数据的缓存」,可以将一些用户的基本信息等常用的信息做一个缓存,这样可以保证ajax请求的减少。同时,HTML5新增的storage的内容,也不用怕cookie暴露,引起的信息泄漏问题。

  • 「数据埋点和统计」,对于资深的程序员来说,比较了解。而且目前的大部分公司也会做这方面的处理。

七、webpack优化

【打包公共代码】

  使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件

  webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项optimization.splitChunksoptimization.runtimeChunk      通过设置 optimization.splitChunks.chunks: "all" 来启动默认的代码分割配置项

【动态导入和按需加载】

  webpack提供了两种技术通过模块的内联函数调用来分离代码,优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure

【剔除无用代码】

  tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup

  JS的tree shaking主要通过uglifyjs插件来完成,CSS的tree shaking主要通过purify CSS来实现的

【长缓存优化】

  1、将hash替换为chunkhash,这样当chunk不变时,缓存依然有效

  2、使用Name而不是id

  每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变

  下面来使用两个插件解决这个问题。第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建

【公用代码内联】

  使用html-webpack-inline-chunk-plugin插件将mainfest.js内联到html文件中

你以为到这里就结束了,

你的以为是正确的。

总结如有不当,或引起您的不满,我深表愧疚,如有错误,还望不吝赐教。

致此完结,谢谢观赏,关注我,会让你成为装逼界的灵魂人物

可学习文章

你知道在浏览器地址栏键入URL,按下回车之后会经历了那些事传送门在这里你不点进来看看吗~~~

这篇文章对你还是有帮助的CSS书写规范和顺序