BING
Surmon's digital vihara
OG

Web 性能优化指南

6,011 characters, 15 min read2017/08/09 AM6,751 views

#跳票

不跳了,我决定继续更新,慢慢写

#废话

就是整理一下,不想太乱。

我尽量以一个偏全栈能力的角度去完整地整理 Web 性能优化相关的知识,而不是局限于 Web 客户端的形式化优化。

也许不是每个类别都很详细,事实上几乎每个细节都能够拿出来长篇大论。

#正文

我把 Web 的优化点分为下面四个类别:

  • 程序

    • 算法
    • 设计模式
    • 程序设计
      • 事件代理
      • ...
  • 资源

    • 资源的类型及应用场景
    • 资源的压缩及编译处理
    • 资源的最优化产出形态
  • 网络

    • 数据交互
      • 强缓存
      • 弱缓存
      • HTTP/2
      • HTTPS
      • 跨域
    • 数据传输
      • 数据压缩
      • 数据缓存
    • 网路节点
      • 负载均衡(域名、请求、程序)
      • DNS
      • 数据库连接池
      • CDN
      • ...
  • 渲染

    • 渲染过程
      • 排版
      • 绘制
    • 优化点
      • 回流(重排)
      • 重绘

好,从头开始。

因为我们做优化的本质是让客户端程序(Web App)以条件内最优的状态到达客户端,并最优地执行,所以客户端程序本身才是真正的核心。

而一个丰富的 Web 客户端会包含各种视频、音频、图片、文字、字体...等多种资源,资源的协调者便是 JavaScript 脚本(或 HTML),所以其实从优化来说,各占一半的重要性,OK,先说程序。

#程序

程序(算法、设计模式的优化,还有类似事件代理这样的特定平台优化...)

算法

数据结构、算法,与语言无关,是程序真正的 “原力”,所以 Web 开发者(特别是只专注于 Web site 那种)不要刻意去忽略,甚至应该加以重视。

我们假设几个例子:

我们假设有一个 User-List 用以存储...

[ 待更 ]

设计模式

一般除却游戏开发和框架等基建开发,很少用到设计模式,就先不说了。

每一种设计模式都是现实生活中的思想在程序中的映射,理解便好。

[ 待更 ]

程序设计

  • 避免使用 JavaScript

如果你只是想创建一个例如下拉菜单这样的功能,那么尽量用 CSS 代替 JavaScript,CSS 在客户端本身上进行高度优化,并有浏览器的本地预编译的支持。 如果使用这些预定义函数,不仅可以节省编译 JavaScript 的时间,还可以减少代码体积。至少客户端 API 针对 CSS 平台进行了优化,并且可能会使用 GPU 和其他一些无法用 JavaScript 控制的底层优化。

  • 基本原则

evalwithtry catch...等语句在非必要时候尽量避免使用,js 脚本运行时依赖宿主环境的预读优化生成中间码并执行,这些语句块内的代码都无法被优化,速度可能相差几十倍,不过调试确实很有用。

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
try { eval(code) } catch (e) { if (e instanceof SyntaxError) { alert(e.message) } else { throw(e) } }
  • 变量的就近维护

  • ...

[ 待更 ]

DOM 相关

  • 事件代理
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
<ul id="ul"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul>
          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
// bad code const Li = Ul.getElementsByTagName('li') Li.forEach(li => { li.onclick = event => { alert(li.innerText) } }) // 事件代理 const Ul = document.getElementById("ul") Ul.onclick = event => { event = event || window.event var target = event.target || event.srcElement if (target.nodeName.toLowerCase() === 'li') { alert(target.innerHTML) } }

[ 待更 ]

#资源

简单说,资源优化的目的是:保证首屏 HTTP 请求数量和资源体积之间取得最佳平衡

当然,绝大多数时候,资源都是通过首屏加载完毕的,所以首屏这个节点至关重要。

#资源的类型及应用场景:

图片

图片是最常见的富文本资源形式,常用格式有:jpg png(8/24) gif webp svg...

如果效果真的需要图片来表现,那么选择图片格式是优化的第一步。我们经常听到的词语包括矢量图、标量图、SVG、有损压缩、无损压缩等,首先要知道各种图片格式的特点:

图片格式 压缩方式 透明度 动画 浏览器兼容 适应场景
JPEG 有损压缩 不支持 不支持 所有 复杂颜色及形状、尤其是照片
GIF 无损压缩 支持 支持 所有 简单颜色,动画
PNG 无损压缩 支持 不支持 所有 需要高品质全透明
APNG 无损压缩 支持 支持 Firefox Safari iOS Safari 需要半透明效果的动画
WebP 有损压缩 支持 支持 Chrome、Opera、Android Chrome、Android Browser 复杂颜色及形状、浏览器平台可预知
SVG 无损压缩 支持 支持 IE8+ 简单图形,需要良好的放缩体验/需要动态控制图片特效

其中 APNGWebP 格式出现的较晚,尚未被 Web 标准所采纳,只有在特定平台或浏览器环境可以预知的情况下加以采用,虽然均可以在不支持的环境中较好的功能降级,但这里暂不讨论这两种格式。图片格式选择过程如下:

资源(图片格式的使用及 Base64,CSS 的分包或不分包处理、脚本的模块化处理、字体媒体等其他资源的压缩和生成)...

[ 待更 ]

#网络

  • 客户端节点

Web server(包含 gzip 这类东西)、CDN、HTTP 强弱缓存、HTTP2、跨域预请求的优化)...

HTTP 缓存

首先,服务端给客户端资源响应时,在头部会告诉客户端这个资源的一些元信息,如:最后修改时间、唯一 hash 值、最大缓存时间...,客户端根据这些信息来决定自己要不要发起真正的请求。

强缓存:服务端告诉客户端,某一个资源在某一个时间点之前我一定不会更新,你不需要向我发起请求,所以我们在 Network 可以看到这类请求的 status 为 "from disk cache/from memory cache", 即不需要真正地发起网络请求,而是直接读取本地缓存。

from memory cache:在进程中内存读取数据,一般为窗口未关闭,刷新时,浏览器会直接读取此类数据;一般浏览器用来存放体积较小的资源,如 Base64 的图片,或其他资源等。

from disk cache:在本机磁盘缓存文件中读取数据,一般与进程无关,除非你清空浏览器缓存了;可以看到很多的 js、css 都来自 disk cache。

有一种说法是:三级缓存原理

  1. 先去内存看,如果有,直接加载(from memory cache)
  2. 如果内存没有,择取硬盘获取,如果有直接加载(from disk cache)
  3. 如果硬盘也没有,那么就进行网络请求
  4. 加载到的资源缓存到硬盘和内存

弱缓存:客户端依然向对应地址发起请求,同时会附带上次拉取资源时服务端给的头部信息,如:唯一 hash 值, 服务端根据客户端给的信息来判断客户端缓存的文件和他要访问的存放在服务端的文件是否一模一样未被修改,若未被修改,则返回空数据,HTTP code 为 304, 客户端收到状态码便知道服务端的资源没有变动,可以直接使用本地缓存。

注意,两种缓存是建立在本地已有此资源的缓存的情况下。

在不同版本的 HTTP 协议中,由于新版协议需要兼容旧的协议,所以浏览器及 Web server 在处理 header 头时也会有优先级判断,且在某些极端情况下,不同的元信息字段可能会带来不同的结果。

last-modified(HTTP/1.0),资源的最后更新时间,精确到秒,若一个资源在 1 秒内被修改多次,那么这个字段提供的信息将会导致异常结果,所以只适用于对资源变动不敏感的场景。

ETag(HTTP/1.1)假设一个资源 a 被修改为 b,又被修改为 a,那么 ETag 可能是无法体现出来的,事实上任何一个 Web server 可能都有自己的计算规则,所以难以作证。

一般我们会同时使用,Web server 若支持 HTTP/1.1 协议,则会优先验证 ETag

[ 待更 ]

  • 网络节点

负载均衡、智能 DNS、Web server 分发,数据库连接池分发、多级缓存)...

[ 待更 ]

#数据交互

[ 待更 ]

#数据传输

因为在 HTTP/1.1 及之前版本中,浏览器对同一个域(域名、端口、协议)的最高并发数是有限制的,不同浏览器大概是 2-8 个左右的限制。

旧的方案有:静态资源多域名、CDN 域名,来分担 HTTP/1.1 浏览器对连接并发的限制, 缺点是,首次加载会有 DNS 压力,(具体关于 DNS 的整个过程怎么解析的,可以阅读 网络链路和跨域 )优点是利用不同域名突破资源加载瓶颈, 使静态资源路径与业务分离,可以减少很多没必要的 header 头数据的传输。

