EP20-zake学浏览器

245 阅读20分钟

参考文档:

  1. 提高 DevTools 控制台调试的 12 种方法(译)
  2. 脱离996,Chrome DevTools 面板全攻略!!!

1,Chrome浏览器调试技巧

1,控制台输出

1,console.log

  • 1,使用ES6的解构输出变量名称
console.log({ variableX });
/*
output:
{ variableX: 42 }
*/

2,console.error

输出结果为红色,更显目。

3,console.table

使用表格输出,

var arr = [
  { name: '梅西', qq: 10 },
  { name: 'C 罗', qq: 7 },
  { name: '内马尔', qq: 11 },
]
console.table(arr)

复制代码

4,console.clear

清除前面代码的输出。

5,使用断言测试

断言,用来进行条件判断。当表达式为false时,则显示错误信息,不会中断程序运行。

//第一种
console.assert(
  life === 42,
  'life is expected to be',
  42,
  'but is set to',
  life
);
//第二种
console.assert(
  life === 42,
  'life is expected to be %s but is set to %s',
  42,
  life
);

6,使用性能计数器

// start timer
console.time('bigloop');

for (let i = 999999999; i > 0; i--);

// show elapsed time
console.timeEnd('bigloop');

7,console.group(), groupEnd()

分组输出信息,可以用鼠标折叠展开。

console.group('分组 1')
console.log('分组 1-1111')
console.log('分组 1-2222')
console.log('分组 1-3333')
console.groupEnd()
console.group('分组 2')
console.log('分组 2-1111')
console.log('分组 2-2222')
console.log('分组 2-3333')
console.groupEnd()

2,Element面板

3,network面板

1,网络请求瀑布图

相关字段描述
Queuing(排队)

颜色为无色。
请求出现排队的场景:

  • 存在更高优先级的请求,经常发生在images(图像)上,它被认为比关键资源(脚本/样式)优先级更低。
  • 已经打开了6个TCP连接,达到了限值,适用于http1.0/http1.1.
  • 浏览器正在短暂分配磁盘缓存的空间。
Stalled(停滞)
DNS lookup(DNS查找)
Proxy Negotiation

浏览器与代理服务器协商请求

initial connection

初始连接

SSL handshake

SSL握手,完成SSL握手所用的时间。

waiting(等待初始响应所花的时间)
content download
serviceWorker preparation

浏览器正在启动serviceWorker.

request to serviceWorker

正在将请求发送到serviceWorker

receiving push
reading push

2,summary区域

DOMContentLoaded 会比load时间小,两者的时间差大致等于外部资源加载(一般是图片、字体)。
Finish时间是页面上所有http请求发送到响应完成的时间(如果有轮询的请求),这个时间会累加。http1.0/1.1协议限定单个域名的的请求并发量为6个,即Finish是所有请求(不只是XHR请求,还包括DOC、img、js以及CSS等资源)在并发量为6的限制下完成的时间。
Finish时间与COMContentLoaded和load时间并无直接联系。

2,浏览器原理

参考文档:

1,进程与线程

进程和线程是浏览器的基本概念。
进程是CPU资源分配的最小单位。
线程是CPU调度的最小单位。 Chrome采用多进程架构,顶层存在一个浏览器进程(主进程)以协调其他进程。默认新开一个tab页,新建一个进程,单个tab页的崩溃不会影响整个浏览器。第三方插件的崩溃页不会影响浏览器。多进程可以利用CPU多核的优势。方便使用沙盒模型隔离插件等进程,提高浏览器的稳定性。缺点是浏览器需要为新开的进程分配内存,CPU等资源。

image.png 进程间的通信通过IPC机制(Inter process communication)来工作。
多进程的缺点:内存的消耗。
由于每个进程都有各自独立的内存空间,不能像同一个进程的线程那样共享内存空间,造成了一些的架构(例如V8 JavaScript引擎)会在不同进程的内存空间中同时存在,这些重复的内容会消耗更多的内存。为了节省内存,chrome会限制被启动的进程数量,当进程达到一定的界限后,chrome会将访问同一个网站的Tab页放在同一个进程中运行。

1,主进程(Brower)

