前端技术演进(一):Web前端技术基础

11,233 阅读31分钟
这个来自之前做的培训,删减了一些业务相关的,参考了很多资料(参考资料列表),谢谢前辈们,么么哒 😘

每个开发者或多或少都接触过前端,但现在的前端变化有点快,这里是截至2018年的前端,暂且叫:现代前端技术演进。

互联网信息呈现的方式主要依靠终端设备屏幕,如今,终端设备的种类越来越多,智能化越来越高,交互也越来越复杂。

image.png | center | 827x411.29466666666667

苹果官网的演进

1992年:

image.png | left | 469x240

1996年:

image.png | left | 683x427

1998年:

image.png | left | 683x427

1999年:

image.png | left | 683x427

2000年:

image.png | left | 550x436

2001年:

image.png | left | 683x427

2003年:

image.png | left | 683x427

2005年:

image.png | left | 480x407

2006年:

image.png | left | 827x516.875

2007年:

image.png | left | 827x516.875

2008年:

image.png | left | 827x516.875

2010年:

image.png | left | 827x516.875

2011年:

image.png | left | 827x516.875

乔布斯去世:

image.png | left | 827x516.875

2012年:

image.png | left | 827x516.875

2013年:

image.png | left | 827x516.875

2014年:

image.png | left | 827x516.875

2015年:

image.png | left | 827x516.875

2016年:

image.png | left | 827x385.4697845507094

2017年:

image.png | left | 827x462.73704520396916

2018年:

image.png | center | 827x562

苹果的发展是IT行业的缩影,包含了工业设计、产品设计、硬件技术、用户体验、UI设计、前端技术等方方面面。

仅从前端来看,通过苹果官网的演进,可以看到,随着互联网和移动互联网的快速发展,前端技术也发生了巨大的变化。

前端应用开发模式演变

1、静态黄页
2、服务器组装数据的动态网页
3、后端为主的 MVC
4、前后端分离
5、前端 MV* 框架
6、前端 VIrtual DOM,MNV*,前后端同构

image.png | center | 827x308.0575

所有变化,都是围绕提升前端开发的效率和质量。

浏览器基础

前端大部分时间都是在和浏览器打交道,所以我们先从浏览器说起。

从一个常见的前端面试题说起:

从我们打开浏览器输入一个网址到页面展示网页内容这段时间内,浏览器和服务端都发生了什么事情?

这个问题也有一种发散性更强的说法是:从我们打开浏览器输入一个网址到页面展示网页内容这段时间内,都发生了什么事情?比如下面是百度FEX(Web 前端研发部)的面试题:

image.png | center | 827x435

github.com/fex-team/in…

越详细越好,有人可能从移动光电鼠标说起:发光二极管发出的光线照亮鼠标底部表面,然后将鼠标底部表面反射回的一部分光线,经过一组光学透镜,传输到一个光感应器件内成像,当鼠标移动时,其移动轨迹便会被记录为一组高速拍摄的连贯图像,通过DSP分析图像,就可以判断鼠标的移动方向和移动距离,从而完成光标的定位。。。从鼠标键盘屏幕计算机、输入输出、操作系统、编译原理啥的说起,可以说一天,所以这里只关注浏览器和服务端主要的过程。

一个简化的过程是这样的:

  1. 在接收到用户输入的网址后,浏览器会开启一个线程来处理这个请求,对用户输入的URL地址进行分析判断,如果是HTTP协议就按照HTTP方式来处理。
  2. 调用浏览器引擎中的对应方法,比如WebView中的loadUrl方法,分析并加载这个URL地址。
  3. 通过DNS解析获取该网站地址对应的IP地址,查询完成后连同浏览器的Cookie、userAgent等信息向网站目的IP发出请求。
  4. 进行HTTP协议会话,浏览器客户端向Web服务器发送报文。
  5. 进入网站后台上的Web服务器处理请求,如Apache、Tomcat、Nginx、Node.js 等服务器。
  6. 进入部署好的后端应用,如PHP、Java、 JavaScript、 Python 等后端程序,找到对应的请求处理逻辑,这期间可能会读取服务器缓存或查询数据库等。
  7. 服务器处理请求并返回响应报文,此时如果浏览器访问过该页面,缓存上有对应资源,会与服务器最后修改记录对比,一致则返回304,否则返回200和对应的内容。
  8. 浏览器开始下载HTML文档(响应报头状态码为200时)或者从本地缓存读取文件内容(浏览器缓存有效或响应报头状态码为304时)。
  9. 浏览器根据下载接收到的HTML文件解析结构建立DOM文档树,并根据HTML中的标记请求下载指定的MIME类型文件(如CSS、JavaScript 脚本等),同时设置缓存等内容。
  10. 页面开始解析渲染DOM、CSS根据规则解析并结合DOM文档树进行网页内容布局和绘制渲染,JavaScript 根据DOM API操作DOM,并读取浏览器缓存、执行事件绑定等,页面整个展示过程完成。

