admin管理员组文章数量:1567448
01 | Chrome架构:仅仅打开了1个页面,为什么有4个进程?
- 渲染 io 插件 网络 等等
02 | TCP协议:如何保证页面文件能被完整送达浏览器?
- 计算机网络, 数据先tcp再ip
03 | HTTP请求流程:为什么很多站点第二次打开速度会很快?
04 | 导航流程:从输入URL到页面展示,这中间发生了什么?
- 经典面试题: 加上浏览器渲染过程, 缓存, 后端架构(组成一个完美答案)
05 | 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?
- 这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
- 把 CSS 转换为浏览器能够理解的结构
- 当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
- 转换样式表中的属性值,使其标准化
- 创建布局树
- css属性会继承
06 | 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
- z-index分层结构
- 通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)
- 一个完整的渲染流程大致可总结为如下:
渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
创建布局树,并计算元素的布局信息。
对布局树进行分层,并生成分层树。
为每个图层生成绘制列表,并将其提交到合成线程。
合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
合成线程发送绘制图块命令DrawQuad给浏览器进程。
浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
07 | 变量提升:JavaScript代码是按顺序执行的吗?
-
变量提升, 有个执行上下文的对象
-
定义函数存储在堆中
-
当函数被调用时, 会在变量对象中查找该函数
-
编译过程是非常复杂的, 包括词法分析, 语法分析, 代码优化, 代码生成等
-
下面代码会输出什么
function showName() {
console.log('极客邦');
}
showName();
function showName() {
console.log('极客时间');
}
showName();
-
先是编译阶段,纪录函数到栈中, 纪录变量对象。再是执行阶段同样都是输出“极客时间”,后定义的会覆盖之前变量的值
-
但是Python中不是这样, 这跟编译器的区别有关
08 | 调用栈:为什么JavaScript代码会出现栈溢出?
- 调用栈就是用来管理函数调用关系的一种数据结构
- 栈可以理解为一个胡同
- 管理执行上下文的栈成为执行上下文栈又称调用栈
- 程序的执行过程
var a = 2
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return a+result+d
}
addAll(3,6)
- 创建全局上下文, 并将其压入栈底
- 先初始化变量后进入代码的执行阶段。赋值,将a赋值成为2
- 然后调用addAll函数,js引擎会编译该函数, 并创建执行上下文,最后将该函数执行上下文压入栈中
- 进入代码执行阶段, 将d赋值为10
- 当执行到add()函数时还会创建执行上下文, 并压入栈
- 当add()函数返回时,函数的执行上下文就会从栈顶弹出, 并将result的值设置成add()函数的返回值
- 最后addAll执行并返回, 也会从栈顶弹出来,此时只剩下全局上下文了
- 至此,整个JavaScript流程执行结束
- 调用栈是JavaScript引擎追中函数执行的一个机制
- call Stack就是调用栈信息
- console.trace()可以输出函数的调用关系
- 因为调用栈有大小,写递归的代码容易出现栈溢出, 就是函数的调用栈
- 调用栈有两个指标, 最大栈容量和最大调用深度
- 算法经典思想:循环消除尾递归
09 | 块级作用域:var缺陷以及为什么要引入let和const?
- 为什么js会有变量提升?
作用域: 在ES6之前只有两种作用域: 全局作用域和函数作用域
全局作用域在代码中的任何地方都会被访问, 伴随这页面的生命周期
函数级作用域: 定义的变量或函数只能在函数内部被访问. 函数执行之后, 函数内部营地的变量会被销毁.
块级作用域在ES6之后被支持就是使用大括号包裹的代码, 判断语句, 循环都是块级作用域
- 外部不能访问内部, 内部可以访问父节
- 点的外部
- js会优先执行当前执行上下文中的变量
- let关键字是可以被修改的, const不可以被修改
- Python中变量提升: 将函数的局部作用域的变量提升到全局
- gloabal n n = 100
def run():
global n
n = 100
# 必须先执行才会创建变量n
run()
print(n)
- 变量提升存在变量覆盖, 变量污染的缺陷, 使用块级作用域来解决
- js中存在变量提升和函数提升, Python中并不存在, 只是从上到下执行代码
let myname = "极客时间";
{
console.log(myname);
let myname = "极客邦";
}
- 上述代码运行时会报错, 变量只是创建时会被提升, 但是初始化并未提升
10 | 作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?
function bar() {
console.log(myName)
}
function foo() {
var myName = " 极客邦 "
bar()
}
var myName = " 极客时间 "
foo()
- 作用域链: 每个执行上下文的变量环境中, 都包含一个外部引用, 用来指向外部上下文, 这个外部的引用称为outer
- 当一段代码中使用了一个变量, js引擎会在当前的执行上下文查找该变量
- 两者的外部指向执行上下文都是全局执行上下文
- Python中的闭包是因为函数是一等公民, 和go中类似了但是js中的闭包是返回一个对象, 都是返回一个对象, 对象里面有函数或者, 这个对象可以被调用, 执行里面的代码.
- 闭包是如何销毁的, 垃圾回收器会判断
- 作用域链是查找变量的路径
- 词法环境用于存放let 和 const的内容
11 | this:从JavaScript执行上下文的视角讲清楚this
- 每个执行上下文都会绑定一个this
- 全局执行上下文的this指向window对象
- 嵌套函数中的 this 不会继承外层函数的 this 值。
- 可以用that = this或者使用箭头函数, 箭头函数 不会创建执行上下文
- 里面的this就是外层函数的this
- this就是该对象
12 | 栈空间和堆空间:数据是如何存储的?
- js在运行过程中是如何存储数据的? js的内存模型
- 栈空间就是调用栈, 存储执行上下文
- 当存在引用类型, 就不存放在栈中, 而是存放地址, 指向堆, 堆中存储Object数据.值类型则保存在栈中, 因为栈空间有限, 存储太多内容会造成栈溢出
- 堆空间很大, 能存放很多数据, 不过分配内存和回收内存都会占用一定的时间
- 闭包: 内部函数引入了外部函数的变量
- 原始类型和引用类型, Python都是引用类型, 但是分为可变对象和不可变对象
13 | 垃圾回收:垃圾数据是如何自动回收的?
- 有的垃圾回收之后后会产生内存随便, 需要整理
- v8引擎主垃圾回收器采用标记清除法但是会产生内存碎片, 优化是标记整理算法
- 一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿
- 为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为增量标记(Incremental Marking)算法
- 使用增量标记算法,可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样当执行上述动画效果时,就不会让用户因为垃圾回收任务而感受到页面的卡顿了。
- v8引擎分为新生代和老生代
14 | 编译器和解释器:V8是如何执行一段JavaScript代码的?
-
编译器和解释器
-
v8是如何执行js的
- 编译器和解释器只能理解AST: 抽象语法树, 类似于将HTML编译成DOM树
- babel可以将es6转化成es5
- ESLint也是通过AST
- AST是怎么产生的: 先是分词(词法分析) parse:语法分析 语义分析
- 生成字节码或二进制码
其实一开始 V8 并没有字节码,而是直接将 AST 转换为机器码,由于执行机器码的效率是非常高效的,所以这种方式在发布后的一段时间内运行效果是非常好的。但是随着 Chrome 在手机上的广泛普及,特别是运行在 512M 内存的手机上,内存占用问题也暴露出来了,因为 V8 需要消耗大量的内存来存放转换后的机器码。为了解决内存占用问题,V8 团队大幅重构了引擎架构,引入字节码,并且抛弃了之前的编译器,最终花了将进四年的时间,实现了现在的这套架构。
- 字节码就是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需要通过解释器将其转换为机器码后才能执行。
- 当出现了热点代码就是编译成机器码
- 字节码配合解释器和编译器就是Java Python的虚拟机称为JIT技术
- WebAssembly就像java先编译成字节码然后执行在虚拟机上.
15 | 消息队列和事件循环:页面是怎么“活”起来的?
-
如何处理高优先级的任务?
-
如果有一些确定好的任务,可以使用一个单线程来按照顺序处理这些任务,这是第一版线程模型。
-
要在线程执行过程中接收并处理新的任务,就需要引入循环语句和事件系统,这是第二版线程模型。
-
如果要接收其他线程发送过来的任务,就需要引入消息队列,这是第三版线程模型。
-
如果其他进程想要发送任务给页面主线程,那么先通过 IPC 把任务发送给渲染进程的 IO 线程,IO 线程再把任务发送给页面主线程。
-
消息队列机制并不是太灵活,为了适应效率和实时性,引入了微任务。
-
宏任务是开会分配的工作内容,微任务是工作过程中被临时安排的内容
16 | WebAPI:setTimeout是如何实现的?
- setTimeout是一个定时器,用来指定某个函数在多少毫秒之后执行。
- 延时器最大为21.8天, 否则会立即执行
17 | WebAPI:XMLHttpRequest是怎么实现的?
- 将一个函数作为参数传递给另一个函数, 这个函数就叫回调函数
- XMLHttpRequest就是不用刷新整个界面只需要局部刷新就可以
- 在主函数外部执行的过程叫异步回调
18 | 宏任务和微任务:不是所有任务都是一个待遇
- Promise就是微任务
- 宏任务: 渲染, 交互, js脚本
- 为什么宏任务无法满足时间精度高的需求
- 宏任务和微任务是绑定的
- web应用要监控DOM变化, 是前端很重要的需求
- mutation event采用了观察者模式
- 异步+微任务(解决实时性问题) 微任务队列
19 | Promise:使用Promise,告别回调函数
- 这一讲没听懂, 等以后心情好了再研究研究
20 | async/await:使用同步的方式去写异步代码
- ES7引入了async/await
- 一个线程可以存在多个协程, 但是线程上只能执行一个协程
- 协程加时间循环
21 | Chrome开发者工具:利用网络面板做性能分析
-
- 浏览器会为每个域名最多维护 6 个 TCP 连接
- TTFB: 第一字节时间
- 排队时间过久,大概率是由浏览器为每个域名最多维护 6 个连接导致的。
- 还建议你把站点升级到 HTTP2,因为 HTTP2 已经没有每个域名最多维护 6 个 TCP 连接的限制了。
- 资源放在多个域名下面,比如放到 3 个域名下面,这样就可以同时支持 18 个连接了,这种方案称为域名分片技术。
- 第一字节时间(TTFB)时间过久
22 | DOM树:JavaScript是如何影响DOM树构建的?
- 渲染引擎无法理解HTML, 需要组成DOM树
- js可以访问DOM
- HTML Parser就是负责将HTML字节流转化为DOM结构
- 字节流转化为DOM也都是通过编译技术
- 遇到js会进行下载
23 | 渲染流水线:CSS如何影响首次加载时的白屏时间?
- css对页面的渲染也造成了影响
24 | 分层和合成机制:为什么CSS动画比JavaScript高效?
- css动画比js动画更加高效
25 | 页面性能:如何系统地优化页面?
- 通常 1 个 HTTP 的数据包在 14KB 左右
- 内联css会减少资源请求数量
- 减少 JavaScript 脚本执行时间
26 | 虚拟DOM:虚拟DOM和实际的DOM有何不同?
- 两大著名前端框架 React 和 Vue 都使用了虚拟 DOM
- 减少 JavaScript 对 DOM 的操作,这时候虚拟 DOM 就上场了。
- 将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上。
- 变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。
- 在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。
- 在执行算法的过程中出让主线程yield
- 虚拟 DOM 看成是 DOM 的一个 buffer
- MVC核心思想就是将数据和视图分离
27 | 渐进式网页应用(PWA):它究竟解决了Web应用的哪些问题?
- 第一个是应用程序 Web 化;
- 第二个是 Web 应用移动化;
- 第三个是 Web 操作系统化;
- PWA渐进式网页应用
- 渐进式 +Web 应用
- Web 应用缺少离线使用能力,在离线或者在弱网环境下基本上是无法使用的
- Web 应用还缺少了消息推送的能力,因为作为一个 App 厂商,需要有将消息送达到应用的能力。
- Web 应用缺少一级入口,也就是将 Web 应用安装到桌面,在需要的时候直接从桌面打开 Web 应用,而不是每次都需要通过浏览器来打开。
- Service Worker 来试着解决离线存储和消息推送的问题,通过引入 manifest.json 来解决一级入口的问题。
- 为了避免 JavaScript 过多占用页面主线程时长的情况,浏览器实现了 Web Worker 的功能。Web Worker 的目的是让 JavaScript 能够运行在页面主线程之外,不过由于 Web Worker 中是没有当前页面的 DOM 环境的,所以在 Web Worker 中只能执行一些和 DOM 无关的 JavaScript 脚本
28 | WebComponent:像搭积木一样构建Web应用
- 组件化思维
29 | HTTP/1:HTTP性能优化
- content-encoding: br
- content-type: text/html; charset=UTF-8
- 服务器的响应信息
- HTTP1.1提供了keep-alived, 使用CDN实现域名分片机制
- HTTP1.1: 队头阻塞问题, 同一个域名6个链接, 请求头文本传输
30|HTTP/2:如何提升网络速度?
- RTT: 单次请求响应
- HTTP1.1不能将带宽跑满
- HTTP2实现了并行请求
- HTTP2的特性
- 可是设置请求优先级
- 多路复用是HTTP2最核心的功能
- 服务器可以主动推送, 就不用等着请求再要
- 头部压缩: 请求头和响应头
- 影响HTTP/1.1效率的主要原因: TCP的慢启动, 多条TCP链接竞争带宽, 队头阻塞
- HTTP/2多路复用在协议栈中添加了二进制分帧层
- 但是还是基于TCP的, 还是会出现数据包的队头阻塞
- TCP的慢启动: 发送数据由慢到快: 为了解决网络拥塞
- HTTP2思路是一个域名只使用一个TCP长连接传输数据, 这样只需要一次慢启动, 同时也避免了多个TCP链接竞争资源
- 解决队头阻塞问题采用并行请求, 就是任何时候都可以请求, 不需要等待上一个请求完成.
31|HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
- HTTP3解决HTTP/2的TCP的队头阻塞问题
- TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。
- 这不同于 HTTP/1.1,使用 HTTP/1.1 时,浏览器为每个域名开启了 6 个 TCP 连接,如果其中的 1 个 TCP 连接发生了队头阻塞,那么其他的 5 个连接依然可以继续传输数据。
- 所以随着丢包率的增加,HTTP/2 的传输效率也会越来越差。有测试数据表明,当系统达到了 2% 的丢包率时,HTTP/1.1 的传输效率反而比 HTTP/2 表现得更好。
- 除了 TCP 队头阻塞之外,TCP 的握手过程也是影响传输效率的一个重要因素。
- 网络延迟又称为 RTT(Round Trip Time)。我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为 RTT
- RTT 是反映网络性能的一个重要指标。
- 我们知道 HTTP/1 和 HTTP/2 都是使用 TCP 协议来传输的,而如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程。
- 在建立 TCP 连接的时候,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完 1.5 个 RTT 之后才能进行数据传输。
- 进行 TLS 连接,TLS 有两个版本——TLS1.2 和 TLS1.3,每个版本建立连接所花的时间不同,大致是需要 1~2 个 RTT
- 如果浏览器和服务器的物理距离较近,那么 1 个 RTT 的时间可能在 10 毫秒以内
- 但如果服务器相隔较远,那么 1 个 RTT 就可能需要 100 毫秒以上了
- HTTP/3采用了UDP协议, 基于 UDP 实现了类似于 TCP 的多路数据流、传输可靠性等功能,我们把这套功能称为QUIC 协议
- 实现了类似 TCP 的流量控制、传输可靠性的功能
- 集成了 TLS 加密功能
- 实现了 HTTP/2 中的多路复用功能
- 实现了快速握手功能
- HTTP/3还是存在很多挑战: 因为系统对UDP的优化程度没有TCP好会丢包
- TCP+HTTP/2 的多路复用 +TLS 等功能 QUIC协议
32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?
- 在没有安全保障的 Web 世界中,我们是没有隐私的,因此需要安全策略来保障我们的隐私和数据的安全。
- 比如你打开了一个银行站点,然后又一不小心打开了一个恶意站点,如果没有安全措施,恶意站点就可以做很多事情:修改银行站点的 DOM CSSOM 等信息;在银行站点内部插入 JavaScript 脚本;劫持用户登录的用户名和密码;读取银行站点的 Cookie、IndexDB 等数据;
- 如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源
- 浏览器默认两个相同的源之间是可以相互访问资源和操作 DOM 的
- 同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据
- 为了解决XSS工具引入CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。通过这些手段就可以大大减少 XSS 攻击。
- 两个不同源的 DOM 是不能相互操纵的,因此,浏览器中又实现了跨文档消息机制,让其可以比较安全地通信。 CORS
33 | 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
- 默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,然后又通过 CORS 策略来支持其跨域。
- XSS就是跨站脚本
- 喜马拉雅的存储性XSS攻击用户设置专辑名称时,服务器对关键字过滤不严格,比如可以将专辑名称设置为一段 JavaScript
- 一句话前端传来的内容都不可信
- HttpOnly的cookie只能通过HTTP请求, 不能通过js读取
34 | CSRF攻击:陌生链接不要随便点
- 邮箱被盗执行恶意脚本
- CSRF攻击会利用用户的登录状态发起攻击利用好cookie的SameSite属性, 前后端分离会禁止CSRF但是通过auth2规范, 带上token
- CSRF token post请求会带上csrftoken
35 | 安全沙箱:页面和系统之间的隔离墙
36 | HTTPS:让数据传输更安全
- 页面安全, 系统安全, 网络安全
- 非对称传输秘钥, 对称传输数据.
- 服务器返回数字证书, 而不是直接返回公钥, 因为证书无法伪造
- 向CA申请数字证书, 通过hash函数计算提交的明文信息, 并得出摘要, 然后用私钥对摘要加密, 密文就是CA给极客时间的数字签名
- 要确保私钥永远自己保存
- https://freessl 免费申请https证书
结束语 | 大道至简
版权声明:本文标题:浏览器工作原理与实践 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1726872154a1087966.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论