(翻译) 什么让一个网站“高性能”?

原文:http://www.sitepoint.com/what-makes-website-high-performance/

什么是性能?

创建一个高性能的web 站点是一种微妙的舞蹈。很多人认为它仅仅关于你的 JavaScript 跑得多快。事实是,它远远不止于此。也有很多方式以测量性能。
首先是加载时间。从用户点击链接,或者往地址栏中输入一个 URL 的时刻开始,加载那个页面花了多长时间? 甚至这个都有多种定义,从一方面来说,完全载入整个页面需要花多长时间,而另一方面,则是用户对页面花了多久才能做有用的事或者有所响应的感知。
例如,你可以载入主要内容以使页面可以交互,与此同时,你程序化的返回服务器,抓取余下的内容。
第二种性能度量则更加复杂。这个是关于你的站点上的动画和交互在浏览器中表现的有多快的。

Web 运行时架构


在这两种情况下,都需要对 web 运行时架构有相当的理解以协调任何一个 case. 我在这里故意说 web 运行时架构而不是 IE 运行时架构,因为在这个级别上讨论它时,所有的浏览器具有大致相同的架构,因为它们都在实施相同的标准。它们可能会给它们的子系统起不一样的名称,或者会为个别子系统做优化,但是总体来说,这是所有浏览器的流程。

网络


发生的第一件事是网络活动。当 URL 被初次敲入时,网络活动就开始了。现在,我将从 HTML 页面开始讨论,但是,网络处理页面所引用的无论哪种类型的资源,包括 XML, JSON, 图像和 PDF 等,而不同的子系统在处理不同的资源方面发挥作用。
但是让我们从一个 HTML 文件开始。首先,浏览器开始下载 HTML 文件,而随着页面被下载,预处理就开始了。预处理检查文件,寻找 HTML引用的,浏览器可以开始下载的任何其它东西。这包括 CSS 文件,JavaScript 文件,图像,或者这个文件所引用的任何东西。
这很重要,因为浏览器需要尽可能快的获取所有引用的内容 - 由于这些内容相互依赖; HTML 依赖 CSS 和 JavaScript, JavaScript 只能与通过 HTML 生成的 DOM 交流,而没有了 HTML DOM(就是 JavaScript可以操作的), CSS 就没有任何东西可以添加样式。是的,有点令人不解,但是不久之后,我们就可以指望它具有更多的意义了。

需要考虑的一个重要问题是,根据标准规范,对任何一个指定的服务器,浏览器只被允许同时打开至多 6 个连接,而在 HTML 中指定的每一个资源,都需要一个连接。当你查看具有大量图片内容,JavaScript 文件和 CSS 文件的页面,如 Facebook, Pinterest 或者类似页面时,这听起来不仅仅像是很多连接。事实上,它听起来令人瘫痪。
它事实上是来自开始时的规范 -只允许两个同时连接! 这个限制的理由是,帮助服务器检测拒绝服务攻击 (DOS 或 DDOS),在这种情况下,客户端尽可能多的打开连接,从服务器端消耗它们所有可能消耗的尽可能多的带宽。关于 6 个连接限制的好消息是,它允许浏览器同时获得更多内容。
我所看见的绝大多数 web 站点让它们的所有内容来自同一个站点。有大量图片,CSS, JavaScript 或其它外部资源时,最好的实践是把这些资源分散在不同域中。这是使用 CDN (Content Delivery Network) 所立刻获得的好处之一。这也是为什么使用通用 JavaScript 框架的 CDN 版本 (原文 hosted version) 而不是把它放在你自己的服务器有意义。

解析器


当一个项目被下载时,它便交给解析器处理。有几十种解析器,它们解析一切种类的内容,包括 - 但不限于 - HTML, CSS, XML, XHTML, SVG, JavaScript 以及所有这些东西的变体。解析器的工作是创建浏览器在余下的处理中所会用到的内在的数据结构。
我在过度的简化解析器的工作,而仅仅覆盖这些,就可以是一篇完整的独立的文章了。关于这个对话,当前重要的部分是,写格式良好的文档 - HTML, XML, XSLT, JSON... 可以节约你的载入时间。

内部数据结构


有很多浏览器为完成其任务,所需要的不同内部数据结构。它们之中,最广为人知的就是 DOM (Document Object Model)。如果你之前在一个 web 站点上做过任何 JavaScript 编程,你很可能已经在一定水平上与 DOM 交互了。通常的思想是,DOM 缓慢而痛苦。而事实更加复杂。
每当某种东西接触 DOM 时,它就会影响到该点下游的一切东西。那为什么是个问题就稍微清楚一点了。另一个需要理解的重要的结构是 CSS 层叠。这是在HTML 和所有 CSS 文件中引用的所有 CSS 规则,以及通过 JavaScript 编程设定的规则的合并。它包含所有的优先级顺序,以决定哪个覆盖哪个,等等。

JavaScript