负责浏览器界面的显示和交互。各个页面的管理,创建和销毁其他进程,网络的资源管理和下载,文件的读写。
导航栏,书签,前进和后退按钮。
浏览器Tab外发生的一切都是由浏览器进程控制的。浏览器进程中有很多负责不同工作的线程,包括绘制浏览器顶部按钮和导航栏输入框等组件的UI 线程(UI thread),管理网络请求的网络线程(network thread),以及控制文件读写的存储线程(storage thread)。

2,第三方插件进程

每个类型的插件对应一个进程,仅当使用该插件时才创建。

3,GPU进程

最多只有一个,用于3D绘制。
负责独立于其他进程的GPU任务,之所以被独立为一个进程,是因为要处理来自不同tab页的渲染请求,并在同一个页面上画出来。

4,渲染进程(Renderer)

浏览器渲染进程或者浏览器内核,内部是多线程的。主要负责页面渲染,脚本执行,事件处理。每个tab页一个渲染进程。
负责tab页和网页展示的相关工作。

1,GUI渲染线程
  • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和renderObject树,布局和绘制。
  • 当界面需要重绘或者回流时,该线程会执行。
  • GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时,GUI线程会会被挂起,GUI更新会被保存到一个队列中等待JS引擎执行完成,空闲时才会被执行。
2,JS引擎线程

比较典型的是V8引擎。
参考文档:


  • JS内核,负责处理JavaScript脚本程序。
  • JS引擎一直等待队列中任务的到来,一个tab页中只有一个JS引擎线程在运行。
  • 因为JS引擎和GUI渲染线程是互斥的,所以如果JS引擎的运行时间过长,会导致页面的渲染不流畅。

JS引擎包括parse,解释器,gc,和一个JIT编译器。

  • parser: 负责把JavaScript源码转为AST.
  • interperter: 解释器,负责将AST转为字节码,并解释执行。
  • JIT compiler: 对执行时的热点代码进行编译,把字节码转为机器码,之后可以直接执行机器码。
  • gc:垃圾回收器,清理堆内存中不再使用的对象。
事件循环(event loop)

JS引擎并不提供event loop ,是宿主环境为了集合渲染和JS执行,而设计的机制。
宿主环境有浏览器,node,跨端引擎等。
事件循环时宿主环境提供的,不同的宿主环境有不同的需要调度的任务,所以也会有不同的设计:

  • 浏览器里面主要是调度渲染和JS执行,还有worker
  • node里面是调度各种IO
  • 跨端引擎也是调度渲染和JS执行

执行栈与消息队列
执行栈中都是同步代码,运行到异步代码时,会将异步任务交由其他线程,待到异步任务执行完成将事件放入消息队列中。执行栈中代码执行完毕之后,会到消息队列中取一个消息执行,这样一直执行下去。

宏任务与微任务
消息队列可以分为宏任务和微任务。 在一个宏任务执行完成之后,下一个宏任务执行之前,GUI渲染线程会工作,对页面进行渲染。
在上一个宏任务执行完成之后,GUI渲染线程工作之前,会执行微任务。
宏任务
微任务

3,事件触发线程
  • 当对应的事件被触发时,事件触发线程会将事件添加到队列的末尾,等待JS引擎的执行。
4,定时器线程
  • 浏览器定时计数器不是由JS引擎计数的,因此需要通过单独的线程来计时并触发定时。
  • W3C在HTML中规定,setTimeout小于4ms的时间间隔算为4ms。
5,异步http请求线程
  • XMLHttpRequest连接后通过浏览器新开一个线程。
  • 在检测到状态变化后,如果设置了回调函数,异步线程就会生成状态变更事件,将回调函数放入事件队列中,等待JS引擎的执行。

2,浏览器的渲染流程

image.png

  1. 解析HTML文件,构建DOM树,同时浏览器主进程下载CSS文件。
  2. 解析CSS文件,构建CSSOM树,将其与DOM树合成为renderObject(渲染树)。
  3. 布局渲染树。(layout/reflow)
  4. 绘制渲染树。(paint)
  5. 浏览器主进程将默认图层和复合图层转交GPU进程,GPU进程将各个图层合并(composite),最后显示页面。

1,DOMContentLoaded事件

从页面空白到页面展示出内容的过程中,有一些资源例如图片还没有加载,但是这个时候页面已经可以进行交互了,这个时候会触发DOMContentLoaded事件。

3,window和document