下图是在服务端的一些展开:

image.png | center | 827x551.3333333333333

作为前端,重点会关注这个过程中地址栏输入、网络请求、浏览器文档解析、渲染引擎、Javascript执行引擎,客户端存储等部分。所以我们先了解一下浏览器:

浏览器通常由如下七个部分组成:

image.png | center | 500x339

  1. 用户界面(User Interface): 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示请求的页面外,其他显示的各个部分都属于用户界面。
  2. 浏览器引擎(Browser engine): 在用户界面和呈现引擎之间传送指令,或者在客户端本地缓存中读写数据等,是浏览器中各个部分之间相互通信的核心。
  3. 呈现(渲染)引擎(Rendering engine): 负责显示请求的内容,并将内容排版到浏览器中显示成有样式的界面。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上,也叫排版引擎,常说的浏览器内核一般也主要是指呈现引擎。
  4. 网络功能模块(Networking): 用于网络调用,比如 HTTP 请求。它的接口与平台无关,并为所有平台提供底层实现。
  5. 用户界面后端(UI Backend): 用于绘制基本的窗口小部件,比如组合框和窗口。它公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
  6. JavaScript 解释器(Javascript Interpreter): 用于解析和执行 JavaScript 代码。
  7. 数据存储(Data Persistence): 持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie、localStorage。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

浏览器呈现引擎简介

呈现引擎的作用嘛...当然就是“呈现”了,也就是在浏览器的屏幕上显示请求的内容。

目前,主流的浏览器内核有4类:

  • Trident:IE、360、搜狗等;
  • Gecko:Netscape、Firefox等;
  • Presto:Opera(新的 Opera 使用了 Blink 内核,是 Webkit 的一个分支,添加了一些优化特性);
  • Webkit:Safari、Chrome

呈现引擎一开始会从网络层获取请求文档的内容(内容的大小一般限制在 8000 个块以内)。

然后进行如下所示的基本流程:

image.png | center | 600x66

  1. 解析HTML构建DOM树时呈现引擎会先将HTML元素标签解析成由多个DOM元素对象节点组成的且具有节点父子关系的DOM树结构(DOM tree)
  2. 然后根据DOM树结构的每个节点顺序提取计算使用的CSS规则,并重新计算DOM树结构的样式数据,生成一个带样式描述的DOM渲染树(render tree)对象。
  3. DOM渲染树生成结束后,进入渲染树的布局(Layout)阶段,即根据每个渲染树节点在页面中的大小和位置,将节点固定到页面的对应位置上,这个阶段主要是元素的布局属性(例如position、float、margin 等属性)生效,即在浏览器中绘制页面上元素节点的位置。
  4. 接下来就是绘制阶段,将渲染树节点的背景、颜色、文本等样式信息应用到每个节点上,这个阶段主要是元素的内部显示样式(例如color、background. text-shadow 等属性)生效,最终完成整个DOM在页面上的绘制(Painting)显示。

这是一个渐进的过程。为达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。

image.png | center | 624x289

Webkit主流程

image.png | center | 624x290

Gecko主流程

两种呈现引擎工作流程的主要区别在于解析HTML或CSS文档生成呈现树的过程:

  • Webkit 内核中的HTML和CSS解析可以认为是并行的;
  • Gecko 则是先解析HTML,生成内容槽 (Content Sink)后再开始解析CSS。

这两种呈现引擎工作过程中使用的描述术语也不一样:

  • Webkit 内核解析后的渲染对象被称为呈现树(Render Tree),由“呈现对象”组成;
  • Gecko 内核解析后的渲染对象则称为Frame树(Frame Tree),每个元素都是一个框架。

对于元素的放置:

  • WebKit 使用的术语是“布局(Layout)”;
  • Gecko 使用的术语是“重排(Reflow)”。

