浏览器原理

Posted by Qz on April 21, 2020

“Yeah It’s on. ”

正文

浏览器工作原理与实践

进程(process)和 线程(thread)

  • 进程是 CPU 资源分配的最小单位
  • 线程是 CPU 调度资源的最小单位
  • 一个进程可包含 1 个 或 多个 线程

一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。

当一个进程关闭之后,操作系统会回收进程所占用的内存。

当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。

比如之前的IE浏览器,支持很多插件,而这些插件很容易导致内存泄漏,这意味着只要浏览器开着,内存占用就有可能会越来越多,但是当关闭浏览器进程时,这些内存就都会被系统回收掉

浏览器的进程

浏览以 chrome 为例,是多进程的浏览器,可以分为四个进程

  1. 浏览器主进程 (Browser Process):浏览器的主进程(负责协调、主控)
  2. 第三方插件进程(Plugin Process)
  3. GPU进程 (GPU Process):最多一个
  4. 浏览器渲染进程(Render Process),默认开启 1个 Tab —> 1个 进程
    • 前端接触的大多数东西,都在 渲染进程 里(页面的渲染,JS的执行,事件的循环…)
  5. 网络进程(Network Process)

浏览器主进程、网络进程、GPU 进程都是所有 Tab 共用的。

多进程的优势:任何进程崩掉,不影响整个浏览器使用; 缺点是消耗资源更大

谈渲染进程(Render进程)

此进程中,主要的渲染的线程有5个,分别是

  1. GUI渲染 线程
  2. JS引擎 线程 (V8)
  3. 事件触发 线程
  4. 定时触发器 线程
  5. 异步HTTP请求 线程

  • GUI 渲染线程与 JS 引擎线程是互斥的
  • 通过 WebWorker,可以让 JS 引擎向浏览器申请开一个子线程(不能操作 DOM,原因显而易见)
  • 再说 WebWorker 和 ShareWorker 的区别
    • WebWorker 只属于某个 Render 进程,不共享 (可以理解为 Render 进程 —> 创建一个新的线程来运行 js 代码)
    • SharedWorker 浏览器所有页面,共享 (由单独的进程管理)
  • GPU中,各个复合图层是单独绘制的,所以互不影响

网页浏览的原理

https://zhuanlan.zhihu.com/p/60725155

打开的网页本质上就是你从对方服务器上获取的文件,比如说你现在正在看的这篇文章,就存储在知乎的服务器上,你通过知乎客户端的浏览器,获取到了这些数据,并下载到了你的手机缓存当中,然后你的手机再把他们显示到屏幕上。

浏览网页的本质,就是下载文件

所以浏览网页的原理就是,在互联网上找到了对方的电脑,然后从对方的电脑里拷贝出来html网页文件到你的电脑上,并将其转化成了文字和图片显示到显示器或手机上。

DNS解析服务器

输入的是域名,你的电脑该怎么将他变成IP地址呢?就比如你输入的是http://www.baidu.com,为什么你的电脑知道对方的IP是14.215.177.39呢?这个东西就是hosts文件,hosts文件就在你的C:\windows\system32\drivers\etc文件夹下,他相当于电脑的电话本,他记录着每一个域名对应的IP地址,当你输入域名而不是IP的时候,他就会在这个电话本里找到对应的域名,然后把他转化成IP地址。

但是这样也有问题,那就是Hosts文件是有限的,就和你不可能拥有这个世界上所有人的电话号码一样。既然我们自己不可能拥有全世界所有人的电话号码,但是我们可以将收集电话号码这个任务交给一个专门来干这个活的人,然后大家想要问电话的时候去他那查一下就可以了。

这就是DNS服务器,DNS服务器有着相当全的域名和IP,当你输入一串网站的时候,这串网站并不会直接访问,而是先将这个网站发送给DNS服务器,DNS服务器帮你把这串网站变成了IP地址,然后返回给你的电脑,你再访问这个IP地址,这样就解决了IP难记,而域名不能直接访问的问题了。

DNS劫持

那DNS服务器会告诉你A网站的IP是A,B网站的IP是B,那假如你输入的A网站域名,但是他给你B的IP,你是不是就访问到别的网站去了?

DNS服务器想给你返回什么IP就给你返回什么,所以他可以在幕后操作一些东西。最简单的就是广告。比如A网站没有广告,你直接访问就是没有的,但是你的DNS服务器把A网站下载下来了,给这个网站加了个广告,然后重新上传到了一个IP上,并把这个IP告诉了你,那就是你虽然输入的A网站的域名,但是你访问的是一个包含了广告的复制版A网站,虽然两者功能一样,但是却完全不是一个服务器上的。

这些就被我们成为DNS劫持,DNS劫持对于网络访问的影响和体验是非常严重的,除了DNS服务器,你的路由器同样可以这么干,所以在买路由器的时候也有人会关注这个路由器是否会进行DNS劫持。

Headless

https://developer.chrome.com/blog/headless-chrome/

It brings all modern web platform features provided by Chromium and the Blink rendering engine to the command line.

new-headless

const browser = await puppeteer.launch({
  headless: 'new',
  // `headless: true` (default) enables old Headless;
  // `headless: 'new'` enables new Headless;
  // `headless: false` enables “headful” mode.
});

实现和维护这个独立的无头浏览器会带来大量的工程开销——但这并不是唯一的问题。因为 Headless 是一个单独的实现,所以它有自己的错误和功能,而这些错误和功能在 headful Chrome 中是不存在的。这造成了一种令人困惑的情况,即任何自动化浏览器测试都可能在有头模式下通过但在无头模式下失败,反之亦然——这是自动化工程师的主要痛点。例如,它还排除了任何依赖于安装浏览器扩展的自动化测试。这同样适用于任何其他浏览器级别的功能:除非 Headless 有自己的、单独的实现,否则它不受支持。2021 年,Chrome 团队着手解决这个问题,一劳永逸地统一了 Headless 和 headful 模式。

补充

setTimeout存在最低间隔

规定要求setTimeout中低于4ms的时间间隔算为4ms

DOMContentLoaded触发时机

<html lang="en">
 <head>
 <title>css阻塞</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <script>
 document.addEventListener('DOMContentLoaded', function() {
 console.log('DOMContentLoaded');
 })![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d30219afd9e84bc99875991b7d284435~tplv-k3u1fbpfcp-zoom-1.image)

![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87519598e6484ec38db8daed23f586c9~tplv-k3u1fbpfcp-zoom-1.image)
 </script>
 <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
 </head>
 <body>
 </body>
</html>

css还未加载完,就已经触发了DOMContentLoaded事件了。因为css后面没有任何js代码。

<html lang="en">
 <head>
 <title>css阻塞</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <script>
 document.addEventListener('DOMContentLoaded', function() {
 console.log('DOMContentLoaded');
 })
 </script>
 <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
 <script>
 console.log('到我了没');
 </script>
 </head>
 <body>
 </body>
</html>

接下来我们对第二种情况做测试,很简单,就在css后面加一行代码就行了

我们可以看到,只有在css加载完成后,才会触发DOMContentLoaded事件。

因此,我们可以得出结论:

  • 如果页面中同时存在css和js,并且存在js在css后面,则DOMContentLoaded事件会在css加载完后才执行。
  • 其他情况下,DOMContentLoaded都不会等待css加载,并且DOMContentLoaded事件也不会等待图片、视频等其他资源加载。