web前端 网页加载 性能优化大全

web前端 性能优化 — — 如何提高网页加载速度

1. 减少DNS查找1

DNS(Domain Name System): 负责将域名URL转化为服务器主机IP。:把域名转换成为网络可以识别的ip地址
 
DNS查找流程:首先查看浏览器缓存是否存在,不存在则访问本机DNS缓存,再不存在则访问本地DNS服务器。所以DNS也是开销,通常浏览器查找一个给定URL的IP地址要花费20-120ms,在DNS查找完成前,浏览器不能从host那里下载任何东西。
 
TTL(Time To Live):表示查找返回的DNS记录包含的一个存活时间,过期则这个DNS记录将被抛弃
 
DNS优化两个方面:DNS缓存、DNS负载均衡

  • 影响DNS缓存的因素

    1. 服务器可以设置TTL值表示DNS记录的存活时间。本机DNS缓存将根据这个TTL值判断DNS记录什么时候被抛弃,这个TTL值一般都不会设置很大,主要是考虑到快速故障转移的问题
    2. 浏览器DNS缓存也有自己的过期时间,这个时间是独立于本机DNS缓存的,相对也比较短,例如chrome只有1分钟左右
    3. 浏览器DNS记录的数量也有限制,如果短时间内访问了大量不同域名的网站,则较早的DNS记录将被抛弃,必须重新查找。不过即使浏览器丢弃了DNS记录,操作系统的DNS缓存也有很大机率保留着该记录,这样可以避免通过网络查询而带来的延迟。
  • 最佳实践

    • 当客户端的DNS缓存为空时,DNS查找的数量与Web页面中唯一主机名的数量相等。所以减少唯一主机名的数量就可以减少DNS查找的数量。

    • 然而减少唯一主机名的数量会潜在地减少页面中并行下载的数量,避免DNS查找降低了响应时间,但减少并行下载可能会增加响应时间。当页面的组件量比较多的时候,可以考虑将组件分别放到至少2-4个主机名,已获得最大收益

2. 使用CDN托管资源2

CDN (Content Delivery Network) 可直译成内容分发网络。CDN的本质仍然利用缓存技术缓存, 解决的是如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN对于数据的分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的
即 — 把资源放在离用户地理位置更近的地方

  • 用户在通过浏览器访问未使用CDN加速的网站的大致过程:

    1. 用户在浏览器中输入要访问的域名。
    2. 浏览器向DNS服务器请求对该域名的解析。
    3. DNS服务器返回该域名的IP地址给浏览器。
    4. 浏览器使用该IP地址向服务器请求内容。
    5. 服务器将用户请求的内容返回给浏览器。
  • CDN访问过程

    1. 用户向浏览器提供要访问的域名
    2. 浏览器调用域名解析库对域名进行解析,由于CDN对域名解析过程进行了调整,所以解析函数库一般得到的是该域名对应的CNAME记录,为了得到实际IP地址,浏览器需要再次对获得的CNAME域名进行解析以得到实际的IP地址。在此过程中,使用的全局负载均衡DNS解析,如根据地理位置信息解析对应的IP地址,使得用户能就近访问;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器的负载情况,判断哪一台服务器的负载较小
    3. 此次解析得到CDN缓存服务器的IP地址,浏览器在得到实际的IP地址以后,向缓存服务器发出访问请求
    4. 缓存服务器根据浏览器提供的要访问的域名,通过Cache内部专用DNS解析得到此域名的实际IP地址,再由缓存服务器向此实际IP地址提交访问请求
    5. 缓存服务器从实际IP地址得得到内容以后,一方面在本地进行保存,以备以后使用,二方面把获取的数据返回给客户端,完成数据服务过程
    6. 客户端得到由缓存服务器返回的数据以后显示出来并完成整个浏览的数据请求过程
  •   使用CDN可以解决资源并行下载限制,处理静态资源Cookie同域名携带等问题;

  • CDN缓存和回源需要合理的设置静态资源hash;

  • 接入CDN会引入多个域名,增加域名解析时间,可进行预解析域名

       使用CDN服务的网站,只需将其域名的解析权交给CDN的负载均衡设备,CDN负载均衡设备将为用户选择一台合适的缓存服务器,用户通过访问这台缓存服务器来获取自己所需的数据。
       由于缓存服务器部署在网络运营商的机房,而这些运营商又是用户的网络服务提供商,因此用户可以以最短的路径,最快的速度对网站进行访问。因此,CDN可以加速用户访问速度,减少源站中心负载压力。