但是它们主要的流程是相似的,都经过HTML DOM解析、CSS样式解析、呈现树生成和呈现树绘制显示阶段。一般呈现引擎的解析过程中都包含了HTML解析和CSS解析阶段,这也是呈现引擎解析流程中最重要的两个部分。

什么是解析

解析文档是指将文档转化成为有意义的结构,也就是可让代码理解和使用的结构。解析得到的结果通常是代表了文档结构的节点树,它称作解析树或者语法树。

比如:2 + 3 - 1 解析后会变成下面的树:

image.png | center | 400x155

解析的过程可以分成两个子过程:词法分析(lexer)和语法分析(parser)。简单来说,一个是拆分标记,一个是匹配规则。

image.png | center | 101x300

一般都会用一些工具来生成解析器,Webkit 用了两个知名的解析器生成器:词法分析器 Flex ,语法分析器 Bison(Lex/Yacc)。

HTML文档解析

<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

会被翻译成如下的DOM树:

image.png | center | 400x219

HTML的解析规则比较复杂,因为HTML有很高的容错性,比如:

</br><br><br /> 都要解析成空行;

<table>
    <table>
        <tr><td>inner table</td></tr>
    </table>
    <tr><td>outer table</td></tr>
</table>

<!-- 离散表格会被解析成 -->

<table>
    <tr><td>outer table</td></tr>
</table>
<table>
    <tr><td>inner table</td></tr>
</table>

CSS解析

Webkit会把CSS文件解析成 StyleSheet 对象,每个对象都包含CSS规则,CSS 规则对象则包含选择器和声明对象,以及其他与 CSS 语法对应的对象。

image.png | center | 500x393

一个节点上如果有多条不同的样式规则,会通过权重的方式来计算。一般认为是:

!important > 内联样式规则 > id选择器 > 类选择器 > 元素选择器

在 DOM 树构建的同时,浏览器还会构建另一个树结构:呈现树。这是由可视化元素按照其显示顺序而组成的树,也是文档的可视化表示。它的作用是按照正确的顺序绘制内容。

image.png | center | 731x396

布局

呈现器在创建完成并添加到呈现树时,并不包含位置和大小信息。计算这些值的过程称为布局(layout)或重排(reflow)。

布局是一个递归的过程。它从根呈现器(对应于 HTML 文档的 <html> 元素)开始,然后递归遍历部分或所有的框架层次结构,为每一个需要计算的呈现器计算几何信息。

布局分为全局布局和增量布局。全局布局是指触发了整个呈现树范围的布局,触发原因可能包括:

  • 影响所有呈现器的全局样式更改,例如字体大小更改。
  • 屏幕大小调整。

增量布局是只对改变的部分(dirty呈现器)进行布局。

绘制

在绘制阶段,系统会遍历呈现树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。

和布局一样,绘制也分为全局(绘制整个呈现树)和增量两种。

在发生变化时,浏览器会尽可能做出最小的响应。因此,元素的颜色改变后,只会对该元素进行重绘(repaint)。元素的位置改变后,只会对该元素及其子元素(可能还有同级元素)进行布局(layout)和重绘(repaint)。添加 DOM 节点后,会对该节点进行布局(layout)和重绘(repaint)。一些重大变化(例如增大“html”元素的字体)会导致缓存无效,使得整个呈现树都会进行重新布局(relayout)和绘制(repaint)。

浏览器数据持久化存储

浏览器缓存(Browser Caching)是浏览器端用于在本地保存数据并进行快速读取,以避免重复资源请求的传输机制的统称。有效的缓存可以避免重复的网络资源请求并让浏览器快速地响应用户操作,提高页面内容的加载速度。浏览器端缓存的实现机制种类较多,一般可以分为九种:

  • HTTP文件缓存
  • LocalStorage
  • SessionStorage
  • indexDB
  • Web SQL
  • Cookie
  • CacheStorage
  • Application Cache
  • Flash缓存

图片描述

HTTP文件缓存

HTTP文件缓存是基于HTTP协议的浏览器端文件级缓存机制。在文件重复请求的情况下,浏览器可以根据HTTP响应的协议头信息判断是从服务器端请求文件还是从本地读取文件,Chrome控制台下的Frames就可以查看浏览器的HTTP文件资源缓存列表内容。

HTTP文件缓存处理的流程图如下:

image.png | center | 747x464

比如:

image.png | center | 400x528.8000000000001

如果同时设置了 Expires 和 Cache-Control,则只有 Cache-Control 生效。

