从 URL 到页面展现发生了什么

《从 URL 到页面展现发生了什么》这是一个老生常谈的博客题目

老生常谈,在编程界里可以理解为【很重要】

以为个人的理解,前端工程师,说到底就是在研究并实现《从 URL 到页面展现发生了什么》

我实在能力有限,对于这个题目一直不敢写,因为这涉及的知识点太广了

然而再难也得迈出这一步,因为你不写出来,你永远不知道自己的水平到底是多有限

今天是入职的前一天晚上,希望在工作的半年或一年以后,我能对这个题目有更加深刻的认识

举个例子简短回答

为了更好的理解,我们走一遍流程

  1. 访问百度主页
  2. 这个时候 Client 就是浏览器,它发了一个请求
  3. 这个请求是发给百度服务器的,也就是上图的 Server,但是这个说法不太准确,Server 不一定是一个机器,也可能是一个软件(应用程序)
  4. 接着这个 Server 会返回 Client 一个网页
  5. 我们就看到了百度

其实以上的例子总结下来就是以下四步么

  • 用户请求远程资源
  • 浏览器查找远程资源,打包用户请求并发送
  • 服务器根据用户请求的资源路径及附带参数,配合自身逻辑生成相关内容,发送给浏览器
  • 浏览器解析结果,翻译为直观方式呈现

好了,回答完毕

标准答案

那么以上步骤具体可以细化为

  1. 输入地址
  2. 浏览器查找域名的 IP 地址
  3. 浏览器向 web 服务器发送一个 HTTP 请求
  4. 服务器的永久重定向响应
  5. 浏览器跟踪重定向地址
  6. 服务器处理请求
  7. 服务器返回一个 HTTP 响应
  8. 浏览器显示 HTML
  9. 浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)

好了,你对这一系列过程有一个大体的概念了,接下来细说每一个步骤

输入地址

当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,然后给出智能提示,让你可以补全url地址。对于 google 的 chrome 的浏览器,他甚至会直接从缓存中把网页展示出来,就是说,你还没有按下 enter,页面就出来了。

厉害了

浏览器查找域名的 IP 地址

URL 到服务器逐级

  • 一个页面访问的本质是我们希望通过一个路径找到相应的资源
  • 路径就是我们的 URL,资源是服务器给我们的请求的响应
  • 首先我们需要找到网络上的服务器才能找到机器上的资源,网络主机的定位靠的是 IP 地址

域名到IP

IP(Internet Protocol)
: 互联网中设备间进行通信都要遵从的一种协议,它规定了每台设备都要有且唯一的 IP 地址,用来标识自己在互联网中的地址。格式通常为 http://XXX.XXX.XXX.XXX,不同网段下 IP 地址的范围也不同。如有兴趣者,请自行百度。

域名(Domain Name)
: 由于IP协议规定的纯数字 IP 地址在日常中难以记忆,因此人们便产生使用更加常见,好记的字符标识设备的地址,域名应运而生。一个域名就是一个更加容易记忆的目标主机的地址标识符。例如:百度的域名就为百度一下,你就知道,实际对应的IP地址为 119.75.217.109

DNS(Domain Name System)
: 互联网中实际定位设备时还是使用 IP 地址来定位,因此产生了 DNS,一种专门用来将域名转换为 IP 地址的协议,提供该协议服务的服务器就叫 DNS 服务器。

总结一下

  • 为什么用域名不用IP
    • 因为 IP 有点反人类的记忆思维
  • 域名和 IP 对应 DNS (Domain Name System)

实际上DNS就是一组键值对,键名就是域名,值就是IP地址

DNS解析

输入了一个域名,你得靠 DNS 解析出一个相应的 IP 地址吧

  • 浏览器缓存:如果之前访问过该主机,(不是URL指定的资源),浏览器会缓存DNS一段时间,这样就可以直接使用浏览器缓存的DNS,至于一段时间是多久没要求,浏览器自行决定
  • 系统缓存:如果浏览器缓存里没有记录,浏览器会做系统调用,获取系统中的缓存记录
  • 路由器缓存:如果系统缓存同样没有命中,那就需要查询路由器缓存了
  • ISP(互联网服务提供商,例如电信,移动)的 DNS 缓存:路由器缓存未命中会查询 ISP(Internet Service Provider),一般域名在这里都可以找到了
  • 递归搜索:我们使用的 ISP 的 DNS 记录里面如果还没有的话,那么就会从顶级域名服务器的根域名服务器开始递归查询,这个肯定会查到了