JavaScript 沙盒是 JavaScript 解析,字节码生成,原生代码生成以及全部的 JavaScript 执行所发生的地方。这就是为什么 JavaScript 在浏览器内部运行是安全的。不是因为它是一个安全的语言。恰恰相反,它是一种难以置信的强大语言,如果不加以检查,有可能猖獗的运行于你机器的一切之上。
相反,它很安全,因为 JavaScript 只有两种方式访问沙盒外部 - 调用 DOM API 或者通过 XHR 请求调用网络。有数不胜数的辅助这两个任务的框架 - jQuery 是最流行的之一。作为一个DOM选择器,$("path statement") 句法难以置信的强大。$.getJSON() 方法是发出 AJAX 请求的一种极为简单易用且快速的方法。

样式 - 格式与布局


接下来的步骤,添加样式,实际上包括了两个子系统。
第一个样式子系统是格式化。格式化很重要,因为 DOM 树对视觉方面的任何东西毫不知情。它所知道的全部,仅仅是元素之间的父子关系和属性。是 CSS 的层叠才知道全部那些视觉信息。在格式化的期间,这些信息被连接起来,给予 DOM 元素尺寸,颜色,背景,字号,等等。
在这个子系统结束时,每个单一的元素都知道了它们看起来是什么样的。
下一步,既然所有 DOM 元素知道它们看起来什么样,就是确定在页面上,它们在一起看起来是什么样的 - 这就是布局的过程。

CSS 本质上是一个基于块 (blocks) 的布局。这意味着一切元素: 图像,段落,divs, spans, 奇形怪状 (原文 event shapes) 如圆形,等等,事实上对于 CSS 来说都是块。大问题是一个块到底有多大。而 HTML/CSS 从本质上是一个基于流的布局,除非某些东西覆盖了它。这意味着内容,在默认情况下,将会跑到下一个可用的,它可以占据的左上方 /* 原文 top right, 但是我真心怀疑原作者左右不分 =w= */ 角落。
所以如果你的屏幕是 800px 宽,你有 4 个每个宽 300px 的元素 /* 不保证准确的注释: 根据上下文,貌似这些元素需要 display: inline-block 或者是 inline 替换元素 (e.g. img, 表单元素当 display: inline 时等) */,你在最顶端将可以容纳两个元素,第三个则会到下一行。然而,CSS 层叠可以覆盖这个,把元素通过一个相对或绝对的方式定位。如果是相对的,它的位置将会是偏移上一个在屏幕上被布局的元素一定距离。/* 虽然我个人认为,说成 “偏移它本来应该被布局的位置一定距离” 更为合适。 */
如果是绝对定位,它忽略了所有这些规则,而被布局到距屏幕的角落的一定距离。/* 不保证准确性: 这是在指定 top or bottom 之一和 left or right 之一各自一定距离的情况下。如果不指定任何 top, bottom, left or right, 这个元素就会出现在本来在文档流中出现的位置,但是会在不指定宽度的情况下尽可能收缩,也不占据任何的空间。 */在这种情况下,它很有可能甚至被定位于覆盖屏幕上的另一个元素。
由此可见,排版引擎的首要任务是把所有这些块放到屏幕上。这包括根据元素的相对或绝对定位放置元素,包裹或缩放太宽的元素以及所有其它进入所需的这个俄罗斯方块的闪电轮 (原文:that go into that lightning round of Tetris that is required)的一切。
布局阶段结束时,浏览器有一个叫做渲染树的内部结构。这是一个有趣的数据结构。有些业余人士问,为什么浏览器不只用 DOM 树,但是 DOM 树中的元素和渲染树中的项目不是一个 1-1 的比例关系。也许需要相当一段时间你的脑袋才能转过来,但是这是有意义的。
在 DOM 树中,有些东西并不在渲染树上,比如 display: none 的元素,和 type 为 hidden 的 input 域。也有一些在渲染树上的东西并不在 DOM 中,如有序列表中的序号。

<ol><li>Text</li></ol>

绘制和合成