image.png | center | 500x553.5279805352799

localStorage

localStorage 是HTML5的一种本地缓存方案。支持目前的主流浏览器,在不同浏览器中的长度限制各不相同,比如 Chrome 是 2.6MB,IE 是 5MB,这个长度限制是指单个域名下的 localStorage 大小限制。

image.png | left | 827x291

localStorage 不适合存放太多的数据,它遵循同源策略,但是它是持续存在的。

当浏览器进入隐私浏览模式,会创建一个新的、临时的数据库来存储localStorage的数据;当关闭隐私浏览模式时,该数据库将被清空并丢弃。

sessionStorage

sessionStorage 和 localStorage 类似,不过 sessionStorage 在浏览器关闭时会自动清空。用到的比较少,比如可以自动保存表单输入框的内容,如果浏览器因偶然因素被刷新了,输入框里面的内容会被恢复,因此写入的内容不会丢失。

Cookie

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。一条Cookie记录主要由键、值、域、过期时间和大小组成,一般用于保存用户网站认证信息或个性化设置。Cookie 最大长度限制一般为 4KB。

持久型Cookie会保存在用户的硬盘上。

image.png | center | 827x264.6714150047483

WebSQL

WebSQL是独立于HTML5的单独规范,现在支持的浏览器很少,所以用的不多。

WebSQL允许用SQL语句进行查询,相当于集成在浏览器里的小型数据库。

image.png | center | 827x404.0266253869969

IndexDB

IndexDB支持的浏览器比较广泛,一般推荐使用IndexDB来进行大量数据的存储。浏览器对IndexDB的大小限制通常约为50MB。

DEMO:girliemac.com/stickies/

Application Cache

Application Cache 是一种允许浏览器通过manifest 配置文件在本地有选择性地存储JavaScript、CSS、图片等静态资源的文件级缓存机制。当页面不是首次打开时,通过一个特定的manifest文件配置描述来选择读取本地Application Cache里面的文件。
.
使用Application Cache来实现浏览器应用具有以下三个优势。

  1. 离线浏览:通过manifest配置描述来读取本地文件,用户可在离线时浏览完整的页面内容。
  2. 快速加载:由于缓存资源为本地资源,因此页面加载速度较快。
  3. 服务器负载小:只有在文件资源更新时,浏览器才会从服务器端下载,这样就减小了服务器资源请求的压力。

image.png | center | 827x327.6083995459705

Application Cache 已经开始慢慢被弃用,被 Service Worker 取代。

cacheStorage

cacheStorage是在ServiceWorker规范中定义的,可用于保存每个ServiceWorker声明的Cache对象,是未来可能用来代替Application Cache的离线方案。

示例代码:

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('mysite-static-v3').then(function(cache) {
      return cache.addAll([
        '/css/whatever-v3.css',
        '/css/imgs/sprites-v6.png',
        '/css/fonts/whatever-v8.woff',
        '/js/all-min-v4.js'
        // etc
      ]);
    })
  );
});

演示:fed.renren.com/

具体使用可以参考:developers.google.com/web/fundame…

前端调试工具——Chrome 开发者工具

Chrome 开发者工具是一套内置于Google Chrome中的Web开发和调试工具,可用来对网站进行迭代、调试和分析,是前端开发人员最常用的调试工具之一。

元素面板(Elements)

使用 Chrome DevTools 的 Elements 面板检查和实时编辑页面的 HTML 与 CSS。

image.png | center | 747x433

🖥 演示:

  • 实时编辑DOM节点;
  • 查看元素事件侦听器;
  • 实时编辑样式;
  • 检查和编辑盒模型参数;
  • 查看本地更改;
  • 切换 Device Mode
  • 模拟传感器

控制台面板(Console)

控制台主要是用来查看动态信息和执行Javascript代码。

使用 Console API 可以向控制台写入信息、创建 JavaScript 配置文件,以及启动调试会话。

  • console.log
  • console.info
  • console.warn
  • console.error
  • console.assert
  • console.count
  • console.time
  • console.timeEnd
  • console.timeStamp
  • console.group
  • console.groupEnd
  • console.profile
  • console.profileEnd
  • console.trace
  • console.clear

🖥 演示:

console.time("Array initialize");
var array= new Array(1000000);
for (var i = array.length - 1; i >= 0; i--) {
    array[i] = new Object();
};
console.timeEnd("Array initialize");
var user = "jsmith", authenticated = false;
console.group("Authentication phase");
console.log("Authenticating user '%s'", user);
// authentication code here...
if (!authenticated) {
    console.log("User '%s' not authenticated.", user)
}
console.groupEnd();
function add(num) {
    if(num > 0) {
        console.trace('num:', num);
        return num + add(num - 1);
    } else {
        return 0;
    }
}
add(5);

便捷函数:

  • $_ 返回最近评估的表达式的值。
  • $(selector) <span data-type="color" style="color:rgb(33, 33, 33)">返回带有指定的 CSS 选择器的第一个 DOM 元素的引用。</span>
  • ?(selector) 返回与给定 CSS 选择器匹配的元素数组。

一些有趣的console:
www.zhihu.com/
www.tmall.com/
www.baidu.com/
www.iqiyi.com/

源代码面板(Sources)

源代码面板主要是用来查看和调试代码。

image.png | center | 827x394

🖥 演示:googlechrome.github.io/devtools-sa…

网络面板(Network)

我们的开发很多都是基于HTTP接口的调试,所以网络面板非常重要。

Network 面板记录页面上每个网络操作的相关信息,包括详细的耗时数据、HTTP 请求与响应标头和 Cookie等等。

Network 面板由五个窗格组成:

image.png | center | 827x423

  1. Controls。使用这些选项可以控制 Network 面板的外观和功能。
  2. Filters。 使用这些选项可以控制在 Requests Table 中显示哪些资源。按住 Cmd (Mac) 或 Ctrl (Windows/Linux) 并点击过滤器可以同时选择多个过滤器。
  3. Overview。 此图表显示了资源检索时间的时间线。如果看到多条竖线堆叠在一起,则说明这些资源被同时检索。
  4. Requests Table。 此表格列出了检索的每一个资源。 默认情况下,此表格按时间顺序排序,最早的资源在顶部。点击资源的名称可以显示更多信息。 提示:右键点击 Timeline 以外的任何一个表格标题可以添加或移除信息列。
  5. Summary。 此窗格可以一目了然地查看请求总数、传输的数据量和加载时间。

Network 面板可以在页面加载期间捕捉屏幕截图。此功能称为幻灯片。

Network 面板突出显示两种事件:DOMContentLoaded 和 load。解析页面的初始标记时会触发 DOMContentLoaded。页面完全加载时将触发 load。

🖥 演示:面板、幻灯片

Filter 文本字段还支持各种关键词,Ctrl+空格可以查看所有命令:

  • domain。仅显示来自指定域的资源。可以使用通配符字符 (*
    ) 来包含多个域。 例如,*
    .com 将显示来自以 .com 结尾的所有域名的资源。 DevTools 会使用它遇到的所有域填充自动填充下拉菜单。
  • has-response-header。显示包含指定 HTTP 响应标头的资源。 DevTools 会使用它遇到的所有响应标头填充自动填充下拉菜单。
  • is。使用 is:running 可以查找 WebSocket 资源。
  • larger-than。显示大于指定大小的资源(以字节为单位)。 将值设为 1000 等同于设置为 1k。
  • method。显示通过指定 HTTP 方法类型检索的资源。 DevTools 会使用它遇到的所有 HTTP 方法填充下拉菜单。
  • mime-type。显示指定 MIME 类型的资源。DevTools 会使用它遇到的所有 MIME 类型填充下拉菜单。
  • mixed-content。显示所有混合内容资源 (mixed-content:all),或者仅显示当前显示的资源 (mixed-content:displayed)。
    scheme。显示通过未保护 HTTP (scheme:http) 或受保护 HTTPS (scheme:https) 检索的资源。
  • set-cookie-domain。显示具有 Set-Cookie 标头并带有与指定值匹配的 Domain 属性的资源。 DevTools 会使用它遇到的所有 Cookie 域填充自动填充下拉菜单。
  • set-cookie-name。显示具有 Set-Cookie 标头并且名称与指定值匹配的资源。 DevTools 会使用它遇到的所有 Cookie 名称填充自动填充下拉菜单。
  • set-cookie-value。显示具有 Set-Cookie 标头并且值与指定值匹配的资源。 DevTools 会使用它遇到的所有 Cookie 值填充自动填充下拉菜单。
  • status-code。仅显示 HTTP 状态代码与指定代码匹配的资源。 DevTools 会使用它遇到的所有状态代码填充自动填充下拉菜单。

🖥 演示:Filter、复制、保存和清除网络信息