第三者

实际上浏览器充当了我们要找到的 IP 地址的第三者,类似中介

可以用以下代码来看浏览器的版本

window.navigator.userAgent
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"

浏览器向 web 服务器发送一个 HTTP 请求

打包 HTTP 请求

图中的请求是什么,仅仅是输入的 URL 么

当然不是,请求是一段报文,包括但不仅仅是 URL

那么请求(request)包含什么呢

  • 请求行
  • 请求头
  • 空行
  • 消息体

请求又分为两类

  • GET 请求
  • POST 请求

GET 请求

GET / HTTP/1.1 // 请求行,下面一直到空行之上都是请求头
Host: www.baidu.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4
Cookie: BAIDUID=5A056AFFCD3D7072D17933F3750CAB0B:FG=1;
// 空行
// 如果是 GET,那么一般消息体是空的

POST 请求

POST /login/email HTTP/1.1 // 请求行,下面一直到空行之上都是请求头
Host: www.zhihu.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8 // 描述消息体
Content-Length: 119
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9
Cookie: _xsrf=e0f0996099f0c4e3e0157d92d65dfe23;
// 空行
password=zhihu&captcha=mrah&remember_me=true&email=huayiqishi%40qq.com // 消息体

创建 TCP 连接

  • 一般浏览器都是通过使用的 TCP 协议,UDP 不可靠,所以浏览器得到的 response 要么是全的,要么得不到
  • 浏览器打包请求自然也是打包的 HTTP 报文
  • 那么为什么有时候我们看到的页面是残缺不全的呢(因为是资源下载失败了而不是 response 这个文本有残缺)

浏览器发送请求的方法

  • get
  • head
  • post
  • trace
  • options
  • put(往服务器上面放置一些东西)
  • delete(往服务器上删除一些东西)

一般的浏览器只能发起 GET 或者 POST 请求

服务器的永久重定向响应

服务器给浏览器响应一个301永久重定向响应,这样浏览器就会访问 http://www.google.com/ 而非 http://google.com/

为什么服务器一定要重定向而不是直接发送用户想看的网页内容呢?其中一个原因跟搜索引擎排名有关。如果一个页面有两个地址,就像 http://www.yy.com/http://yy.com/,搜索引擎会认为它们是两个网站,结果造成每个搜索链接都减少从而降低排名。而搜索引擎知道301永久重定向是什么意思,这样就会把访问带 www 的和不带 www 的地址归到同一个网站排名下。还有就是用不同的地址会造成缓存友好性变差,当一个页面有好几个名字时,它可能会在缓存里出现好几次。

301和302的区别

301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的 URL 地址,这个地址可以从响应的 Location 首部中获取(用户看到的效果就是他输入的地址 A 瞬间变成了另一个地址 B)——这是它们的共同点。

他们的不同在于

  • 301表示旧地址 A 的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
  • 302表示旧地址 A 的资源还在(仍然可以访问),这个重定向只是临时地从旧地址 A 跳转到地址 B,搜索引擎会抓取新的内容而保存旧的网址。 SEO 302好于301

重定向原因

  • 网站调整(如改变网页目录结构)
  • 网页被移到一个新地址
  • 网页扩展名改变(如应用需要把.php改成.Html或.shtml)

这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。

什么时候进行301或者302跳转

当一个网站或者网页24—48小时内临时移动到一个新的位置,这时候就要进行302跳转,而使用301跳转的场景就是之前的网站因为某种原因需要移除掉,然后要到新的地址访问,是永久性的。

清晰明确而言,使用301跳转的大概场景如下:

  • 域名到期不想续费(或者发现了更适合网站的域名),想换个域名。
  • 在搜索引擎的搜索结果中出现了不带www的域名,而带www的域名却没有收录,这个时候可以用301重定向来告诉搜索引擎我们目标的域名是哪一个。
  • 空间服务器不稳定,换空间的时候。

浏览器跟踪重定向地址

现在浏览器知道了 "http://www.google.com/"才是要访问的正确地址,所以它会发送另一个http请求。这里没有啥好说的

服务器处理请求

相关进程处理请求

主机运行多个程序,那个来处理HTTP请求呢

  • http: 80
  • https: 443
  • ftp: 21
  • ssh: 22

服务器

  • 物理主机
  • web server

服务器返回一个 HTTP 响应