1,window

  • window对象表示一个包含document对象的窗口,使用document.defaultView属性可以获取指定文档所在窗口。
  • window作为全局对象,代表了脚本正在运行的窗口,暴露给JavaScript代码。
  • 在有标签也功能的浏览器中,每个标签都有自己的window对象。如果一个方法无法恰当的作用于标签,那么就会作用于窗口。

2,document

document接口表示任何在浏览器中载入的网页,作为网页内容的入口,即是DOM树。向网页文档提供了全局操作功能。

4,浏览器缓存

首先判断是否有强制缓存,在判断协商缓存。

1,强制缓存

Expires和Cache-Control(优先级更高)。

  • Expires Expires是http/1.0控制网页缓存的字段,值是缓存的到期时间,如何再次发送请求时,如果客户端的时间小于这个值,则直接使用缓存。
  • Cache-Control http/1.1提出,替代Expires。HTTP1.0中的字段Pragma具有与Cache-Control相同的功能。
    主要取值:
  1. public:所有内容都将被缓存,客户端和代理服务器都可以缓存
  2. private:所有内容只有客户端可以缓存,是cache-control的默认值。
  3. no-cache:客户端缓存内容,但是是否使用缓存由协商缓存决定。
  4. no-store: 所有内容都不会被缓存,即不使用强制缓存也不使用协商缓存。
  5. max-age=xxx(xxx is numeric):缓存内容将在xxx秒后失效。

2,协商缓存

当强制缓存不能确定是否使用缓存时,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存。

  • Last-Modified/If-Modified-Since Last-Modified返回服务器内文件的最后修改时间。
  • Etag/If-None-Match(优先级高) Etag是服务器返回的当前资源文件的唯一标识

3,内存缓存和硬盘缓存

内存缓存

将编译解析后的文件直接存入该进程的内存中,占据进程一定的内存资源,但是一旦进程关闭,这些缓存将会被删除。

硬盘缓存

将缓存写入硬盘文件中,读取缓存时需要对硬盘执行IO操作,读取的速度比较慢。
在浏览器中,JS和图片等文件会存入内存缓存中,而CSS文件会存入硬盘缓存中。

5,浏览器跨域问题

协议,域名和端口不一致就是跨域。

1,浏览器跨域限制会导致哪些问题

  • Ajax请求不能发送
  • 不能获取其他页面的DOM元素
  • 不能获取cookie,localstorage, indexdb

2,为什么需要同源策略

保护用户信息安全,可以分为Ajax同源策略和DOM同源策略。

  • Ajax同源策略 限制不同域的服务器不能发送Ajax请求,可以一定程度上防止csrf攻击。
  • DOM同源策略 限制不同源的页面不能获取DOM,防止在iframe中嵌入正常网站,窃取用户信息。

3,为什么需要跨域

  • 前后端服务器部署在不同的云服务器上,前端同后端交互就会出现跨域。

4,如何解决跨域问题

1,jsonp

利用<script>标签不受同源策略限制的特性,

jsonp解决跨域问题的优缺点

优点:

  • 兼容性好,能够解决主流浏览器的跨域问题。 缺点:
  • 仅支持get请求
  • 容易收到XSS攻击
2,cors(跨域资源共享)

关键在于服务器端设置了Access-control-allow-origin,就可以开启CORS。浏览器的请求头中下发了一个origin头,value值与Access-control-allow-origin的值相同,服务器验证通过,就可以从服务器获得跨域资源。复杂请求和简单请求在使用CORS时,服务器返回的响应头不一样。

1,简单请求

简单请求需要满足下列条件:

  • 请求方法 get,post,head
  • 请求头 Accept,Accept-language,Content-language,Last-Event-ID,Content-type(只能是这三者:application/x-www-form-urlencoded、multipart/form-data、text/plain)

对于简单请求,浏览器直接发送CORS请求。具体来说,就是在请求头信息中,增加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器根据Origin这个字段来判断是否向客户端返回资源。
CORS设置的响应头字段,都以Access-Control开头。

  • Access-Control-Allow-Origin 必选。
    它的值要么是请求头中Origin的值,要么是*,表示接收任意域名的请求。
  • Access-Control-Allow-Credentials 可选。
    表明是否允许向服务器发送cookie,为true时表示允许。如果不允许发送cookie,响应头中删除这个字段。默认请求下,cookie是不包含在CORS中的。
  • Access-Control-Expose-Headers 可选。
    CORS请求时,XMLHttpRequest对象的getReponseHeader()方法只能拿到六个基本字段:Cache-Control,Content-language,Content-Type,Expires,Last-Modified,Pragma.如果希望拿到其他字段,就需要在Access-Control-Expose-Headers中指定。