一个请求生命周期的主要阶段包括:

image.png | center | 827x347

  • 重定向

    • 立即开始 startTime。
    • 如果正在发生重定向,redirectStart 也会开始。
    • 如果重定向在本阶段末发生,将采集 redirectEnd。
  • 应用缓存

    • 如果是应用缓存在实现请求,将采集 fetchStart 时间。
  • DNS

    • domainLookupStart 时间在 DNS 请求开始时采集。
    • domainLookupEnd 时间在 DNS 请求结束时采集。
  • TCP

    • connectStart 在初始连接到服务器时采集。
    • 如果正在使用 TLS 或 SSL,secureConnectionStart 将在握手(确保连接安全)开始时开始。
    • connectEnd 将在到服务器的连接完成时采集。
  • 请求

    • requestStart 会在对某个资源的请求被发送到服务器后立即采集。
  • 响应

    • responseStart 是服务器初始响应请求的时间。
    • responseEnd 是请求结束并且数据完成检索的时间。

Timing 标签可以查看网络请求完整的耗时信息:

image.png | center | 500x433.2688588007737

  • Queuing:请求排队时间,一般排队是因为请求优先级低,或者被暂停(在 HTTP 1 上,浏览器仅允许每个源拥有六个 TCP 连接)等。
  • Stalled/Blocking:请求等待发送所用的时间。
  • Proxy Negotiation:与代理服务器连接协商所用的时间。
  • DNS Lookup:执行 DNS 查询所用的时间。
  • Initial Connection / Connecting:建立连接所用的时间,包括 TCP 握手/重试和协商 SSL 的时间。
  • SSL:完成 SSL 握手所用的时间。
  • Request Sent / Sending:发出网络请求所用的时间。 通常不到一毫秒。
  • Waiting (TTFB):等待初始响应所用的时间,也称为至第一字节的时间。 此时间将捕捉到服务器往返的延迟时间,以及等待服务器传送响应所用的时间。
  • Content Download / Downloading:接收响应数据所用的时间。

通过 Network 面板可以发现大量可能的问题,比如:

有很多被停止的条目:
表明正在从单个域检索太多的资源。在 HTTP 1.0/1.1 连接上,Chrome 会将每个主机强制设置为最多六个 TCP 连接。要解决此问题,需要实现域分片。也就是在应用上设置多个子域,以便提供资源。比如京东的首页,就有很多的子域名来加载图片:

image.png | center | 500x750

至第一字节的时间很长:
又称:大片绿色,一般是因为:客户端与服务器之间的网络条件较差或者服务器应用的响应慢。如果本地托管后 TTFB 仍然漫长,那么问题一般出在客户端与服务器之间的网络上。

image.png | center | 257x247

达到吞吐量能力:
又称:大片蓝色。一般是返回的报文太大了,首要的解决办法是减少发送的字节数。

image.png | center | 410x243

🖥 演示:京东首页 www.jd.com/;使用 DevTools 可以模拟不同的网络条件,解决加载时间问题。

性能面板(Performance)

性能面板之前叫 Timeline,在 Chrome 57 之后更名为性能面板。主要用来记录和分析应用在运行时的所有活动。

Performance 面板包含以下四个窗格:

  1. Controls。开始记录,停止记录和配置记录期间捕获的信息。
  2. Overview。 页面性能的高级汇总。
  3. 火焰图。 CPU 堆叠追踪的可视化。可以在火焰图上看到一到三条垂直的虚线。蓝线代表 DOMContentLoaded 事件。 绿线代表首次绘制的时间。 红线代表 load 事件。
  4. Details。选择事件后,此窗格会显示与该事件有关的更多信息。 未选择事件时,此窗格会显示选定时间范围的相关信息。

image.png | center | 827x649

Overview 窗格包含以下三个图表:

image.png | center | 827x150

  1. FPS。每秒帧数。绿色竖线越高,FPS 越高。 FPS 图表上的红色块表示长时间帧,很可能会出现卡顿。
  2. CPU。 CPU 资源。此面积图指示消耗 CPU 资源的事件类型。
  3. NET。每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)。横杠按照以下方式进行彩色编码:

    • HTML 文件为<span data-type="color" style="color:rgb(110, 161, 226)">蓝色</span>
    • 脚本为<span data-type="color" style="color:rgb(239, 196, 87)">黄色</span>
    • 样式表为<span data-type="color" style="color:rgb(155, 127, 230)">紫色</span>
    • 媒体文件为<span data-type="color" style="color:rgb(116, 178, 102)">绿色</span>
    • 其他资源为<span data-type="color" style="color:rgb(179, 179, 179)">灰色</span>