服务器响应请求

哪些内容影响服务器结果呢

  • 请求方法
  • 路径
  • query string
  • cookkie
  • 服务器配置
  • 动态语言代码逻辑

服务器响应内容

  • 状态行
  • 响应头
  • 响应正文

1. 状态行

  • 协议版本:是用http1.0还是其他版本
  • 状态描述:状态描述给出了关于状态代码的简短的文字描述。比如状态代码为200时的描述为 ok
  • 状态代码:状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值

例如:HTTP/1.1 200 OK

2. 响应头

响应头部:由关键字/值对组成,每行一对,关键字和值用英文冒号":"分隔,典型的响应头有:

3. 响应正文

包含着我们需要的一些具体信息,比如cookie,html,image,后端返回的请求数据等等。这里需要注意,响应正文和响应头之间有一行空格,表示响应头的信息到空格为止,下图是fiddler抓到的请求正文,红色框中的响应正文:

浏览器显示 HTML

渲染页面

  • 浏览器下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染同时进行
  • 解析 HTML 生成 DOM 树
  • 解析 HTML 中的 CSS,生成渲染树
  • 解析 JavaScript,解析到的时候执行

浏览器是如何把页面呈现在屏幕上的呢?不同浏览器可能解析的过程不太一样,这里我们只介绍webkit的渲染过程,下图对应的就是WebKit渲染的过程,这个过程包括:

解析html以构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

浏览器在解析html文件时,会”自上而下“加载,并在加载过程中进行解析渲染。在解析过程中,如果遇到请求外部资源时,如图片、外链的 CSS、iconfont 等,请求过程是异步的,并不会影响 html 文档进行加载。

解析过程中,浏览器首先会解析 HTML 文件构建 DOM 树,然后解析 CSS 文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和 repain(重绘)。

DOM 节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为 relow,当盒模型的位置,大小以及其他属性,如颜色、字体等确定下来之后,浏览器便开始绘制内容,这个过程称为 repain。

页面在首次加载时必然会经历 reflow 和 repain。reflow 和 repain 过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少 reflow 和 repain。

关联资源处理

  • 在展现到页面的某一部分时,即上面的所有部分都已经下载完成
  • 并不是说所有相关联的元素都已经下载完,图片,视频等元素需要另外并行下载
  • 同一个域名下并行下载数量有限制

JS 和 AJAX

  • JS 不能并行的下载和解析,采用阻塞的方式,当页面引用了 JS 的时候浏览器发送请求后会一直阻塞直到得到响应
  • 因为浏览器需要1个稳定的 DOM 树结构,而 JS 中很有可能有代码直接改变了 DOM 树结构,浏览器为了防止出现 JS 修改 DOM 树,需要重新构建 DOM 树的情况,所以就会阻塞其他的下载和呈现
  • 遇到 AJAX 后执行,然后进行下面的步骤,AJAX 拿到结果后再执行 AJAX 回调函数

CSS

  • 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染
  • JS,CSS 中如果有重定义,后定义将覆盖之前的定义,而不会报错

直接使用本地缓存

  • 服务器发给浏览器的文件中会带有 Expires 或 Cache-Control 说明文件什么时候失效
  • 在有效期内的话浏览器直接使用本地文件,不发请求

服务器验证

  • 服务器响应中会带有文件的最后修改时间或 Etag
  • 浏览器发送重复请求会带上这些信息,如果服务器判断没有变化,发送304状态码,让浏览器使用本地缓存

好了,从 URL 到页面展现大概就是这个样子的

当然了,作为励志做一名号前端的我们,知道这些是远远不够的,请把以上的每一个点都细化成博客,我相信功力会大增的

学点单词

  • preserve
    英 [prɪˈzɜ:v] 美 [prɪˈzɜrv]
    vt. 保护; 保持,保存; 腌制食物; 防腐处理;
    vi. 保鲜; 保持原状; 做蜜饯; 禁猎;
    n. 蜜饯; 防护用品; 禁猎地; 独占的事物(或范围);
  • cache
    英 [kæʃ] 美 [kæʃ]
    n. 藏物处; 隐藏处; 藏匿的珍宝; <电脑>快速缓冲贮存区;
    vt. 贮藏;
    vi. 躲藏;

参考文章

(完)


文档信息

  • 自由转载-非商用-非衍生-保持署名
  • 发表日期:2017年6月4日

推荐阅读更多精彩内容