文章目录

  • 1. 浏览器的发展史
  • 2. 浏览器的结构
  • 3. 什么是渲染引擎(浏览器内核)
  • 4. 浏览器是如何工作的

文章内容整理自视频:https://www.bilibili.com/video/BV1x54y1B7RE?from=search&seid=13571971023072330809&spm_id_from=333.337.0.0


1. 浏览器的发展史

  • 1991年,Berners Lee建立了第一代网络浏览器WorldWideWeb。当时的WorldWideWeb功能非常简单,只支持显示文本图片。

  • 1993年,Mosaic浏览器问世。这是一种可以同时显示文本和图像的浏览器,一经推出就受到了全球用户的欢迎。

  • 1994年,网景浏览器发布,它是由曾经参与开发Mosaic的人共同创建。虽然网景的功能也十分有限,只能显示简单的静态html,没有js、css,但依然大受欢迎,获得了世界范围内的成功,并占领了绝大多数市场份额。

    同年,出现了opera。

  • 1995年,微软发布了万恶的IE1.0,IE2.0。自此第一次浏览器大战正式打响。

  • 1996年,微软发布的IE3.0和Windows操作系统集成在了一起,而此时网景的市场份额已经达到了86%。IE发行后的4年内,在Windows操作系统的帮助下,逐渐取代了网景浏览器的领导地位,达到了市场份额的75%。到1999年,IE浏览器已经占据了市场的99%。

  • 1998年,网景成立了Mozilla基金会,在该基金会的推动下,网景公司开发了著名的开源项目:火狐浏览器Firefox。网景利用火狐来迎击IE,并在2004年发布了1.0版本,拉开了第二次浏览器大战的序幕。

  • 2003年,苹果发布了Safari浏览器,而且该浏览器被包含在所有苹果操作系统中。

  • 2005年,苹果开源了Safari浏览器的内核webkit。

  • 2008年,谷歌以苹果开源项目webkit作为内核,创建了一个新的项目Chromium。在该项目的基础上,苹果发布了自己的浏览器产品Chrome。Chrome发展也十分迅速,现在已经成为了全球最受欢迎的浏览器。

  • 由于IE的性能和体验问题,IE逐渐掉队。2015年,微软也放弃了IE,推出了基于webkit内核的Edge浏览器作为IE的替代品,但为时已晚。

根据StatCounter的统计,截至2020年5月份,各个web浏览器的市场份额如下:

由上图可以看出,Chrome已经占据了60%多的市场份额。

2. 浏览器的结构

虽然浏览器的种类繁多,但他们提供的功能基本类似,国家结构也都大同小异,下图是一张简化的浏览器结构图,我们大致可以简单的分为用户界面、浏览器引擎、渲染引擎。

  • 用户界面用于展示除标签页窗口之外的其他用户内容。

  • 渲染引擎负责渲染用户请求的页面内容。渲染引擎下面还有很多小的功能模块,比如负责网络请求的网络模块、用于解析和执行js的js解释器,还有数据存储持久层来帮助浏览器存储各种数据(比如cookie等)。

    渲染引擎可以说是一个浏览器的核心与灵魂,也是本篇文章的重点。

  • 在用户界面和渲染引擎之间的浏览器引擎,用于在用户界面和渲染引擎之间传递数据。

3. 什么是渲染引擎(浏览器内核)

我们往往会把渲染引擎称为浏览器的内核,不同浏览器使用的内核也不大一样。其中,IE使用的是Trident;Firefox使用的是Gecko;Safari使用的是Webkit并将其开源;Chrome是使用的基于webkit改造优化的Blink渲染引擎,并且也将其开源;Opera和Edge使用的是Blink。

可以看到,webkit项目的开源为浏览器的发展做了多大贡献。本篇文章主要以Chrome为例来讲解浏览器是如何工作的。

4. 浏览器是如何工作的

我们换一个角度来拆解浏览器的组成结构。浏览器是运行在操作系统上的一个应用程序。我们知道,每启动一个应用程序必须至少启动一个进程来执行其功能。每个程序往往需要运行很多任务,那么进程就会创建一些线程来帮助它去执行这些小的任务。这里我们就引入了两个概念:进程和线程。