一般性能面板主要用于优化页面的加载时间,提高页面的流畅度,对于用户量很大的页面调优非常有用。比如:

  • 通过找出较长的Evaluate Script 事件,通过 JS 分析器获取究竟调用了哪些 JS 函数以及调用每个函数需要多长时间的更详细信息。
  • 样式更改开销较大,在这些更改会影响 DOM 中的多个元素时更是如此。通过检查大型 Recalculate Style 事件的记录(以紫色显示),如果样式更改需要较长时间,对性能的影响会非常大。
  • “布局抖动”是指反复出现强制同步布局情况。 这种情况会在 JavaScript 从 DOM 反复地写入和读取时出现,将会强制浏览器反复重新计算布局。通过观察红色竖线标记的 Layout 事件,可以发现布局方面的问题。
  • 绘制是填充像素的过程。这经常是渲染流程开销最大的部分。 在任何情况下注意到页面出现卡顿现象,很有可能存在绘制问题。

🖥 演示:快速确定绘制瓶颈,Paint flashing(在Rendering面板中),打开此选项后,每次发生绘制时,Chrome 将让屏幕闪烁绿色。www.jd.com/

内存面板(Memory)

内存面板之前叫 Profile,在 Chrome 57 之后更名为内存面板。主要用于查找影响页面性能的内存问题,包括内存泄漏、内存膨胀和频繁的垃圾回收。

用户一般会通过以下方式察觉到有内存问题:

  • 页面的性能随着时间的延长越来越差。 这可能是内存泄漏的症状。 内存泄漏是指,页面中的错误导致页面随着时间的延长使用的内存越来越多。
  • 页面的性能一直很糟糕。 这可能是内存膨胀的症状。 内存膨胀是指,页面为达到最佳速度而使用的内存比本应使用的内存多。
  • 页面出现延迟或者经常暂停。 这可能是频繁垃圾回收的症状。 垃圾回收是指浏览器收回内存。 浏览器决定何时进行垃圾回收。 回收期间,所有脚本执行都将暂停。因此,如果浏览器经常进行垃圾回收,脚本执行就会被频繁暂停。

内存泄漏很容易确定。如果网站使用的内存越来越多,则说明发生内存泄漏。内存膨胀比较难以界定,一般会考虑网站经常访问的设备的配置,说到这里,插播一下RAIL模型。

使用 RAIL 模型评估性能

RAIL 是一种以用户为中心的性能模型。每个网络应用均具有与其生命周期有关的四个不同方面,且这些方面以不同的方式影响着性能:

image.png | center | 827x300

让用户成为性能工作的中心。用户花在网站上的大多数时间不是等待加载,而是在使用时等待响应。

延迟与用户反应:

0 - 16 毫秒用户可以感知每秒渲染 60 帧的平滑动画转场。也就是每帧 16 毫秒
0 - 100 毫秒在此时间窗口内响应用户操作,他们会觉得可以立即获得结果。时间再长,操作与反应之间的连接就会中断。
100 - 300 毫秒用户会遇到轻微可觉察的延迟。
300 - 1000 毫秒对于网络上的大多数用户,这个时间段代表任务还在继续,是合理的延迟。
1000+ 毫秒超过 1 秒,用户的注意力将离开他们正在执行的任务。
10,000+ 毫秒用户感到失望,可能会放弃任务;之后他们或许不会再回来。

响应:尽量在 100 毫秒以内响应,对于需要超过 500 毫秒才能完成的操作,要始终提供反馈。

动画:尽量在 10 毫秒内生成一帧,因为浏览器需要花费时间将新帧绘制到屏幕上,所以只有 10 毫秒来执行代码。

空闲:最大程度增加空闲时间,利用空闲时间完成推迟的工作。例如,尽可能减少预加载数据,以便应用快速加载,并利用空闲时间加载剩余数据。

加载:尽量在 1000 毫秒以内呈现内容。

使用 Chrome 任务管理器实时监视内存使用,打开任务管理器,右键点击任务管理器的表格标题并启用 JavaScript memory。

内存可以表示为一个由多个互连的点组成的图表:

image.png | center | 538x339