3. 减少Http请求3

  • 浏览器发送Http请求过程4
    1. DNS 查找 IP 地址 :
            DNS查找:浏览器缓存 -> 系统缓存 -> 路由器缓存 -> ISP DNS缓存 -> 递归搜索
            DNS域名解析
    2. 三次握手建立 TCP 连接
    3. 服务器的永久重定向响应:返回真正访问的地址;
    4. 浏览器跟踪重定向地址:另发一个 http 请求;
    5. 服务器接收到获取请求,然后处理并返回一个http响应,浏览器得到html代码。
    6. 浏览器解析 html 页面:解析 html 以构建 DOM 树 –> 构建渲染树 –> 布局渲染树 –> 绘制渲染树
    7. 浏览器请求html代码中的资源,获取嵌入在 html 中的资源(如图片、音频、视频、CSS 、JS 等等);
    8. 浏览器对页面进行渲染呈现给用户
    9. 服务器关闭关闭TCP连接(四次挥手终止连接)
  • 一个http请求绝大多数的时间消耗在了建立连接跟等待的时间,优化的方法是减少http请求。

    • 减少图片的请求:css sprites、内联图片、IconFont。
    • 减少脚本与样式表的请求主要原则就是合并:将多个样式表或者脚本文件合并到一个文件中,可以减少HTTP请求的数量从而缩短效应时间。
      • 然而合并所有文件对许多人尤其是编写模块化代码的人来说是不能忍的,而且合并所有的样式文件或者脚本文件可能会导致在一个页面加载时加载了多于自己所需要的样式或者脚本,对于只访问该网站一个(或几个)页面的人来说反而增加了下载量,所以大家应该自己权衡利弊。

4. 压缩文本和图像Gzip5

      前端生产环境中将js、css、图片等文件进行压缩的好处显而易见,通过减少数据传输量减小传输时间,节省服务器网络带宽,加快加载文本的速度,提高前端性能
      而且测试表明,压缩对网站还是起到优化性能的作用的,那些基于文本的响应,包括HTML,XML,JSON(Javascript Object Notation),Javascript,和CSS可以减少大约70%的大小。
      目前比较通用的压缩方法是启用gzip压缩。它会把浏览器请求的页面,以及页面中引用的静态资源以压缩包的形式发送到客户端,然后在客户端完成解压和拼装.

  • http协议对压缩文件的传输的支持

    1. 浏览器请求数据时,通过Accept-Encoding申明自己可以接受的压缩方法

    2. 服务端接收到请求后,选取Accept-Encoding中的一种对响应数据进行压缩

    3. 服务端返回响应数据时,在Content-Encoding字段中说明数据的压缩方式

    4. 浏览器接收到响应数据后根据Content-Encoding的响应头对结果进行解压

      注:如果服务器没有对响应数据进行压缩,则不返回Content-Encoding,浏览器也不进行解压

  • 压缩的时间
时间 流程 说明
服务端响应请求时 服务端接收请求后找到响应文件,并进行压缩,然后将压缩后的文件作为内容返回给客户端 压缩等级越高压缩效果越好,同时CPU消耗也越大,压缩时间也越长。为了减少响应时间这一目的,服务端响应请求时压缩等级不宜过高
构建时 项目构建时将文件压缩后发布 构建时压缩不占用响应时间,可以选择较高的压缩等级,生成压缩后的文件后部署到服务器
结合使用 请求根据文件类型,大小,请求频率等对压缩时间做策略选择 构建时对需要压缩的文件进行压缩;服务器在收到请求后首先查找对应的已压缩文件,找不到的情况下使用服务端压缩
  • 服务器响应请求时压缩nginx
  • 构建时压缩webpack
    使用插件:cnpm install --save-dev compression-webpack-plugin
    webpack配置
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
plugins:[
  new CompressionWebpackPlugin({ //这里对大于10k的js和css文件进行压缩,其它配置参考官方文档
  	  filename: '[path].gz[query]',  // 目标资源名称 [file]会被替换成原资源,[path]替换成原资源路径 [query]替换成原查询字符串
  	  algorithm: 'gzip'  // 算法
      test: /\.(js|css)$/,  // 压缩js与css
      threshold: 10240,  // 只处理比这个值大的资源,按字节计算
      minRatio: 0.8  // 只有压缩率比这个值小的资源才会被处理
  })
]

后台开启使用koa