在这一点上,样式已经完成,渲染树也准备好绘制了。选用词语是有讲究的,由于浏览器已经准备好绘制 (ready to paint) 但是目前还没有 painting. 下一步需要硬件告诉浏览器下一次对屏幕绘制的时刻。显示器每秒只能绘制那么多次。绝大多数现代显示器是 60 次每秒,因此是 60Hz 的刷新率。
这意味着在绝大多数显示器上,两次刷新之间是 1000/60 ms, 约 66.67 毫秒。这听起来不像很长时间,但是事实上,如今一个 i7 处理器在这段时间内可以执行约2138333333 次指令 (根据维基百科所提的每秒运算指令数除以 60 得到)。在你和我之间,是大量的指令。
它不是无穷大,但是它已经很多了。它意味着电脑可以在显示器的两次刷新之间,执行约 3~4e7 次指令 /* 是不是应该 @Ent */。
当显示器准备好绘制时,它发送硬件中断,引起绘制周期。在这一点,浏览器把页面的分层绘制到表面 /* surface 我不知道怎么翻译,反正不是那种平板电脑 */,对于 IE 来说就是 DirectX 表面,被桌面窗口管理器 (DWM) 在显示器上合成和显示给你看 (原文: for your user's enjoyment)。

交互


在这一点上,浏览器已经把东西显示到屏幕上了,所以这是载入时间。然而,还没有任何动画,或任何交互。

交互包括像是鼠标,指针或触控事件之类的东西。这些都在一定方式或形式上触动了页面。当一个用户以某种方式接触了屏幕 /* 包括鼠标等 */,浏览器必须查看格式和布局以获取何处被接触 /* event.target 这种东西 */。随后它必须检查 DOM, 以得知当该项目被接触时,是否有一个对应的事件需要触发。如果事件被发现了,JavaScript 就可以启动,执行事件代码,接触 DOM 树。
在这个点上,你有希望看到这里的问题了。接触 DOM 树意味着渲染树不再是 DOM 树的一个准确表示。这意味着浏览器需要重做布局和格式化,准备重绘。
这就是之前当我说描述 DOM 为 “缓慢” 而事实更加复杂时,我的意思。不是操作 DOM 自身非常缓慢。缓慢的是接触它时,会造成强迫其它子系统触发的多米诺效应。

Web 运行时架构的回顾


可以期望这给你整个 web 运行时架构的一个图景。没有单一的组件驱动你的站点的性能。是无数种东西,而事实上,有时候聚焦一个东西将会引起另一个的牺牲。例如,如果你把所有的 JavaScript 文件放在同一个文件中,你就只需要下载一个文件了,但它也意味着,你的页面有可能被阻塞,直到整个 JavaScript 文件被下载和解析为止。
理解应该做什么以获取最优性能和减轻这些决定所带来的冲击,是区分好和真正优秀的 web 开发者的所在。

本文由we_cry授权(果壳网)发表,文章著作权为原作者所有。
推荐
13条评论

  • 1楼
    2013-12-04 17:39 moogee

    居然是坑。。

    评论
  • 2楼
    2013-12-04 19:12 K_C

    (稍后继续。。。)

    评论
  • 3楼
    2013-12-04 19:13 we_cry
    引用@K_C 的话:(稍后继续。。。)


    Kerwin Che on G+?

    评论
  • 4楼
    2013-12-04 19:33 飞翔的鱼

    话说不是稍候继续么。。。

    评论
  • 5楼
    2013-12-04 19:34 we_cry
    引用@飞翔的鱼 的话:话说不是稍候继续么。。。


    My fault...

    评论
  • 6楼
    2013-12-05 00:11 we_cry
    引用@moogee 的话:居然是坑。。


    引用@K_C 的话:(稍后继续。。。)


    引用@飞翔的鱼 的话:话说不是稍候继续么。。。

    写完了!!!!!

    Welcome to Eye and Vision group
    评论
  • 7楼
    2013-12-05 00:13 Administrator_su

    不明觉厉

    评论
  • 8楼
    2013-12-05 00:15 we_cry
    引用@正装怪人李叔叔 的话:不明觉厉


    你是狸猫。。。

    评论
  • 9楼
    2013-12-05 00:17 Administrator_su
    引用@we_cry 的话:你是狸猫。。。

    你是cry。。。

    评论
  • 10楼
    2013-12-05 08:51 飞翔的鱼
    /* 不保证准确的注释: 根据上下文,貌似这些元素需要 display: inline-block 或者是 inline 替换元素 (e.g. img, 表单元素当 display: inline 时等) */


    或者最简单的。。Float

    评论
  • 11楼
    2013-12-05 12:30 春田花花花手镯

    "由于浏览器已经准备好绘制 (ready to paint) 但是目前还没有 painting. 下一步需要硬件告诉浏览器下一次对屏幕绘制的时刻。显示器每秒只能绘制那么多次。绝大多数现代显示器是 60 次每秒,因此是 60Hz 的刷新率。"

    绘制流程明明不是这样的T.T

    ”两次刷新之间是 1000/60 ms, 约 66.67 毫秒。这听起来不像很长时间“

    不可能只由CPU来跑这个,你看逗逼Flash就卡得很

    评论
  • 12楼
    2013-12-05 12:38 we_cry
    引用@飞翔的鱼 的话:或者最简单的。。Float


    Yep.

    评论
  • 13楼
    2013-12-05 12:42 we_cry
    引用@春田花花花手镯 的话:由于浏览器已经准备好绘制 (ready to paint) 但是目前还没有 painting. 下一步需要硬件告诉浏览器下一次对屏幕绘制的时刻。显示器每秒只能绘制那么多次。绝大多数现代显示器是 60...


    貌似这里也没说是 CPU 跑这个啊。。。

    Welcome to Eye and Vision group
    评论

你的评论

回复请先登录
we_cry 空间信息与数字技术专业 we_cry的新浪微博 发表于 2013-12-04 16:14

©2017果壳网    京ICP证100430号    京网文[2015] 0609-239号    新出发京零字东150005号     京公网安备11010502007133号

违法和不良信息举报邮箱:jubao@guokr.com    举报电话:13488674940