浏览器的工作方式

最近又复习了一下基础知识,做个记录。

一. 浏览器的进程和线程

浏览器是一个多进程,多线程的应用程序。

什么是程序,什么是线程,什么进程?

举个例子:
程序是一个工厂,而进程就是工厂中的一条流水线,工厂可能有一条流水线,也可能有多条流水线。
线程就是流水线上的工人,可能只有一个工人,也可能有多个工人。
当程序启动的时候,相当于工厂开始进行生产,流水线(进程)也启动,流水线上的工人(线程),都有自己的本职工作,轮到自己的环节,就拿过来处理。

1. 什么是进程

程序的执行就是进程。
也可以把进程看成一个独立的程序,在内存中有其对应的代码空间和数据空间,一个进程所拥有的数据和代码只属于自己。
进程是资源分配的基本单位,也是调度运行的基本单位。

2. 什么是线程

线程是进程中单独运行的一路程序。换句话说,就是一个进程可以包含多个线程,并且至少有一个主线程,同时同一进程的线程共享该进程的代码和数据。于此同时,每一个线程又都有自己的堆栈,这些堆栈对于线程来说是私有的。线程是处理机调度的基本单位。

那为什么要引入线程呢?
a) 便于调度
b) 线程可以共享进程的数据和代码,从而比进程需要通过消息才能通信来得更加简单。启动和切换的速度也比进程快。
c) 具有高并发性,可以启动多个线程执行同程序的不同部分。
d) 充分利用处理器的功能。让每一个处理器上运行不同线程,从而实现应用程序的并发性。

3. 进程和线程的区别

a) 一个进程可以拥有多个线程,而一个线程同时只能被一个进程所拥有。
b) 进程是资源分配的基本单位,线程是处理机调度的基本单位,所有的线程共享其所属进程的所有资源与代码。
d) 线程执行过程之中很容易进行协作同步,而进程需要通过消息通信进行同步。
d) 线程的划分尺度更小,并发性更高。
e) 线程共享进程的数据的同时,有自己私有的的堆栈。
f) 线程不能单独执行,但是每一个线程都有程序的入口、执行序列以及程序出口。它必须组成进程才能被执行。

浏览器启动的时候,会开辟多个进程,前端相关的主要有:
浏览器进程
网络进程
渲染进程
每一个进程,都有一块自己独立的内存空间。
多个进程,多个独立不相干的内存空间有什么好处呢,起一个隔离作用,一个进程崩溃了,不会影响其他的进程,比如,网络进程崩溃了,不会影响网页渲染。
每打开一个页面,就会创建一个渲染进程,这样的好处是,每个页面互不相干,一个页面崩溃不会影响别的页面,谷歌浏览器现在就是这样做的,但是,同样的,每一个页面,开一个渲染进程,很消耗内存,据说,谷歌浏览器已经尝试用新的做法来减少内存开销。
再来说一说几个进程的作用。
浏览器进程:这个就是负责浏览器界面(不包含网页的界面,这里的界面指的是比如浏览器上的前进、后退按钮,浏览器菜单这些)、监听用户操作(这个‘用户操作’指的是你点击了什么按钮,包括点击网页内部的按钮,键盘等用户所有操作的事件)
网络进程:这个就是负责网络通信,前端Ajax请求,就是靠它完成
渲染进程:这个是前端关系最紧密的进程,渲染进程启动后,它会开启一个渲染主线程,注意,这个是渲染主线程,线程是进程下面的工人。
前端代码都是再这个渲染主线程里执行的。

常常看到一句话:js是一门单线程语言。现在明白是哪个单线程了吧,就是渲染主线程这个单线程。

二、渲染主线程

弄清了浏览器进程、线程,再说一说这个渲染主线程。
渲染主线程的事情很多,HTML、css、js,都归它管。
看看它做了什么:
1解析HMTL,一开始的时候,页面中的HTML就是一段html字符串,需要根据规则解析它,生成dom树,dom树是一个对象,用console.dir(document)可以将它打印出来
2解析CSS,这一步是将样式收集起来,创建CSSOM树,也是一个对象,它的样子可以通过document.styleSheets查看,这是一个类数组,里面有很多样式表规则,我们还可以通过它操作样式,document.styleSheets[0].insertRule("div { color:green;}")
3计算各个元素的样式,比如把em、%的数值,换算成像素单位,计算元素所有的几何信息
4页面布局,display:none的元素没有几何信息,不会生成到布局树里面。
5处理图层,分层的目的是为了提高效率,如果某一层发生了变化,只处理当前层级就可以了,不用影响其他的元素层级,浏览器默认的滚动条,就是一个单独的层级,因为滚动条是经常变化位置的。这些层级也可以再浏览器里面看的到。
7渲染出画面
8执行全局的JS代码
9执行事件、延时器等各个地方的回调函数

主渲染线程解析Html字符串的时候,是从头到尾解析的,一开始是doctype、html...一直解析到最后一个字符,这一步就生成了dom树,cssom树。
因为js代码只要执行一遍就可以的,所以没有js树这种东西,遇到就执行。
第二步,就是将dom树对象、cssom树对象一起计算元素的样式信息
第三步,布局,各个元素对应浏览器中的位置
第四步,元素会有层级关系,要计算出来,z-index会影响这个层级计算的因素,但不是唯一的,浏览器有自己的逻辑
第五步,最后HTML字符串变成了一系列的像素点和对应的颜色,通过渲染,把dom元素画出来,。