const staticCache = require('koa-static-cache')
import config from './config'
const app = new Koa()
app.use(staticCache(path.resolve(_dirname, "../dist"), {
	maxAge: 7 * 24 * 60 * 60,
	gzip: true, // 开启
	dynamic: true
}))
Treeshaking、Scope hoisting、Code splitting
  1. Tree shaking —— 清除不使用的代码,找出使用的代码

  2. Scope hoisting ——检查import链,并尽可能的将散乱的模块放到一个函数中,前提是不能造成代码冗余,所以只有被引用了一次的模块才会被合并

  3. Code-splitting —— 能够把代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。

      基于 ES6 的静态引用,Tree shaking通过扫描所有 ES6 的 export,找出被 import 的内容并添加到最终代码中。 webpack 的实现是把所有 import 标记为有使用/无使用两种,在后续压缩时进行区别处理
 
      使用Scope Hoisting可以让代码体积更小并且可以降低代码在运行时的内存开销,同时它的运行速度更快。Scope Hoisting可以减少搜索时间。(变量从局部作用域到全局作用域的搜索过程越长执行速度越慢)
 
      Code-splitting可以用于获取更小的bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

  • 生产环境proxy_pass + gzip

5. 善用缓存添加Expires头,配置Etag...6

  • 缓存:CDN缓存,DNS缓存,浏览器缓存,本地缓存,Service Worker…
浏览器缓存

自动化缓存-处理大规模缓存:
http-Header(协议头部):基于http-Request(请求头部)和http-Response(响应头)来实现的缓存策略

  • 对http请求来说,客户端缓存分三类:

    1. 不发任何请求,直接从缓存中取数据(本地缓存)如: ExpiresCache-Control=appcache
    2. 发请求确认是否新鲜,再决定是否返回304并从缓存中取数据(协商缓存)如:Last-Modified/If-Modified-SinceEtag/If-None-Match
    3. 直接发送请求, 没有缓存,如:Cache-Control:max-age=0/no-cache

      现在所有的浏览器都会使用本地资源去缓存住那些被Cache一Control或者Expires头标记的资源,这些头能标记资源需要缓存的时间。
      另外,ETag(实体标签)和Last一Modified头来标识当资源过期后是否需要重新请求,浏览器为了减少不必要的服务器请求,尽可能地从本地缓存中获取资源,并且将那些已经过期的、或者当缓存空间减小的时候将那些很久不用的资源进行清理。
      浏览器缓存通常包括图片,CSS,Javascript代码,这些缓存能合理地提高网站的性能(比如为了支持后退和前进的按钮,使用一个单独的缓存来保存整个渲染的页面)

   Expires:

      指定缓存到期GMT的绝对时间,指服务端具体时间点,过期之前均从浏览器缓存读取数据。如果设了max-age,max-age就会覆盖expires。如果expires到期需要重新请求。

   Cache-control:

      假设你的站点有引用一个脚本文件,你非常确认这个脚本文件内容十年不变。那么自然希望浏览器把这个脚本缓存起来,不用每一次都请求服务器,然后服务器再返回相同的内容。这样能够节省带宽开销并且提升性能。
      设置文件返回的HTTP头中的Cache-Control设置为:Cache-Control: max-age=31536000(标准中规定max-age值最大不能超过一年,以秒为单位,值为31536000)
Cache-Control:
      public:表示缓存的版本可以被代理服务器或者其他中间服务器识别。
       private:意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
       no-cache:意味着文件的内容不应当被缓存。这在搜索或者翻页结果中非常有用,因为同样的URL,对应的内容会发生变化。

相关字段:
max-age:指定缓存的有效时间(以秒为单位),max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00。
s-maxage:类似于max-age,只能指定Public类型的共享缓存,优先级高于max-age,比如proxy。
private:私有缓存,如果个人计算机上的缓存
public:公开缓存,如cdn服务器上的资源缓存,因为共很多用户使用所以是public。通常情况下需要http身份验证的情况,响应是不可cache的,加上public可以使它被cache。
no-cache: 强制浏览器在使用cache拷贝之前先提交一个http请求到源服务器进行确认。请求获取当前服务器端该文件的Last-Modified对比判断是否使用继续使用本地缓存文件
no-store:告诉浏览器在任何情况下都不要进行cache,不在本地保留拷贝。
must-revalidate: 强制浏览器严格遵守你设置的cache规则。
proxy-revalidate: 强制proxy严格遵守你设置的cache规则。
cache:使用本地缓存,不发生请求。

   Last-Modified & if-modified-since:

      Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
       If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。

   ETag & If-None-Match:

       Etag(全称Entity Tag.):web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定,具体下文中介绍)。
       If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。

Etag和304类似,但是级别比 Last-Modified 高一些

  • 304:通过If-Modified-Since If-Match判断资源是否修改,如未修改则返回304,发生了一次请求,但请求内容长度为0,节省了带宽。 如果有多台负载均衡的服务器,不同服务器计算出的Etag可能不同,这样就会造成资源的重复加载。