我们回忆一下关于进程和线程的概念:

  • 进程是操作系统进行资源分配和调度基本单元,可以申请和拥有计算机资源。进程是程序的基本执行体。
  • 线程是操作系统能够进行运算调度的最小单位。一个进程可以并发多个线程,每条线程执行不同的任务。

当我们启动某个程序时,就会创建一个进程来执行任务代码,同时操作系统会为该进程分配内存和空间,该应用进程的状态都保存在该内存空间里。当应用关闭时,该内存空间就会被回收。

进程可以启动更多的进程来执行任务,由于每个进程分配的内存空间是独立的,如果两个进程间需要传递某些数据,则需要通过进程间通信管道IPC来传递。

很多应用程序都是多进程的结构,这样是为了避免某一个进程卡死,由于进程间相互独立,这样就不会影响到整个应用程序。举个例子,你可以把笔记本电脑想象成一个应用程序,外接鼠标是该应用程序的一个进程,如果外接鼠标出了问题并不会影响你继续使用笔记本电脑。

进程可以将任务分成更多细小的任务,然后通过创建多个线程并行执行不同的任务,同一进程下的线程之间是可以直接通信共享数据的。

浏览器也是一个多进程结构,但早期的浏览器并不是多进程的结构,而是一个单进程的结构。在一个进程中,由页面线程负责页面渲染和展示等;js线程执行js代码;还有其他各种线程。

单进程的结构引发了很多问题:一是不稳定,其中一个进程的卡死可能会导致整个进程出问题,比如打开多个标签页,其中一个标签页卡死可能会导致整个浏览器无法正常运行;二是不安全,浏览器之间是可以共享数据的,那js线程就可以随意访问浏览器进程内的所有数据;三是不流畅,一个进程需要负责太多事情会导致运行效率的问题。

所以巍为了解决以上这些问题现在采用了多进程浏览器结构,根据进程功能的不同来拆卸浏览器,我们可以将它分解为下图这些进程。

其中,浏览器进程负责控制Chrome浏览器除标签页外的用户界面,包括地址栏、书签、后退和前进按钮,以及负责与浏览器的其他进程协调工作;网络进程负责发起或接手网络请求;GPU进程负责整个浏览器页面的渲染;插件进程负责控制网站使用的所有插件,例如flash(这里的插件并不是指Chrome市场里安装的扩展);渲染进程用来控制显示tab标签内的所有内容,浏览器在默认情况下chromium为用户访问的网站的每个实例创建一个渲染器进程,这样可以确保来自不同站点的页面是独立呈现的,并且对同一站点的单独访问也是彼此隔离的,简单来说就是访问不同站点和统一站点的不同页面都会创建一个新的进程,每个tab及tab内的每个站点都是相互隔离互不影响的,当其中一个标签页渲染器进程卡死,并不会影响其他标签页。

当我们在地址栏里输入内容时,浏览器内部会发生什么事情?

当我们在地址栏里输入内容时,浏览器进程的UI线程会捕捉你的输入内容,如果访问的是网址,则UI线程会启动一个网络线程来请求DNS进行域名解析,接着开始连接服务器获取数据;如果你的输入不是网址而是一串关键词,浏览器就知道你是要搜索,于是就会使用默认配置的搜索引擎来查询。

网络线程获取到数据之后会发生什么样的事情?

当网络线程获取到数据后,会通过SafeBrowsing来检查站点是否是恶意站点。如果是,则会提示警告页面,告诉你这个站点有安全问题,浏览器会阻止你的访问,当然你也可以强行继续访问。SafeBrowsing是谷歌内部的一套站点安全系统,通过检测该站点的数据来判断是否安全,比如通过查看该站点的IP是否在谷歌的黑名单之内。

当返回数据准备完毕,并且安全校验通过时,网络线程会通知UI线程我就要准备好了,该你了,然后UI线程会创建一个渲染进程来渲染页面。浏览器进程通过IPC管道将数据依次传递给渲染器进程,正式进入渲染流程。