对象可通过以下两种方式占用内存:

  • 直接通过对象自身占用。
  • 通过保持对其他对象的引用隐式占用,这种方式可以阻止这些对象被垃圾回收器(GC)自动处置。

堆分析器中:

  • Shallow Size(浅层大小),这是对象自身占用内存的大小。
  • Retained Size(保留大小),这是将对象本身连同其相关对象一起删除后释放的内存大小。

image.png | center | 775x257

内存图从根开始,根可以是浏览器的 window 对象或 Node.js 模块的 Global 对象。距离(Distance)字段,表示与 GC 根之间的距离。

image.png | center | 478x295

任何无法从根到达的对象都会被 GC 回收。

🖥 演示:按函数调查内存分配 Allocation sampling

应用面板(Application)

应用面板用来检查加载的所有资源,包括IndexedDB与Web SQL数据库,本地和会话存储,cookie,应用程序缓存,图像,字体和样式表。在持久化存储已经聊过。

🖥 演示:查看和编辑本地存储。

安全面板(Security)

安全面板主要是用来查看页面的整体安全性。

🖥 演示:

非安全页面会通过消息 This page is not secure. 提示

比较:www.google.com/

其他面板

🖥 演示:Audit 基于 Lighthouse,可以用于分析页面性能。

Chrome 的高性能网络(High Performance Networking)

Chrome 从2008年推出以来,现在已经在全世界浏览器市场中占了第一的份额,而且不断的在增长。

image.png | center | 827x136

驱动 Chrome 浏览器发展的主要是这几个原则:

  • Speed: 做最快的浏览器。
  • Security: 给用户提供最安全的上网环境。
  • Stability: 提供一个健壮且稳定的Web应用平台。
  • Simplicity: 提供简洁的用户体验。

这里所说的高性能网络,就是遵循了第一个原则:Speed。

之前说过一个网络请求的生命周期中间有很多步骤:

image.png | center | 635x348

这中间DNS查找、TCP三次握手、SSL握手等等,都会耗费一定的时间,对于响应比较快的请求,可能80%以上的开销都在网络上。

Chrome 的多进程架构为浏览器的网络请求处理带来了重要意义,它目前支持四种不同的执行模式:

  • Process-per-site-instance:就是打开一个网站,然后从这个网站开的每一个Tab属于一个进程。优点:隔离性很强;缺点:内存开销大,实现复杂。
  • Process-per-site:同域名的网站Tab放在一个进程。
  • Process-per-tab:一个Tab一个进程。
  • Single process:传统模式,只有一个进程。

默认情况下,桌面的 Chrome 浏览器使用 process-per-site 模式, 将不同的网站页面隔离起来, 相同网站的页面组织在一起。

Chrome Predictor 预测功能优化:

Chrome会随着使用变得更快。Predictor 会观察和学习当前网络活动方式,提前预估用户下一步的操作。比如:

  • 用户将鼠标停留在一个链接上,就预示着一个用户的偏好以及下一步的浏览行为。这时 Chrome 就可以提前进行 DNS Lookup 及 TCP 握手。用户的点击操作平均需要将近 200ms,在这个时间就可能处理完 DNS 和 TCP 相关的操作, 也就是省去几百毫秒的延迟时间。
  • 当在地址栏触发高可能性选项时,就同样会触发一个 DNS lookup 和 TCP 预连接(pre-connect),甚至在一个不可见的页签中进行预渲染(pre-render)。
  • 我们每个人都一串天天会访问的网站, Chrome 会研究在这些页面上的子资源, 并且尝试进行预解析(pre-resolve), 甚至可能会进行预加载(pre-fetch)以优化浏览体验。

Chrome采用了四种核心优化技术:

  • DNS pre-resolve:DNS 预解析,提前解析主机地址,以减少 DNS 延迟。
  • TCP pre-connect:TCP 预连接,提前连接到目标服务器,以减少 TCP 握手延迟。
  • Resource prefetching:资源预加载,提前加载页面的核心资源,以加快页面显示。
  • Page prerendering:页面预渲染,提前获取整个页面和相关子资源。

🖥 演示:chrome://predictors/

Chrome会维护用户输入的前缀的历史记录,建议的操作以及每个前缀的命中率。比如我平时输入了 appl,那么 100% 是要访问苹果官网。

TL;DR

这次主要聊了前端大概的演变,浏览器的一些基础,之后有时间会聊聊协议、安全、框架、实践、跨栈之类的,大家辛苦!