从 HTTP/2 开始,协议加入了 多路复用、二进制分帧、首部压缩、服务端推送 等几个核心的特性。

多路复用:允许同时通过单一的 HTTP/2 连接发起多重的请求 - 响应消息。

二进制分帧:将 header、body 封装为更小粒度的二进制数据,用于在 “应用层” 和 “传输层” 之间交换数据,且数据的交互都在一个 HTTP/2 连接上完成,无需 HTTP/1.1 的频繁握手等底层连接动作。

首部压缩:将重复的 header、字段信息合并,使用 HPACK 算法将信息进行压缩,相比 HTTP/1.1 减少了大量无用堆叠、重复、庞大数据的传输。

服务端推送:服务端能够更快的把资源主动推送给客户端。例如服务端可以主动把 js 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 再发送这些请求。当客户端需要的时候,它已经在客户端了。

[ 待更 ]

#网络节点

这个部分和 网络链路和跨域 解析有重合

[ 待更 ]

部分资源参考源:

https://segmentfault.com/q/1010000002433991

#渲染

#渲染过程

  1. 浏览器解析 HTML 源码,然后创建一个 DOM 树。在 DOM 树中,每一个 HTML 标签都会有一个对应的节点,并且每一个文本也都会有一个文本节点。
  2. 浏览器解析 CSS 代码,计算出最终的样式数据。对 CSS 代码中的非法数据会直接忽视,优先级定义为:浏览器默认设置,用户设置,外链样式,内联样式,HTML 中的 Style。
  3. 构建 DOM 树,并计算出样式数据后,下一步就是构建渲染树。DOM 树和渲染树相似,但是有区别,DOM 树完全和 HTML 标签对应,但是在渲染树中会忽略不需要渲染的元素。
  4. 渲染树创建好了之后,浏览器就可以根据渲染树将页面绘制到屏幕之上。

#回流(重排)和重绘

  1. 回流:浏览器要花时间去渲染,当它发现某个部分发生了变化影响布局,那就需要倒回去重新渲染。
  2. 重绘:如果只是改变了某个元素的背景颜色,文字颜色登,不影响元素周围或者内部元素布局的属性,就只会引起浏览器重新画某一部分,而不是全部重新绘制。

因为回流是倒回去重新渲染,所以回流比重绘更花费时间,也就更加影响性能。所以在书写HTML代码时候尽量避免过多的回流。

回流的原因

  1. 页面初始化的时候
  2. 操作 DOM 的时候
  3. 某些元素的尺寸改变了
  4. 如果 CSS 的属性发生了变化

#如何优化

  1. 不要一条条的修改 DOM 属性,可以预先定义好 CSS 的 Class,然后修改 DOM 的 className;
  2. 不要把 DOM 节点的属性值当成循环的变量;
  3. 为动画的 HTML 原件使用 Flexd 或者 absolute 的 position,那么修改他们的 CSS 不会产生回流;
  4. 尽量不要使用 Table 布局,因为很小的改动会造成整个 Table 的重新布局。

[ 待更 ]

Creative Commons BY-NC 4.0 https://surmon.me/article/68
8 / 8 comments
Guest
Join the discussion...
  • 淘宝美工
    淘宝美工🇨🇳CNShanghaiMac OSChrome

    还可以

  • lihsai0
    Lihsai0🇨🇳CNTianjinAndroidChrome

    看到createDocumentFragment表示很开心,毕竟自己引发出来的讨论,哈哈哈😜😜

  • 浪浪149
    浪浪149🇨🇳CNShanghaiWindowsChrome
  • 浪浪149
    浪浪149🇨🇳CNShanghaiWindowsChrome

    一起守过长城的,就叫战友!

  • SkyRover
    Skyrover🇨🇳CNXi'anMac OSChrome

    2333333333333

  • jooger
    Jooger🇨🇳CNGuangzhou ShiWindowsChrome

    沙发,占坑,坐等更新

    • Surmon
      Surmon🇨🇳CNXiamenMac OSChrome

      reply:

      来自长者的慈爱关怀已进了炮膛,好好感受下温暖

    • jooger
      Jooger🇨🇳CNGuangzhou ShiMac OSChrome

      reply:

      2333333