2,复杂请求

除了简单请求就是复杂请求。
复杂请求的CORS请求,会在正式通信之前,增加一次http查询,称为预检请求。
再通过预检请求返回头中的methods和headers来判断是否发出复杂请求,如果预检请求返回头中的max-age还没到期,就不需要发出预检请求。
预检请求使用的方法时OPTIONS,表示这个请求是用来询问的,同时还包括两个其他字段。

  • Access-Control-Request-Method 必选。
    表示浏览器的复杂请求的请求方法。
  • Access-Control-Request-Headers 可选。
    表明复杂请求会额外发送的请求头。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..

预检请求的响应,服务器在收到预检请求后,根据CORS请求头的三个字段来判断是否允许跨域请求。响应报文中有如下字段:

  • Access-Control-Allow-Origin 同简单请求

  • Access-Control-Allow-Methods 必选。
    表明服务器支持的跨域请求的方法。注意,这里返回的是所有支持的方法,而不是预检请求中的方法,这样是为了避免多次发送预检请求。

  • Access-Control-Allow-Headers 如果预检请求中包含Access-Control-Request-Headers,则该字段是必须的。也是返回服务器支持的所有头字段信息。

  • Access-Control-Allow-Credentials 同简单请求

  • Access-Control-Max-Age 可选。
    用来指定本次预检请求的有效期,单位为妙。

6,cookie,localstorage,sessionstorage

1,cookie

由于http是无状态的,为了在服务端和客户端做状态保持,所以就产生了cookie。cookie的本质是一份在服务端和客户端之间传输的文本,其中保存了一些客户端的状态信息,方便服务端读取并进行操作。
cookie是不能跨域访问的。
session需要通过cookie来实现。
客户端的cookie的数量不能超过300个,每个大小不得4KB.每个web站点设置的cookie数量不能超过20个

1,如何禁止JavaScript访问cookie

一般来说,只有服务器操作cookie才能保证一些必要的安全。但是有的时候需要前端来对cookie进行增删改查。
将cookie选项设置为HttpOnly=true,对于document.cookie来说是透明的。
Set-Cookie: name=value; HttpOnly

2,Chrome设置SameSite by default cookies实现跨域

地址栏输入flags进入配置页面。
将SameSite by default cookies设置为disabled。

3,cookie字段
  • Name
  • Value
  • Domain 能够携带该cookie的域名,开头为.表示子域名也可携带
  • Path 能够携带该键值对的文件路径
  • Expires/Max-Age
  • Size
  • HttpOnly 禁止JavaScript对cookie进行读取和修改
  • Secure 仅允许在HTTPS中传递
  • Samesite 禁止某些跨站请求携带cookie,值为None,Lax,Strict。
    None:任何请求都可以携带cookie。 Lax:仅 a, link, Get请求可以发送,其他的请求不可发送 Strict:所有跨站请求都不可发送。

3,相关的题目

1. 为什么JavaScript是单线程的。

如果JavaScript是多线程的,多个线程同时操作DOM,会出现冲突。

2. 为什么JS运行会阻塞页面的加载。

因为JavaScript可以操作DOM,如何在JS操作DOM的时候,渲染页面,渲染线程在渲染页面前后获取的数据不一致,与JS修改DOM所要达到的预期不一致。
为了防止出现这种情况,浏览器渲染线程与JS引擎线程是互斥的。当JS在运行的时候,GUI渲染会被暂停。

3. CSS的加载会造成阻塞吗

CSS的加载与解析是与HTML的加载解析并行的,CSS的加载不会造成DOM树的生成,但是渲染树的生成同时需要DOM树和CSSOM树,所以会阻塞渲染树的生成。

4. DOMContentLoaded与load的区别。

当文档中没有脚本文件时,文档加载完就是触发DOMContentLoaded。如果包含脚本文件,而脚本会阻塞文档的解析,如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,会先去加载脚本,再执行。并且脚本需要等CSSOM构建完成之后才能执行,也就是说需要样式文件加载并解析完毕之后才执行脚本,这是因为JavaScript可以查询对象的样式。但是在任何情况下,DOMContentLoaded都不需要等待图片等其他资源加载完成。
当onload事件触发时,页面上的所有DOM,样式表,脚本,图片等资源都加载完成。