这五步是我自己假设的大致步骤,只是为了方便描述流程,浏览器真实渲染过程肯定要复杂的多。

A. 预解析线程

如果主渲染线程解析的时候遇到css代码的时候,浏览器会启动一个叫做预解析的线程。
渲染主线程就会把css解析的工作交给预解析线程,然后渲染主线程就不管刚才交出的的css代码了,一直到预解析线程把解析结果返回给渲染主线程,渲染主线程将结果生成CSSOM树。
因此呢,css是不会阻塞页面加载的,因为css解析的主要工作就不在渲染主线程这里,所以碰到link样式,需要下载远程的css文件时,预解析线程你自己通过网络通信进程去下载就好了,下载成功也好,失败也罢,对主渲染线程来讲,都是没有感觉的,如果css文件下载失败,无非就是返回的结果不符合预期而已。
假设有一个叫1.css文件下载很麻烦,花了很长时间,会有什么影响呢?
对渲染主线程的渲染流程不会有任何影响,所以用户看到的可能是缺少1.css这部分样式的页面,然后过了好几秒,1.css才姗姗来迟,这时候会怎么样?
渲染流程会重新计算元素样式,把第二步到第五步,重新走一遍。然后用户,看到就是,页面闪了一下,页面样式变好了。

B. 回流、重绘

其实两个东西已经说过了,比如,有一个div颜色被js操作,发生了改变,布局没发生变化,这时候,就会重新绘制页面,也就是把第五步,在执行一遍,这个过程叫重绘。

如果你页面有元素发生了几何变化,比如大小变了,位置变了,那就需要重新计算、重新布局、重新渲染,这个过程叫回流,相当于从第二步开始,再走一遍。

所以我们做dom操作的时候,一定要尽量避免回流的发生。

C. transform

transform不会影响元素的样式属性,所有改变,理论上来讲,都是临时的,它发生在GPU,都不在渲染主线程里发生,所以效率也高。

如果非要对元素做left,top改变动画,可以考虑将元素设置成position:absoute;脱离文档流,这样它就是一个单独的层级,不会影响其他元素。

D. 事件循环、任务队列、异步

js是运行在渲染主线程里面的,所以,js是单线程语言。
如果在解析HTML字符串的时候,js卡住了,渲染主线程就会被阻塞,卡在解析HTML字符串这一步,后面的步骤就走不下去了。

基于js单线程这个特点,理论上,js的代码都应该是同步执行的,但是网页中常常有一些无法避免的耗时操作,比如ajax请求,如果碰到这种情况,难道还要页面停止运行,闲在那里一直等待ajax返回吗?
当然不是,所以诞生了事件循环,诞生了异步。

那么,事件循环能完全解决上面js卡住,影响HTML解析的问题吗?
答案是不行,所以你必须尽量保证你代码的质量,不要运行到一半突然来个报错,或者不要直接把某段消耗大量时间的代码放到主流程里面,尽量选择一些规避手段。

事件循环,它是渲染主线程的一种工作方式。

假设有一段js代码,里面有ajax、settimeout,当然也有普通的代码,碰到这种情况,渲染主线程会怎么做呢?
渲染主线程什么也不会做,它只会执行那些普通的代码,至于ajax、settimeout它们的回调函数,会被存到一个队列里面,让这些回调函数去排队,等普通代码全部执行完成,渲染主线程空闲的时候,它就会循环队列,将里面的回调函数取出执行,什么时候轮到回调函数,什么时候取出执行。

所以又引出了任务队列的概念。
与之相关的还有宏任务、微任务的概念,不过官方好像没有宏任务这个词,微任务也是es6里面提出的,es6里面的promise、await的回调就属于微任务。
既然官方没有,我们就不提宏任务,而是另外三个东西:微任务队列、交互任务队列、延时任务队列。

这三个队列就是消息队列里面的东西,还有其他队列,不做考虑,它们的排队权限有高低之分,微任务队列是权限最高的,交互任务次之,延时任务最低。

微任务:promise、await
交互任务: 用户操作事件
延时任务: settimeout、setinterval

  <script type="text/javascript">
    setTimeout(() => {
      console.log('settimeout 1 .....')
      new Promise((resolve, reject) => {
        console.log('promise 1 .....')
        resolve()
      }).then(() => {
        console.log('promise then 1 .....')
        new Promise((resolve, reject) => {
          console.log('promise 2 .....')
          resolve()
        }).then(() => {
          console.log('promise then 2 .....')
        })
      })
    }, 0)
    setTimeout(() => {
      console.log('settimeout 2 .....')
    }, 0)
    new Promise((resolve, reject) => {
      console.log('promise 3 .....')
      resolve()
    }).then(() => {
      console.log('promise then 3 .....')
    })
    console.log('最外部的打印.....')
  </script>
```js

打印结果:

promise 3 .....
最外部的打印.....
promise then 3 .....
settimeout 1 .....
promise 1 .....
promise then 1 .....
promise 2 .....
promise then 2 .....
settimeout 2 .....


事件循环、任务队列,搞清楚这两个概念,就明白什么是异步了。
因为js是单线程的,渲染主线程为了不被js阻塞,所以浏览器使用了一种事件循环的工作方式,这个概念就叫异步。

再说一句:单线程是异步产生的原因,事件循环是异步的实现方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352