渲染器进程接收到的数据,也就是html。渲染器进程的核心任务就是把html、css、js、image等资源渲染成用户可以交互的web页面。渲染器进程的主线程将html进行解析,构造DOM数据结构。DOM也就是文档对象模型,是浏览器对页面在其内部的表示形式,是web开发程序员可以通过js与之交互的数据结构和API。

html首先经过tokeniser标记化,通过词法分析将输入的html内容解析成多个标记,根据识别后的标记进行DOM树构造,在DOM树构造过程中会创建document对象,然后以document为根节点的DOM树不断进行修改,向其中添加各种元素。html代码中往往会引入一些额外的资源,比如图片、CSS、JS脚本等。图片和CSS这些资源需要通过网络下载,或者从缓存中直接加载。这些资源不会阻塞html的解析,因为它们不会影响DOM的生成。但当html解析过程中遇到script标签,就会停止html解析流程,转而去加载并且执行js。

那么为什么不直接跳过js的加载和执行,等html解析完后,再加载运行js呢?

这是因为浏览器并不知道js执行是否会改变当前页面的HTML结构,如果js代码里用了document.write方法来修改html,那之前的html解析就没有任何意义了。这也就是为什么我们一直说要把script标签要放在合适的位置,或者使用async或defer属性来异步加载执行js。

在html解析完成后,我们就会获得一个DOM Tree(树),但我们还不知道DOM树上的每个节点应该长什么样子。主线程需要解析CSS,并确定每个DOM结点的计算样式。即使你没有提供自定义的CSS的样式,浏览器也会有自己默认的样式表,比如h2的字体要比h3的大等等。

在知道DOM结构和每个节点的样式后,我们接下来需要知道每个节点需要放在页面上的哪个位置,也就是节点的坐标以及该节点需要占用多大的区域,这个阶段被称为layout布局。

主线程通过遍历dom和计算好的样式来生成Layout Tree。Layout Tree上的每个节点都记录了x,y坐标和边框尺寸。这里需要知道的一点是DOM Tree和Layout Tree并不是一一对应的,设置了display:none的节点不会出现在Layout Tree上,而在before伪类中添加了content值的元素。content里的内容会出现在Layout Tree上,不会出现在DOM树里。这是因为DOM是通过html解析获得,并不关系样式,而Layout Tree是根据DOM和计算好的样式来生成。Layout Tree是和最后展示在屏幕上的节点是对应的。

现在我们已经知道了元素的大小形状和位置,这还不够,我们还需要知道以什么样的顺序绘制这个节点。举个例子来说,z-index属性会影响节点绘制的层级关系,如果我们按照dom的层级结构来绘制页面,则会导致错误的渲染。为了保证在屏幕上展示正确的层级,主线程会遍历Layout Tree,创建一个绘制记录表。该表记录了绘制的顺序,这个阶段被称为绘制。

现在知道了文档的绘制顺序,终于到了该把这些信息转化成像素点显示在屏幕上的时候了,这种行为被称为栅格化。Chrome最早使用了一种很简单的方式,只栅格化用户可视区域的内容,当用户滚动页面时,再栅格化更多的内容来填充缺失的部分。这种方式带来的问题显而易见,会导致展示延迟。

随着不断的优化升级,现在的Chrome使用了一种更为复杂的栅格化流程,叫做合成。合成是一种将页面的各个部分分成多个图层,分别对其进行栅格化,并在合成器线程中单独进行合成页面的技术。简单来说就是页面所有的元素按照某种规则进行分图层,并把图层都栅格化好了,然后只需要把可视区的内容组合成一帧展示给用户即可。

主线程遍历Layout Tree,生成Layer(图层) Tree。当Layer Tree生成完毕和绘制顺序确定后,主线程将这些信息传递给合成器线程。于一层可能像页面的整个长度一样大,因此合成器线程将他们切分为许多图块,然后将每个图块发送给栅格化线程。

栅格化线程栅格化每个图块,并将它们存储在GPU内存中。当图片栅格化完成后,合成器线程将收集称为“draw quads”的图块信息。这些信息里记录了图块在内存中的位置和在页面的哪个位置绘制图块的信息。

