浏览器工作原理
2023-03-23 17:59:35 16 举报
AI智能生成
浏览器工作原理
作者其他创作
大纲/内容
宏观视角下的浏览器
浏览器发展历史
单进程时代
1、浏览器的所有模块都运行在同一个进程里,这些模块包括网络、插件、JavaScript 运行环境、渲染引擎和页面等。
2、问题:不稳定、不安全、不流畅。
多进程时代(chrome)
浏览器主进程
主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
渲染进程
1、html、css、js转换为可交互的网页
2、排版引擎Blink
3、Javascript V8引擎
4、每个Tab(域名)标签都会创建一个渲染进程,且都运行在沙箱模式下。
GPU进程
css3渲染
网络进程
插件进程
未来面向服务的架构
构建一个更内聚、松耦合、易于维护和扩展的系统
线程和进程
线程
1、线程不能单独存在,它是由进程来启动和管理的
2、进程中的任意一线程执行出错,都会导致整个进程的崩溃。
3、线程之间共享进程中的数据。
进程
1、一个进程就是一个程序运行的实例。
2、当一个进程关闭之后,操作系统会回收进程所占用的内存
3、进程之间的内容相互隔离。
HTTP请求流程
1、构建请求
GET /index.html HTTP1.1
2、查找缓存
浏览器判断当前请求存在缓存,则不会发起请求
3、准备IP地址和端口,即将发送TCP请求
1、首先搜索浏览器自身的DNS缓存,如果存在,则域名解析到此完成。
2、尝试读取操作系统的hosts文件看是否存在对应的映射关系,如果存在,则域名解析到此完成
3、查找本地DNS服务器(ISP服务器,或者自己手动设置的DNS服务器),如果存在,域名到此解析完成。
4、如果本地DNS服务器还没找到的话,它就会向根服务器发出请求,进行递归查询。
4、TCP连接
TCP的连接与断开
HTTP/0.9版本
请求以短连接进行,每次发送完HTTP响应之后,服务器就会断开TCP连接
HTTP/1.1 将Connection: Keep-alive写进标准
Connection: close 每次 HTTP请求都重新建立TCP连接
Connection: keep-alive 一次TCP连接发送多个HTTP请求,但是在响应的时候必须排队响应。
队头阻塞:单个TCP连接在一个时刻只能处理一个HTTP请求,也就是说一个请求处理完成后才能处理下一个请求,某个请求响应时间过长,后面的响应会被阻塞。
同一域名同时最多建立6个(chrome)TCP连接。
5、HTTP请求
请求行
请求方法+请求URL+HTTP协议版本
请求头
headers
cookie
Host...等
请求体
请求数据
6、响应请求
响应行
HTTP协议版本 + 状态码
响应头
headers
set-cookie
响应体
响应数据
7、TCP断开连接
IP
TCP
1、把数据完整地送达应用程序
2、是一种面向连接的、可靠的、基于字节流的传输层通信协议
3、TCP头 = 目标端口号 + 源端口号 + 排序序列号
4、对于数据包丢失的情况,TCP 提供重传机制
5、TCP 引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件。
UDP
1、通过端口号把数据包分发给正确的程序。
2、UDP 不能保证数据可靠性,但是传输速度却非常快。
3、数据传递可能会丢包,并且数据到达时间先后顺序可能不一致。
4、UDP头 = 目的端口号 + 源端口号
5、UDP数据包 = UDP头 + IP头 + 数据包
HTTP
1、HTTP协议和TCP协议都是TCP/IP协议簇的子集。
2、HTTP协议属于应用层,TCP协议属于传输层,HTTP协议位于TCP协议的上层。
浏览器中javascript运行机制
变量提升
1、JavaScript 代码执行过程中,需要先做变量提升,而之所以需要实现变量提升,是因为
JavaScript 代码在执行之前需要先编译。
JavaScript 代码在执行之前需要先编译。
2、在编译阶段,变量和函数会被存放到变量环境中,变量的默认值会被设置为
undefined;在代码执行阶段,JavaScript 引擎会从变量环境中去查找自定义的变量和函
数。
undefined;在代码执行阶段,JavaScript 引擎会从变量环境中去查找自定义的变量和函
数。
3、如果在编译阶段,存在两个相同的函数,那么最终存放在变量环境中的是最后定义的那
个,这是因为后定义的会覆盖掉之前定义的。
个,这是因为后定义的会覆盖掉之前定义的。
4、同名变量和函数的两点处理原则
1、如果是同名的函数,JavaScript编译阶段会选择最后声明的那个。
2、如果变量和函数同名,那么在编译阶段,变量的声明会被忽略
总结:函数提升要比变量提升的优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖
调用栈
1、每调用一个函数,JavaScript 引擎会为其创建执行上下文,并把该执行上下文压入调用栈,然后 JavaScript 引擎开始执行函数代码。
2、如果在一个函数 A 中调用了另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。
3、当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。
4、当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。
5、用栈有两个指标,最大栈容量和最大调用深度,满足其中任意一个就会栈溢出。
块级作用域
1、作用域
在 ES6 之前,ES 的作用域只有两种:全局作用域和函数作用域。
2、变量提升所带来的问题
3、ES6 引入了 let 和 const 关键字,从而使 JavaScript 也能像其他语言一样拥有了块级作用域(作用域块内声明的变量不影响块外面的变量)
4、JavaScript 是如何支持块级作用域的
5、暂时性死区
作用域链和闭包
1、作用域查找变量的链条称为作用域链,作用域链是由词法作用域决定的,而词法作用域反映了代码的结构。
2、词法作用域
3、词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
4、闭包
在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包
5、如果该闭包会一直使用,那么它可以作为全局变量而存在;但如果使用频率不高,而且占用内存又比较大的话,那就尽量让它成为一个局部变量。
this
1、当函数作为对象的方法调用时,函数中的 this 就是该对象;
2、当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
3、嵌套函数中的 this 不会继承外层函数的 this 值。
V8工作原理
栈空间和堆空间:数据是如何存储的?
JavaScript语言属于动态弱类型语言
JavaScript一共有8种数据类型,其中BigInt 和 Symbol 是新增加的两种
1、对null进行typeof运算返回的结果是Object,这个设计是最初JavaScript遗留的bug,但是为了兼容老代码,这个bug一直没有修复,什么时候会修复?或许永远也不会了。。。
2、Object类型比较特殊,是由上面7种数据类型组成的key-value对的数据类型
内存空间
在JavaScript的执行过程中,主要有三种内存空间:代码空间、栈空间和堆空间。
栈空间和堆空间
通常情况下,栈空间都不会设置太大,主要用来存放一些原始类型的小数据
而引用类型的数据占用的空间都比较大,所以这一类数据会被存放到堆中,堆空间很大,能存放很多大的数据,不过缺点是分配内存和回收内存都会占用一定的时间
在 JavaScript 中,赋值操作和其他语言有很大的不同,原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址。
垃圾回收
垃圾回收策略
手动回收策略。
如 C/C++ 就是使用手动回收策略,何时分配内存、何时销毁内存都是由代码控制的
自动回收策略。
另外一种使用的是自动垃圾回收的策略,如 JavaScript、Java、Python 等语言,产生的垃
圾数据是由垃圾回收器来释放的,并不需要手动通过代码来释放。
调用栈中的数据是如何回收的
当一个函数执行结束之后,JavaScript 引擎会通过向下移动 ESP 来销毁该函数保
存在栈中的执行上下文。
堆中的数据是如何回收的
要回收堆中的垃圾数据,就需要用到 JavaScript 中的垃圾回收器了。
代际假说和分代收集
代际假说
1、第一个是大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,
很快就变得不可访问
很快就变得不可访问
2、第二个是不死的对象,会活得更久。
V8
在 V8 中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对
象,老生代中存放的生存时间久的对象。
副垃圾回收器,主要负责新生代的垃圾回收。
主垃圾回收器,主要负责老生代的垃圾回收
垃圾回收器的工作流程
第一步是标记空间中活动对象和非活动对象。所谓活动对象就是还在使用的对象,非活动对
象就是可以进行垃圾回收的对象。
第二步是回收非活动对象所占据的内存。其实就是在所有的标记完成之后,统一清理内存中
所有被标记为可回收的对象。
所有被标记为可回收的对象。
第三步是做内存整理。
编译器和解释器中V8是如何执行一段JavaScript代码的
编译器和解释器
编译型语言在程序执行之前,需要经过编译器的编译过程,并且编译之后会直接保留机器能
读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重
新编译了。比如 C/C++、GO 等都是编译型语言。
读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重
新编译了。比如 C/C++、GO 等都是编译型语言。
而由解释型语言编写的程序,在每次运行时都需要通过解释器对程序进行动态解释和执行。
比如 Python、JavaScript 等都属于解释型语言。
V8 是如何执行一段 JavaScript 代码的
1. 生成抽象语法树(AST)和执行上下文
AST 是如何生成
第一阶段是分词(tokenize),又称为词法分析
第二阶段是解析(parse),又称为语法分析
2. 生成字节码
字节码就是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需
要通过解释器将其转换为机器码后才能执行。
3. 执行代码
JavaScript 的性能优化
1. 提升单次脚本的执行速度,避免 JavaScript 的长任务霸占主线程,这样可以使得页面
快速响应交互;
2. 避免大的内联脚本,因为在解析 HTML 的过程中,解析和编译也会占用主线程;
3. 减少 JavaScript 文件的容量,因为更小的文件会提升下载速度,并且占用更低的内
存。
浏览器中的页面循环系统
消息队列和事件循环
消息队列
消息队列是一种数据结构,可以存放要执行的任务。它符合队列“先进先出”的特点,也就是说要添加任务的话,添加到队列的尾部;要取出任务的话,从队列头部去取
任务类型
1、如输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript 定时器等等
2、包含了很多与页面相关的事件,如 JavaScript 执行、解析 DOM、样式计算、布局计算、CSS 动画等
3、通常我们把消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列。(比喻:宏任务是开会分配的工作内容,微任务是工作过程中被临时安排的内容)
4、微任务是在宏任务快要执行结束之前执行的。
事件循环流程
渲染进程专门有一个 IO 线程用来接收其他进程传进来的消息,接收到消息之后,会将这些消息组装成任务发送给渲染主线程。
如何安全退出
Chrome 是这样解决的,确定要退出当前页面时,页面主线程会设置一个退出标志的变量,在每次执行完一个任务时,判断是否有设置退出标志。
当循环系统在执行一个任务的时候,都要为这个任务维护一个系统调用栈。这个系统调用栈类似于 JavaScript 的调用栈,只不过系统调用栈是 Chromium 的开发语言 C++ 来维护的
WebAPI:setTimeout
浏览器怎么实现 setTimeout
延迟队列
在 Chrome 中除了正常使用的消息队列之外,还有另外一个消息队列,这个队列中维护了需要延迟执行的任务列表,包括了定时器和 Chromium 内部一些需要延迟执行的任务。所以当通过 JavaScript 创建一个定时器时,渲染进程会将该定时器的回调任务添加到延迟队列中。
使用 setTimeout 的一些注意事项
1. 如果当前任务执行时间过久,会影延迟到期定时器任务的执行
2. 如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔为 4 毫秒
3. 未激活的页面,setTimeout 执行最小间隔是 1000 毫秒
4. 延时执行时间有最大值
2147483647 毫秒(大约 24.8 天)
5. 使用 setTimeout 设置的回调函数中的 this 不符合直觉
WebAPI:XMLHttpRequest
1、 回调函数
将一个函数作为参数传递给另外一个函数,那作为参数的这个函数就是回调函数
2、同步回调
回调函数 callback 是在主函数返回之前执行的,我们把这个回调过程称为同步回调
每个任务在执行过程中都有自己的调用栈,那么同步回调就是在当前主函数的上下文中执行回调函数。
3、异步回调
回调函数在主函数外部执行的过程称为异步回调。
第一种是把异步函数做成一个任务,添加到信息队列尾部;
第二种是把异步函数添加到微任务队列中,这样就可以在当前任务的末尾处执行微任务了。
4、XMLHttpRequest工作流程
1、 第一步:创建 XMLHttpRequest 对象。
2、第二步:为 xhr 对象注册回调函数。
3、第三步:配置基础的请求信息。
4、发起请求
5、XMLHttpRequest 使用过程中的“坑”
1. 跨域问题
2. HTTPS 混合内容的问题
宏任务和微任务
宏任务
1、 页面中的大部分任务都是在主线程上执行的
2、WHATWG 规范定义的大致流程
3、宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合了,比如后面要介绍的监听 DOM 变化的需求
微任务
1、微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。
2、V8 引擎的层面分析:
3、每个宏任务都关联了一个微任务队列。
4、产生微任务有两种方式
第一种方式是使用 MutationObserver 监控某个 DOM 节点,然后再通过 JavaScript 来修改这个节点,或者为这个节点添加、删除部分子节点,当 DOM 节点发生变化时,就会产生 DOM 变化记录的微任务。
第二种方式是使用 Promise,当调用 Promise.resolve() 或者 Promise.reject() 的时候,也会产生微任务。
5、微任务队列何时被执行
6、如果在执行微任务的过程中,产生了新的微任务,同样会将该微任务添加到微任务队列中,V8 引擎一直循环执行微任务队列中的任务,直到队列为空才算执行结束。也就是说在执行微任务过程中产生的新的微任务并不会推迟到下个宏任务中执行,而是在当前的宏任务中继续执行。
7、结论
监听 DOM 变化方法演变
MutationObserver 采用了“异步 + 微任务”的策略
通过异步操作解决了同步操作的性能问题;
通过微任务解决了实时性的问题。
Promise:使用Promise,告别回调函数
1、产生回调地狱的原因
1、多层嵌套的问题;
2、每种任务的处理结果存在两种可能性(成功或失败),那么需要在每种任务执行结束后分别处理这两种可能性。
2、Promise 通过回调函数延迟绑定、回调函数返回值穿透和错误“冒泡”技术解决了上面的两个问题。
3、Promise 之所以要使用微任务是由 Promise 回调函数延迟绑定技术导致的。
async/await:使用同步的方式去写异步代码
1、ES7 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。
2、生成器函数:成器函数是一个带星号函数,而且是可以暂停执行和恢复执行的
3、协程
1、协程是一种比线程更加轻量级的存在。
2、以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程
3、协程不是被操作系统内核所xiec管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
4、协程的四点规则
5、V8 是如何切换到协程的调用栈
6、生成器就是协程的一种实现方式
4、async/await:async/await 技术背后的秘密就是 Promise 和生成器应用,往低层说就是微任务和协程应用。
async:async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。
await
浏览器中的页面
Chrome开发者工具:利用网络面板做性能分析
DOM树:JavaScript是如何影响DOM树构建的?
1、什么是 DOM
DOM 是表述 HTML 的内部数据结构,它会将 Web 页面和 JavaScript 脚本连接
起来,并过滤一些不安全的内容。
起来,并过滤一些不安全的内容。
2、DOM 树如何生成
第一个阶段,通过分词器将字节流转换为 Token。
至于后续的第二个和第三个阶段是同步进行的,需要将 Token 解析为 DOM 节点,并将
DOM 节点添加到 DOM 树中。
3、解析过程当中的优化
预解析操做。当渲染引擎收到字节流以后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等相关文件,解析到相关文件以后,预解析线程会提早下载这些文件
使用 CDN 来加速 JavaScript 文件的加载,压缩 JavaScript 文件的体积
若是 JavaScript 文件中没有操做 DOM 相关代码,就能够将该 JavaScript 脚本设置为异步加载,经过 async 或 defer 来标记代码
渲染流水线:CSS如何影响首次加载时的白屏时间
1、影响页面展示的因素以及优化策略
渲染流水线影响到了首
次页面展示的速度,而首次页面展示的速度又直接影响到了用户体验
2、页面请求到页面展示经历着三个阶段:
第一个阶段,请求发出后,到提交数据阶段,这时页面展示是之前的页面的内容
第二个阶段,提交数据之后渲染进程创建空白页面,通常称为解析白屏,等待CSS文件和JS文件加载完成,生成CSSOM和DOM,然后合成布局树
第三个阶段,首次渲染,页面绘制
3、第2阶段是影响页面白屏时间的重要阶段
因为这阶段名手了:解析HTML、CSS文件下载、JS文件下载、生成CSSOM、执行JS、生成布局树等。
4、优化首页白屏的方法
1.通过内联CSS、JS方式减少网络对页面渲染流程的影响,内联CSS和内联JS可以在取到HTML文件之后就可以直接开始渲染流程了
2.但是不是所有的场景都适合内联,例如CSS样式非常多,还可以尽量减少文件大小
3.在解析HTML阶段的js标记上async或者defer
4.在比较大的CSS样式可以通过媒体查询属性来加载特定的CSS文件
分层和合成机制:为什么CSS动画比JavaScript高效?
显示器是怎么显示图像的
(1)显卡
显卡的职责就是合成新的图像,并将图像保存到后缓冲区中
(2) 帧 VS 帧率
我们把渲染流水线生成的每一副图片称为一帧,把渲染流水线每秒更新了多少帧称为帧率,
比如滚动过程中 1 秒更新了 60 帧,那么帧率就是 60Hz(或者 60FPS)。
如何生成一帧图像
重排:它需要重新根据 CSSOM 和 DOM 来计算布局树,这样生成一幅图片时,会让整个渲染流水线的每个阶段都执行一遍,如果布局复杂的话,就很难保证渲染的效率了。
重绘:因为没有了重新布局的阶段,操作效率稍微高点,但是依然需要重新计算绘制信息,并触发绘制操作之后的一系列操作。
合成:相较于重排和重绘,合成操作的路径就显得非常短了,并不需要触发布局和绘制两个阶段,如果采用了 GPU,那么合成的效率会非常高。
分层和合成
(1)在 Chrome 的渲染流水线中,分层体现在生成布局树之后,渲染引擎会根据布局树的特点将其转换为层树(Layer Tree),层树是渲染流水线后续流程的基础结构。
(2)层树中的每个节点都对应着一个图层,下一步的绘制阶段就依赖于层树中的节点。绘制阶段其实并不是真正地绘出图片,而是将绘制指令组合成一个列表,比如一个图层要设置的背景为黑色,并且还要在中间画一个圆形,那么绘制过程会生成|Paint BackGroundColor:Black | Paint Circle|这样的绘制指令列表,绘制过程就完成了。
(3)有了绘制列表之后,就需要进入光栅化阶段了,光栅化就是按照绘制列表中的指令生成图片。每一个图层都对应一张图片,合成线程有了这些图片之后,会将这些图片合成为“一张”图片,并最终将生成的图片发送到后缓冲区。这就是一个大致的分层、合成流程。
分块
如果说分层是从宏观上提升了渲染效率,那么分块则是从微观层面提升了渲染效率。
在首次合成图块的时候使用一个低分辨率的图片
如何利用分层技术优化代码
恰当地使用 will-change
页面性能:如何系统地优化页面?
1. 减少 JavaScript 脚本执行时间
2. 避免强制同步布局
3. 避免布局抖动
4. 合理利用 CSS 合成动画
5. 避免频繁的垃圾回收
虚拟DOM:虚拟DOM和实际的DOM有何不同?
DOM 的缺陷
什么是虚拟 DOM
虚拟 DOM 到底要解决哪些事情
1、将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上。
2、变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。
3、在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
下虚拟 DOM 到底怎么运行的
创建阶段
更新阶段
比较两个虚拟 DOM 的过程是在一个递归
函数里执行的,其核心算法是 reconciliation
React Fiber 更新机制。
双缓存
可以让你先将计算的中间结果存放在另一个缓冲区中,等全部的计算结束,
该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,
这样就使得整个图像的输出非常稳定。
MVC 模式
其核心思想就是将数据和视图分离,也就是说视图和模型之间是不允许直接通信的,它们之间的通信
都是通过控制器来完成的
都是通过控制器来完成的
Redux 就可以构建一个 MVC 的模型,具体实现过程
图中的控制器是用来监控 DOM 的变化,一旦 DOM 发生变化,控制器便会通知模型,让其更新数据;
模型数据更新好之后,控制器会通知视图,告诉它模型的数据发生了变化;
视图接收到更新消息之后,会根据模型所提供的数据来生成新的虚拟 DOM;
新的虚拟 DOM 生成好之后,就需要与之前的虚拟 DOM 进行比较,找出变化的节点;
比较出变化的节点之后,React 将变化的虚拟节点应用到 DOM 上,这样就会触发 DOM 节点的更新;
DOM 节点的变化又会触发后续一系列渲染流水线的变化,从而实现页面的更新。
渐进式网页应用(PWA):它究竟解决了Web应用的哪些问
题?
什么是 PWA
PWA,全称是 Progressive Web App,翻译过来就是渐进式网页应用。根据字面意思,它就是“渐进式 +Web 应用”。
PWA 的定义:它是一套理念,渐进式增强 Web 的优势,并通过技术手段渐进式缩短和本地应用或者小程序的距离。
Web 应用 VS 本地应用
Web 缺陷
1、Web 应用缺少离线使用能力,在离线或者在弱网环境下基本上是无法使用的。而用户需要的是沉浸式的体验,在离线或者弱网环境下能够流畅地使用是用户对一个应用的基本要求。
2、Web 应用还缺少了消息推送的能力,因为作为一个 App 厂商,需要有将消息送达到应用的能力。
3、Web 应用缺少一级入口,也就是将 Web 应用安装到桌面,在需要的时候直接从桌面打开 Web 应用,而不是每次都需要通过浏览器来打开。
PWA 两种解决方案
通过引入 Service Worker 来试着解决离线存储和消息推送的问题
通过引入 manifest.json 来解决一级入口的问题
什么是 Service Worker
在 2014 年的时候,标准委员会就提出了 Service Worker 的概念,它的主要思想是在页面和网络之间增加一个拦截器,用来缓存和拦截请求。
Service Worker 的主要功能就是拦截请求和缓存资源。
Service Worker 的设计思路
1. 架构
Service Worker 需要在 Web Worker 的基础之上加上储存功能
Service Worker 需要会为多个页面提供服务,不能把 Service Worker 和单个页面绑定起来
2. 消息推送
消息推送也是基于 Service Worker 来实现的。
3. 安全
HTTP 采用的是明文传输信息,存在被窃听、被篡改和被劫持的风险
HTTPS 的通信数据都是经过加密的,即便拦截了数据,也无法破解数据内容,而且 HTTPS 还有校验机制,通信双方很容易知道数据是否被篡改
要使站点支持 Service Worker,首先必要的一步就是要将站点升级到 HTTPS。
Service Worker 还需要同时支持 Web 页面默认的安全策略,诸如同源策略、内容安全策略(CSP)等
WebComponent:像搭积木一样构建Web应用
什么是组件化?
:对内高内聚,对外低耦合
阻碍前端组件化的因素
CSS 的全局属性
任何地方都可以直接读取和修改 DOM
WebComponent 组件化开发
1、Customelements(自定义元素)
2、Shadow DOM(影子 DOM)
3、HTML templates(HTML模板)
要使用 WebComponent
首先,使用 template 属性来创建模板。
其次,我们需要创建一个 GeekBang 的类。
1. 查找模板内容;
2. 创建影子 DOM;
3. 再将模板添加到影子 DOM 上。
使用 customElements.define 来自定义元素了
浏览器如何实现影子 DOM
绍影子 DOM 的作用
1. 影子 DOM 中的元素对于整个网页是不可见的;
2. 影子 DOM 的 CSS 不会影响到整个网页的 CSSOM,影子 DOM 内部的 CSS 只对内部
的元素起作用。
的元素起作用。
浏览器中的网络
HTTP性能优化
超文本传输协议 HTTP/0.9
第一个是只有一个请求行,并没有HTTP 请求头和请求体,因为只需要一个请求行就可
以完整表达客户端的需求了。
第二个是服务器也没有返回头信息,这是因为服务器端并不需要告诉客户端太多信息,只
需要返回数据就可以了。
第二个是服务器也没有返回头信息,这是因为服务器端并不需要告诉客户端太多信息,只
需要返回数据就可以了。
被浏览器推动的 HTTP/1.0
支持多种类型的文件下载是 HTTP/1.0 的一个核心诉求,而
且文件格式不仅仅局限于 ASCII 编码,还有很多其他类型编码的文件。
HTTP/1.0 引入了请求头和响应头,它们都是以为 Key-Value 形式保存
的,在 HTTP 发送请求时,会带上请求头信息,服务器返回数据时,会先返回响应头信
息。
新增的特性
引入了状态码。状态码是通过响应行的方式来通知浏览器的。
Cache 机制,用来缓存已经下载过的数据。
服务器需要统计客户端的基础信息,比如 Windows 和 macOS 的用户数量分别是多少,
所以 HTTP/1.0 的请求头中还加入了用户代理的字段。
缝缝补补的 HTTP/1.1
1. 改进持久连接
HTTP/1.1 中增加了持久连接的方法,它的特点是在一个 TCP 连接上
可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会
一直保持
2. 不成熟的 HTTP 管线化
队头阻塞的问题
3. 提供虚拟主机的支持
HTTP/1.1 的请求头中增加了Host 字段,用来表示当前的域名地址,这样服务器就
可以根据不同的 Host 值做不同的处理。
4. 对动态生成的内容提供了完美支持
引入Chunk transfer 机制
5. 客户端 Cookie、安全机制
引入了客户端 Cookie 机制和安全机制。
如何提升网络速度
HTTP/1.1 为网络效率优化
1. 增加了持久连接;
2. 浏览器为每个域名最多同时维护 6 个 TCP 持久连接;
3. 使用 CDN 的实现域名分片机制。
HTTP/1.1 的主要问题
HTTP/1.1对带宽的利用率却并不理想
第一个原因,TCP 的慢启动。
第二个原因,同时开启了多条 TCP 连接,那么这些连接会竞争固定的带宽。
第三个原因,HTTP/1.1 队头阻塞的问题。
HTTP/2 的多路复用
一个域名只使用一个 TCP 长连接和消除队头阻塞问题。
多路复用机制。
多路复用的实现
HTTP/2 添加了一个二进制分帧层,
HTTP/2 其他特性
1. 可以设置请求的优先级
2. 服务器推送
3. 头部压缩
HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
TCP 的队头阻塞
把在 TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。
TCP 建立连接的延时
除了 TCP 队头阻塞之外,TCP 的握手过程也是影响传输效率的一个重要因素。
TCP 协议僵化
中间设备的僵化。
操作系统也是导致 TCP 协议僵化的另外一个原因
QUIC 协议
UDP 协议,基于 UDP 实现了类似于 TCP 的多路数据流、传输可靠性等功能,我们把这套功能称为QUIC 协议。
HTTP/3 中的 QUIC 协议功能
实现了类似 TCP 的流量控制、传输可靠性的功能。
集成了 TLS 加密功能。
实现了 HTTP/2 中的多路复用功能。
实现了快速握手功能。
HTTP/3 的挑战
第一,从目前的情况来看,服务器和浏览器端都没有对 HTTP/3 提供比较完整的支持。
Chrome 虽然在数年前就开始支持 Google 版本的 QUIC,但是这个版本的 QUIC 和官方
的 QUIC 存在着非常大的差异。
第二,部署 HTTP/3 也存在着非常大的问题。因为系统内核对 UDP 的优化远远没有达到
TCP 的优化程度,这也是阻碍 QUIC 的一个重要原因。
第三,中间设备僵化的问题。这些设备对 UDP 的优化程度远远低于 TCP,据统计使用
QUIC 协议时,大约有 3%~7% 的丢包率。
浏览器安全
同源策略:为什么XMLHttpRequest不能跨域请求资源?
浏览器安全
Web 页面安全
浏览器网络安全
浏览器系统安全
什么是同源策略
如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源
同源策略主要表现在 DOM、Web 数据和网络这三个层面。
第一个,DOM 层面。
第二个,数据层面。
第三个,网络层面。
安全和便利性的权衡
1. 页面中可以嵌入第三方资源
CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览
器是否能够执行内联 JavaScript 代码。
2. 跨域资源共享和跨文档消息机制
跨域资源共享(CORS),使用该机制可以进行跨域访问控
制,从而使跨域数据传输得以安全进行。
在介绍同源策略时,我们说明了如果两个页面不是同源的,则无法相互操纵 DOM。不过在
实际应用中,经常需要两个不同源的 DOM 之间进行通信,于是浏览器中又引入了跨文档
消息机制,可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行
通信。
跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
什么是 XSS 攻击
恶意脚本都能做哪些事情。
窃取 Cookie 信息
以监听用户行为。
修改 DOM
以在页面内生成浮窗广告
恶意脚本是怎么注入的
存储型 XSS 攻击
反射型 XSS 攻击
Web 服务器不会存储反射型 XSS 攻击的恶意脚本,这是和存储型
XSS 攻击不同的地方。
基于 DOM的 XSS 攻击
如何阻止 XSS 攻击
1. 服务器对输入脚本进行过滤或转码
2. 充分利用 CSP
3. 使用 HttpOnly 属性
CSRF攻击:陌生链接不要随便点
什么是 CSRF 攻击
1. 自动发起 Get 请求
2. 自动发起 POST 请求
3. 引诱用户点击链接
和 XSS 不同的是,CSRF 攻击不需要将恶
意代码注入用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。
如何防止 CSRF 攻击
CSRF 攻击的三个必要条件:
第一个,目标站点一定要有 CSRF 漏洞;
第二个,用户要登录过目标站点,并且在浏览器上保持有该站点的登录状态;
第三个,需要用户打开一个第三方站点,可以是黑客的站点,也可以是一些论坛。
1. 充分利用好 Cookie 的 SameSite 属性
Cookie 正是浏览器和服务器之间维护登录状态的一个关键数据
2. 验证请求的来源站点
3. CSRF Token
安全沙箱:页面和系统之间的隔离墙
安全视角下的多进程架构
现代浏览器的设计目标是安全、快速和稳定
安全沙箱
将渲染进程和操作系统隔离的这道墙就是我们要聊的安全沙箱
安全沙箱如何影响各个模块功
安全沙箱如何影响各个模块功
1. 持久存储
2. 网络访问
3. 用户交互
站点隔离(Site Isolation)
所谓站点隔离是指 Chrome 将同一站点(包含了相同根域名和相同协议的地址)中相互关
联的页面放到同一个渲染进程中执行。
HTTPS:让数据传输更安全
在 HTTP 协议栈中引入安全层
对发起 HTTP 请求的数据进行加密操作
对接收到HTTP 的内容进行解密操作。
HTTPS 协议。
第一版:使用对称加密
提到加密,最简单的方式是使用对称加密。所谓对称加密是指加密和解密都使用的是相同的
密钥。
第二版:使用非对称加密
非对称加密算法有 A、B 两把密钥,如果你用 A 密钥来加
密,那么只能使用 B 密钥来解密;反过来,如果你要 B 密钥来加密,那么只能用 A 密钥来
解密。
问题
第一个是非对称加密的效率太低。
第二个是无法保证服务器发送给浏览器的数据安全。
第三版:对称加密和非对称加密搭配使用
第四版:添加数字证书
改变
1、服务器没有直接返回公钥给浏览器,而是返回了数字证书,而公钥正是包含在数字证书中的
2、在浏览器端多了一个证书验证的操作,验证了证书之后,才继续后续流程。
数字证书的申请和验证
如何申请数字证书
浏览器如何验证数字证书
申请和使用证书的过程中,还需要注意以下三点:
1. 申请数字证书是不需要提供私钥的,要确保私钥永远只能由服务器掌握;
2. 数字证书最核心的是 CA 使用它的私钥生成的数字签名;
3.内置 CA 对应的证书称为根证书,根证书是最权威的机构,它们自己为自己签名,我们
把这称为自签名证书
把这称为自签名证书
课外加餐
浏览上下文组:如何计算Chrome中渲染进程的个数?
标签页之间的连接
第一种是通过<a>标签来和新标签建立连接
还可以通过 JavaScript 中的 window.open 方法来和新标签页建立连接
Chrome 浏览器会将浏览上下文组中属于同一站点的标签分配到同一个渲染进程中,
Chrome 浏览器为标签页分配渲染进程的策略
1、如果两个标签页都位于同一个浏览上下文组,且属于同一站点,那么这两个标签页会被
浏览器分配到同一个渲染进程中
浏览器分配到同一个渲染进程中
2、如果这两个条件不能同时满足,那么这两个标签页会分别使用不同的渲染进程来渲染
站点隔离
任务调度:有了setTimeOut,为什么还要使用rAF
单消息队列的队头阻塞问题
在单消息队列架构下,存在着低优先级任务会阻塞高优先级任务的情况,
Chromium 是如何解决队头阻塞问题的?
1. 第一次迭代:引入一个高优先级队列
2. 第二次迭代:根据消息类型来实现消息队列
3. 第三次迭代:动态调度策略
4. 第四次迭代:任务饿死
加载阶段性能:使用Audits来优化Web性能
到底什么是 Web 性能
Web 性能描述了 Web 应用在浏览器上的加载和显示的速度。
性能检测工具:Performance vs Audits
检测之前准备工作
利用 Audits 生成 Web 性能报告
解读性能报告
根据性能报告优化 Web 性能
大道至简
大道至简,学会权衡,懂得舍弃,持续进化
0 条评论
下一页