浏览器工作原理与实践
宏观视角下的浏览器
1.线程与进程之间的关系有以下特点:
1.进程中任一线程执行出错,都会导致整个进程的崩溃
2.线程之间共享进程中的数据
3.当一个进程关闭之后,操作系统会回收进程所占的内存
4.进程之间的内容互相隔离
2.浏览器的发展:
1.单进程浏览器时代:所有模块(网络、插件、js、渲染引擎、页面)都在同一个进程里面。(不稳定,不流畅,不安全)
2.早期多进程架构:浏览器主进程 + 多个渲染进程(沙箱) + 多个插件进程(沙箱)
3.目前多进程架构:浏览器主进程 + 网络进程 + GPU进程 + 多个渲染进程(沙箱) + 多个插件进程(沙箱)
4.未来面向服务架构:浏览器主进程 + 浏览器基础服务(网络进程,GPU进程,UI进程等) + 多个渲染进程(沙箱) + 多个插件进程(沙箱)
3.first paint,第一次绘制的时间,也称为白屏时间,有如下计算方式:
const timing = performance.timing;
console.log('准备新页面时间耗时: ', timing.fetchStart - timing.navigationStart);
console.log('redirect 重定向耗时: ', timing.redirectEnd - timing.redirectStart);
console.log('Appcache 耗时: ', timing.domainLookupStart - timing.fetchStart);
console.log('unload 前文档耗时: ', timing.unloadEventEnd - timing.unloadEventStart);
console.log('DNS 查询耗时: ', timing.domainLookupEnd - timing.domainLookupStart);
console.log('TCP连接耗时: ', timing.connectEnd - timing.connectStart);
console.log('request请求耗时: ', timing.responseEnd - timing.requestStart);
console.log('白屏时间: ', timing.responseStart - timing.navigationStart);
console.log('请求完毕至DOM加载: ', timing.domInteractive - timing.responseEnd);
console.log('解释dom树耗时: ', timing.domComplete - timing.domInteractive);
console.log('从开始至load总耗时: ', timing.loadEventEnd - timing.navigationStart);
4.从输入 url 到页面展示:会首先处理用户输入,判断输入的是搜索内容还是请求的url
5.浏览器收到请求后:1.重定向;2.响应数据类型处理;3.准备渲染进程;4.把 html 数据提交给渲染进程(更新前进后退状态;更新安全状态;更新地址栏url;更新web页面);5.渲染阶段(完成后会停止标签图标上的加载动画)
6.渲染流水线:构建DOM树;样式计算(把css转换为浏览器能够理解的结构、计算属性值使其标准化、计算具体样式);布局阶段(创建布局树、布局计算);分层(图层树);图层绘制(生成图层);栅格化(图层转化为图块、位图);合成和显示
7.减少重排重绘的方法:
1.改变样式:js 一次性改变;使用类名
2.批量修改DOM:隐藏元素;文档片段;脱离文档流(absolute、fixed)
3.缓存布局信息
浏览器中的 javascript
1.javascript 的执行需要经过编译阶段才能进入执行阶段。编译阶段会生成执行上下文(var和function放入变量环境,let和const放入词法环境)和可执行代码。
2.三种执行上下文(作用域):全局执行上下文;函数执行上下文;eval执行上下文
3.调用栈:用来管理函数调用关系的一种数据结构
4.作用域:指在程序中定义变量的区域,该位置决定了变量的生命周期,它控制着变量和函数的可见性和生命周期。有三种作用域:全局作用域、函数作用域、块作用域。
5.作用域链:执行上下文的变量环境中有一个 outer 指向外部的执行上下文,javascript 引擎会通过这个 outer 去外面查找变量和函数,这个查找的链条就叫作用域链。(通过作用域查找变量的链条)
6.outer 指向的外部作用域是词法作用域,也就是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,它是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
7.词法环境和变量环境的查找顺序是,先查找词法环境再查找变量环境。
8.闭包:根据词法作用域的规则,内部函数总是可以访问外部函数中声明的变量,当调用一个外部函数返回一个内部函数后,即使这个外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们把这些变量的集合叫做闭包。
9.如果闭包的内部函数被赋值给一个全局变量,则闭包的内容不会被回收,所以最好将它赋值给一个局部变量。
10.this: 在对象内部的方法中访问对象的内部属性是一个非常普遍的需求,基于这个需求,javascript 搞出来一套 this 机制。this 被绑定在执行上下文中。(执行上下文:变量环境、词法环境、outer、this)
11.this有一个严重的失误,就是嵌套函数的this不会继承外部的this,所以要使用self或者箭头函数(它没有自己的执行上下文)。