根据这些信息,合成器线程生成了一个合成器帧,然后合成器帧Frame(帧)通过IPC传送给浏览器进程。接着浏览器进程将合成器帧传送到GPU,然后GPU渲染展示到屏幕上,这时候我们就可以看到页面的内容。当页面发生变化时,比如滚动了当前页面,都会生成一个新的合成器帧,新的帧再传给GPU,然后再次渲染到屏幕上。

小结:

浏览器进程中的网络线程请求获取到html数据后,通过IPC将数据传给渲染器进程的主线程。主线程将html解析构造DOM树,然后进行样式计算,根据DOM树和生成好的样式生成Layout Tree,然后遍历Layout Tree生成绘制顺序表,接着遍历Layout Tree,生成Layer Tree。然后主线程将Layer Tree和绘制顺序信息一起传给合成器线程,合成器线程按规则进行分图层,并把图层分为更小的图块(tiles)传给栅格化线程进行栅格化。栅格化完成后,合成器线程会获得栅格线程传过来的“draw quads”图块信息。根据这些信息,合成器线上合成了一个合成器帧,然后将该合成器帧通过IPC传回给浏览器进程,浏览器进程再传到GPU进行渲染,最后就可以展示到你的屏幕了。

当我们改变一个元素的尺寸位置属性时,会重新进行样式计算、布局、绘制以及后面的所有流程,这种行为我们称为重排。当我们改变某个元素的颜色属性时,不会重新触发布局,但还是会触发样式计算和绘制,这个就是重绘。

我们可以发现重排和重绘都会占用主线程,js也是运行在主线程上。既然它们都是在主线程上运行,就会出现抢占执行时间的问题。如果你写了一个不断导致重排重绘的动画,浏览器则需要在每一帧都运行样式计算、布局和绘制的操作。我们知道当页面以每秒60帧的刷新率刷新时,才不会让用户感觉到卡顿,如果你在运行动画时还有大量的js任务需要执行,因为布局、绘制和js执行都是在主线程运行的,当在一帧的时间内布局和绘制结束后,还有剩余时间,js就会拿到主线程的使用权。如果js执行时间过长,就会导致在下一帧开始时js没有及时归还主线程,导致下一帧动画没有按时渲染,就会出现页面动画的卡顿。

有什么优化的手段吗?

有。第一种就是通过requestAnimationFrame()来帮助我们解决这个问题,这个方法会在每一帧被调用,通过API的回调,我们可以把js运行任务分成一些更小的任务块(分到每一帧),在每一帧时间用完前暂停执行js执行,归还主线程。这样的话,在下一帧开始时,主线程就可以按时执行布局和绘制。React最新的渲染引擎React Fiber,就是用到了API来做了很多优化。

还有第二个优化方法,通过刚才流程图我们知道栅格化的整个流程是不占用主线程的,只在合成器线程和栅格线程中运行,这就意味着它无需和js抢夺主线程。我们刚才提到,如果反复进行重绘和重排,可能会导致掉帧,这是因为有可能js执行阻塞了主线程。而CSS中有个动画属性叫transform,通过该属性实现的动画不会经过布局和绘制,而是直接运行在合成器线程和栅格化线程中,所以不会收到主线程中js执行的影响,更重要的是通过transform实现的动画由于不需要经过布局绘制、样式计算等操作,所以节省了很多运算时间(方便实现负责的动画)。

我们常常会使用哪些手续来实现动画效果呢?位置变化、宽高变化(旋转、3D等)这些都是可以使用transform来代替的。

如果面试的时候,面试官问你为什么要避免大量的重绘和重排,以上所述用作答案可能会得满分。


文章内容整理自视频:https://www.bilibili.com/video/BV1x54y1B7RE?from=search&seid=13571971023072330809&spm_id_from=333.337.0.0