5. 什么时CPR(关键渲染路径)?如何优化?

关键渲染路径时浏览器将HTML,CSS,JavaScript转换为在屏幕上呈现的内容所经历的一系列步骤,就是浏览器的渲染流程。 为了尽快完成首次渲染,需要最大限度减小以下三种可变因素:

  • 关键资源的数量:可能阻止网页首次渲染的资源。
  • 关键路径的长度:获取所有关键资源所需的往返次数或者总时间。
  • 关键字节:实现网页首次渲染所需要的总字节数,等同于所有关键资源传送文件的总和。
    如何优化:
  1. 优化DOM
  • 删除不必要的代码和注释,尽量减少文件的大小。
  • 可以利用GZIP压缩文件。
  • 利用浏览器缓存。
  1. 优化CSSOM
  • 减少关键CSS元素数量。
  • 媒体查询的类型,极大的影响了CPR的性能。
  1. 优化JavaScript 当浏览器遇到script标签时,会阻止DOM的解析,知道CSSOM构建完成,才会运行JavaScript,然后继续DOM的解析。
  • async: 加载不会阻止解析DOM,加载完会执行脚本。
  • defer:会等到解析DOM完之后,再执行脚本,是按照脚本的加载顺序执行的。

image.png

6. 回流和重绘

1, 回流
当渲染树部分或者全部的元素的尺寸、结构、或者某些属性发生变化时,浏览器重新渲染部分或者全部文档的过程称为回流。每次回流会将页面清空,从左上角第一个色素到右下角最后一个元素一点一点渲染。
1,会导致回流的操作

  • 1,页面的首次渲染
  • 2,浏览器的窗口大小发生改变
  • 3,元素的尺寸(外边距,内边距,边框厚度,高度,宽度)或者位置改变。
  • 4,元素的内容改变(input输入框中输入文字,文字的数量发生改变,图片大小)
  • 5,文字的大小发生改变。
  • 6,添加或者删除可见的DOM元素
  • 7,激活css伪类(:hover)
  • 8,查询某些属性或者调用某些方法 2,一些常用的会导致回流的属性和方法
    clientWidth, clientHeight, clientTop, clientLeft,
    offsetWidth, offsetHeight, offsetTop, offsetLeft,
    scrollWidth, scrollHeight, scrollTop, scrollLeft,
    scrollIntoView(), scrollIntoViewIfNeeded(),
    getComputedStyle()(currentStyle in IE) ,
    getBoundingClientReact(),
    scrollTo(),
    每次回流都会产生计算消耗,大多数浏览器通过队列化修改并批量执行来优化回流过程。
    使用一些方法的时候会强制刷新队列并要求计划任务立即执行。
    使用getComputedStyle()方法获取布局信息时,浏览器会为了获取最新的布局信息,会要求渲染队列里面的待处理变化立即执行,从而触发了一次回流。

2,重绘
当元素的外观发生改变(color, background-color,visibility, outline),但是没有改变布局,浏览器会将新的样式赋予元素并重新绘制。
1,常见的引起重绘的属性

  • color,visibility
  • border-style,border-radius,text-decoration, outline-color, outline, outline-style, outline-width, box-shadow
  • backgroud, backgroud-color, backgroud-image, background-position, background-repeat, background-size

3,如何避免回流和重绘
CSS

  • 避免使用table布局。
  • 避免设置多层内联样式。
  • 将动画效果设置到position属性为absolute或者fixed的元素上。
  • 避免使用CSS表达式。 JavaScript
  • 避免频繁操作样式。
  • 避免频繁操作DOM,创建一个documentFragment,在他上面应用所有的DOM操作,最后把他添加到文档中。
  • 可以为元素设置 display:none,操作结束在显示出来,设置为这个样式之后,操作过程中不会引起重拍或者重绘。
  • 避免频繁读取会导致重拍或者重绘的属性。

7,display:none ,visibility,opacity区别

  • visibility设置为hidden会隐藏元素,但是元素还在文档流中,会触发重绘
  • display设置为none会隐藏元素,元素的位置不被保存,会触发重排。
  • opacity将元素设置为透明,触发重绘。