过程:

  1. 客户端请求一个页面(A)。
  2. 服务器返回页面A,并在给A加上一个Last-Modified/ETag。
  3. 客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
  4. 客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
  5. 服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体

Etag 主要为了解决 Last-Modified 无法解决的一些问题:

  1. 某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存,这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;

  2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是秒级的,它将不能准确标注文件的修改时间。(或者说UNIX记录MTIME只能精确到秒);

  3. 某些服务器不能精确的得到文件的最后修改时间,或者与代理服务器时间不一致等情形
    web前端 网页加载 性能优化大全_第1张图片web前端 网页加载 性能优化大全_第2张图片

本地缓存
  • Cookie、localStorage、以及sessionStorage之间的区别7
缓存 cookie localStorage sessionStorage
大小数量 不能超过4K 5M或更大 5M或更大
有效性 可以设置path路径,限制只属于某个路径。在设置的过期时间之前一直有效,即使窗口或浏览器关闭 始终有效,存储持久数据,浏览器关闭后数据不丢失,除非主动删除数据 数据在当前浏览器窗口关闭后自动删除(会话级)
操作方法 修改读取方法需自己实现 提供了get,set方法 提供了get,set方法
作用域 所有同源窗口中都共享 所有同源窗口中都共享 不在不同的浏览器窗口中共享,即使是同一个页面
数据与服务器之间的交互方式 每次请求都会自动发送到服务器,然后回传给浏览器,服务器端也可以写cookie到客户端 不会自动把数据发给服务器,仅在本地保存 不会自动把数据发给服务器,仅在本地保存

Web Storage带来的好处:
1、减少网络流量:一旦数据保存在本地之后,就可以避免再向服务器请求数据,因此减少不必要的数据请求,减少数据在浏览器和服务器间不必要的来回传递
2、快速显示数据:性能好,从本地读数据比通过网络从服务器上获得数据快得多,本地数据可以及时获得,再加上网页本身也可以有缓存,因此整个页面和数据都在本地的话,可以立即显示
3、临时存储:很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用sessionStorage非常方便
补:web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
       web Storage的api接口使用更方便

6.使用Ajax来增强进程8

待更新ing
 

7. CSS优先加载,JS延迟加载9

  • 将CSS文件放在顶部,JS文件放在底部
CSS加载优化
  • 将首屏页面要用到的关键CSS文件,内联到HTML文档中;
  • 异步加载CSS:loadCSS 和 Preload
  1. 使用JavaScript动态创建样式表link元素,并插入到DOM中。
// 创建link标签
const myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet";
myCSS.href = "mystyles.css";
// 插入到header的最后位置
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
  1. 将link元素的media属性设置为用户浏览器不匹配的媒体类型(或媒体查询),如media=“print”,甚至可以是完全不存在的类型media=“noexist”。对浏览器来说,如果样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。文件加载完成之后,将media的值设为screen或all,从而让浏览器开始解析CSS。

  2. 通过rel属性将link元素标记为alternate可选样式表,也能实现浏览器异步加载。加载完成之后,将rel改回去。

  3. 可以使用 loadCSS和 Preload

    • 考虑兼容问题,可以使用 loadCSS
    • Preload:
// 对于一些不是首屏加载的css,可以如下写法:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.rel='stylesheet'">

// 防止浏览器禁止js,保险起见,也可以如下:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/haorooms.css"></noscript>

// 为了避免有些浏览器会重新调用处理程序rel='stylesheet'这个属性,我们一般推荐如下写法:
<link rel="preload" href="path/to/haorooms.css" as="style" onload="this.οnlοad=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/haorooms.css"></noscript>

// 不考虑兼容问题再次优化
<head>
  <link rel="stylesheet" href="/首屏加载css.css">
  <link rel="preload" href="/不是首屏加载的css.css" as="style" onload="this.οnlοad=null;this.rel='stylesheet'">
</head>
<body>
  <header></header>
  <main></main>
  <section class="comments"></section>
  <section class="about-me"></section>
  <footer></footer>
</body>
  • CSS使用技巧
    1. 有选择的使用选择器
      保持简单,不要使用嵌套过多过于复杂的选择器。
      通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
      不要使用类选择器和ID选择器修饰元素标签,如h3#markdown-content,这样多此一举,还会降低效率。
      不要为了追求速度而放弃可读性与可维护性。

    2. 减少使用昂贵的属性
      如box-shadow/border-radius/filter/透明度/:nth-child等。

    3. 减少重排,避免不必要的重绘

    4. 不使用CSS @import —— CSS的@import会造成额外的请求

    5. 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。

JS加载优化(异步加载)
  1. defer属性
           通过给

你可能感兴趣的:(web前端 网页加载 性能优化大全)