浏览器是如何运作的?相关推荐

  1. 浏览器是如何运作【前端必备】

    浏览器是如何运作的 1.进程与线程 2.浏览器结构 2.1用户界面 2.2多进程浏览器结构 浏览器是如何运作的? 浏览器是运行在操作系统上的一个应用程序. 每个应用程序必须至少启动一个进程来执行其功能 ...

  2. 浏览器运作原理笔记(来自up主objtube的卢克儿的视频)

    几乎复刻了视频内容,不过看笔记有层次感一点,O(∩_∩)O哈哈~ [干货]浏览器是如何运作的?_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1x54y1 ...

  3. 微软推安全浏览器Gazelle,取代操作系统?

    微软的研究人员近日揭露了新一代安全浏览器的概念.这群研究人员正在开发名为"瞪羚(Gazelle)"的新概念浏览器,让浏览器拥有多主元(multi-principal)的操作系统架构 ...

  4. 翻译:浏览器内部的工作原理

    译文:引自http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/ 作为一名web开发人员,了解浏览器的内部运作会帮助你做出更 ...

  5. 网络、浏览器专题重点知识(含原理)

    文章目录 一.前言 URL 和 URI 二.互联网如何运作?/ 浏览器怎么通过互联网获取到网页数据的 三.输入url到页面展示经历的步骤 3.1 宏观层面 3.2 微观层面的详细流程 1. 用户输入U ...

  6. 网络连接成功,但浏览器显示网络未连接

    1.打开控制面板,选择网络和 Internet 2.点击 Internet选项,选择连接板块 3.点击局域网设置,勾选自动检测设置,点击确定即可 4.打开浏览器,正常运作

  7. 互联网是如何运作的?

    互联网是如何运作的? 1. 网络协议(network protocol) 简称为协议,是为进行网络中的数据交换而建立的规则.标准或约定. 网络协议通过分层明确每一层的工作职责,通过定义明确的接口来协同 ...

  8. 深入理解cookie和session

    cookie和session在java web开发中扮演了十分重要的作用,本篇文章对其中的重要知识点做一些探究和总结.(转发自https://www.cnblogs.com/roy-blog/p/82 ...

  9. kompozer+mysql_KompoZer for mac下载_KompoZer for mac版V0.8b3下载(暂未上线)_预约_飞翔下载...

    KompoZer for mac是一款完全免费的所见即所得的HTML网页编辑器,这款软件支持所见即所得,在我们在制作网页时,更加的直观化,编辑网页就如同打字一样容易.内建一个FTP Client,让我 ...

最新文章

  1. 拖拽使用 .bat 批处理
  2. CodeAnyWhere
  3. wp java_Java WordPress工具库wp-client
  4. 进程与线程的超级简单形象解释
  5. centos7调节虚拟机字体_初次安装虚拟机中Ubuntu16.04系统设置的一些小问题(小白教程)...
  6. 问题集锦13:数据库升级后,程序无法连接数据库
  7. 美国最受欢迎的电商网站,竟然是一家中国公司?
  8. sklearn 下的 SVM 及其参数
  9. hadoop之Combiner
  10. 安装Rational Rose所踩得坑
  11. 这个季节有离别——观《Sad Movie》有感
  12. 如何提高 CSS 动画性能
  13. No symbols loaded
  14. 学好水彩,给自己做个手机壳吧
  15. Monash call:概述生物特征识别
  16. android 备份 通信录,如何简单备份手机通讯录?
  17. ubuntu安装时姓名、计算机名、用户名的含义
  18. PHP学习练手(十)
  19. 苹果 WWDC 2019 全记录:iPadOS独立、SwiftUI、Project Catalyst
  20. python继承和多态心得_python学习第十五天 -面向对象之继承和多态

热门文章

  1. asp.net998-校园二手书店交易平台#毕业设计
  2. 互联网+医疗是机遇也是挑战
  3. asp.net校园新闻管理系统毕业设计(附源码、运行环境)
  4. intelliJ IDEA maven打包时提示系统资源不足
  5. 论文阅读:Interactive Multiobjective Optimisation: Preference Changes and Algorithm Responsiveness
  6. 计算机网络基础知识合集
  7. div +css 纵向导航
  8. 向日葵橙色调色Lr预设分享
  9. php微信app支付教程,PHP APP端微信支付的方法
  10. 50种推广网站的方法