在本节中主要围绕客户端容器对web浏览器以及跨端方案进行了详解,主要分为以下四个部分:
- 浏览器架构
- 渲染进程
- chrome运行原理
- 跨端容器
主要内容
1.浏览器架构
本小节介绍主要的浏览器架构演变及其区别、多进程的分工等
1.1浏览器架构的演变
单进程架构
单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。因为这样来运行包含很多页面甚至很多插件的和功能的浏览器,会及其的不稳定,不流畅和不安全。
多进程架构
所谓多进程浏览器当然就是将浏览器的各种不同类别的任务拆分出来,放到多个不同的进程中去执行。
面向服务架构
面向服务架构将原来各个功能模块重构成独立的服务(设备、UI、文件等),每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口。 Chrome 正处在向服务化架构过渡阶段。新架构灵活具有弹性,在内存资源充足的设备 上,基础服务将运行在独立的进程中。在内存资源不足时,多个基础服务将合并到浏览器主进程执行。如下图所示。
1.2 浏览器架构对比
进程和线程
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
包含关系:通常一个进程内有一个或多个线程,其执行过程是一条线或者多条线(线程)共同完成的;线程是进程的一部分。
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的(因为进程是资源分配的最小单位)
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
浏览器架构对比
了解了进程和线程的区别就能够更好地对浏览器架构进行对比
浏览器架构对比如下图所示
单进程架构所有功能模块都运行在同一个进程里,新增模块可能导致进程崩溃,扩展性差。通过插件可以获取到操作系统里任意资源,如果是恶意插件,将会引发安全问题,所以安全性也差。插件时很容易出现问题的模块,渲染引擎也不稳定,在单进程架构中这些进程的崩溃也会导致整个浏览器的崩溃,因此稳定性较低。单进程架构所有页面都运行在同一个进程中、页面的内存泄漏都将使得页面卡顿。而多进程架构将各个任务放在相互隔离的单独进程中,一个应用程序的崩溃通常不会影响其他的进程。多进程架构中每个进程拥有独立的内存区域,其运行在独立的沙箱中,因此有较高的稳定性和安全性。面向服务架构将浏览器程序的每个部分作为一项服务运行,在服务模块划分上比多进程架构更细致、耦合度也更低,在扩展时引入一项一项的服务,其扩展性较高。
任务管理器
查看 Chrome 中正在运行的进程数,点击右上角的选项,菜单图标→选择更多工具→任务管理器。
多进程分工
多进程架构中不同的进程其分工不同,具体如下图所示:
思考
- 为什么会有单进程架构
2007年前的浏览器都是单进程的,由于早期的浏览器要展示的网页内容比较简单,只是展示图片和文字等,很少交互的功能,所以采用单进程架构能更节省内存。但是随着技术的进步,网页不仅要展示文字图片,还要展示各种复杂的动画效果,使用JavaScript交互也越来越多。所有的功能模块都在一个进程中运行,会导致浏览器不稳定、不流畅和不安全。
- 面向服务架构是否会替代多进程架构
当浏览器在强大的硬件上运行时,面向服务架构可能会将每个服务拆分成不同的进程,从而提供更高的稳定性。而其在资源受限的设备上,浏览器将服务整合到一个进程中。这样的方案更具灵活性也兼具了安全性、稳定性等要素,所以个人认为以后会逐步过渡到面向服务的架构中。
2.渲染进程
本小节中对常见浏览器内核、多线程架构、引擎渲染过程、多线程工作流程进行介绍
2.1 常见浏览器内核
每个浏览器都有自己独有的浏览器内核的,具体如下图所示
2.2 多线程架构
浏览器内核是多线程的,分别负责页面渲染、脚本执行、事件处理、网络请求等。Chrome浏览器为每个tab页面单独启用进程,因此每个tab网页都有由其独立的渲染引擎实例。浏览器是多线程的,主要有以下几个线程:
2.3 js引擎和渲染引擎
浏览器分为两部分:渲染引擎和js引擎。
js 引擎:也称为 js解析器。用来读取网页的 javaScript 代码,对其处理后运行,比如 chrome 浏览器 V8 。
渲染引擎:用来解析 HTML 与 CSS,俗称内核,比如 chrome 的 blink,老版本的 webkit。
浏览器本身不会执行js代码。而通过内置 javaScript 引擎(解释器)来执行 JS 代码。JS 引擎 执行代码时逐行解释每一句源码(转换为机器语言),然后由计算机去执行。
js引擎主要负责解析执行JS,渲染引擎主要负责生成渲染树,两者通过桥接方式通信
2.4 多线程工作流程
JS事件循环机制
JS是单线程的,在同一时刻只能执行一个任务。程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程(通常是满足了某个条件),某个异步任务可以执行了,该任务才会进入主线程执行。
首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。
macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。
整个js的执行过程为:
1.先执行所有同步任务,碰到异步任务放到任务队列中
2.同步任务执行完毕,开始执行当前所有的异步任务
3.先执行任务队列里面所有的微任务
4.然后执行一个宏任务
5.然后再执行所有的微任务
6.再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。
之后再重复3-6的过程,形成事件循环Event Loop。
多线程工作流程
网络线程负责加载网络资源,js引擎解析脚本并执行,js解析引擎空闲时,渲染线程立即工作,用户交互、定时器等产生的回调函数放入任务队列中,事件线程进行事件循环,将队列中的任务交给js引擎执行。其过程如下图所示。
3.Chrome运行原理
3.1 如何展示网页
浏览器地址输入URL后发生了什么
1.用户输入URL,会使用浏览器默认搜索引擎加上搜索内容合成url;如果是域名会加上协议(如https)合成完整的url。
2.然后按下回车。浏览器进程通过进程间通信把url传给网络进程。(网络进程接收到url才发起真正的网络请求)。
3.网络进程接收到url后,先查找有没有缓存。有缓存,直接返回缓存的资源。 没有缓存。(进入真正的网络请求)。
首先获取域名的IP,系统会首先自动从hosts文件中寻找域名对应的 IP 地址,一旦找到,和服务器建立TCP连接;如果没有找到,则系统会将网址提交 DNS 域名解析服务器进行 IP 地址的解析。
4.利用IP地址和服务器建立TCP连接(3次握手)
5.建立连接后,浏览器构建数据包(包含请求行,请求头,请求正文,并把该域名相关Cookie等数据附加到请求头),然后向服务器发送请求消息。
6.服务器接收到消息后根据请求信息构建响应数据(包括响应行,响应头,响应正文),然后发送回网络进程。
7.网络进程接收到响应数据后进行解析。如果发现响应行的返回的状态码为301,302,说明服务器要我们去找别人要数据。找响应头中的Location字段要,Location的内容是需要重定向的地址url。获取到这个url一切重新来过。如果返回的状态码为200,说明服务器返回了数据。
8.数据传输完成,TCP四次挥手断开连接。如果,浏览器或者服务器在HTTP头部加上如下信息,TCP就一直保持连接。保持TCP连接可以省下下次需要建立连接的时间,提示资源加载速度Connection:Keep-Alive 。
9.网络进程将获取到的数据包进行解析,根据响应头中的Content-type来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器,该导航流程结束,不再进行;如果是text/html类型,就通知浏览器进程获取到文档准备渲染。
浏览器进程获取到通知之后。新建一个渲染进程。
渲染进程对文档进行页面解析和子资源加载。解析html生成DOM树,解析css生成规则树。两个树结合生成渲染树(render tree),浏览器会根据渲染树布局,计算css样式,即每个元素在页面中的位置好和大小等信息。然后为特定的节点生成专用图层。一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程。合成线程接收指令生成图块。栅格线程将图块进行栅格化,展示在屏幕上。
3.2 前端性能
下图可以看出前端加载时的主要时间消耗。
script解析和空闲时间比较长等待资源加载,加载js和网络接口等待数据回来渲染。当js中某个操作执行时间较长时,将会产生卡顿。
如何解决前端耗时问题,提升用户体验感
首屏优化
- 压缩、分包、删除无用代码。构建的时候,借助工具将代码压缩,删除无用代码。
- 静态资源分离。CDN服务器。
- js脚本非阻塞加载。javascript加载后会立即执行,同时会阻塞后面的资源加载。(javascript加载和执行的特点),所以js要放在后面,css放在前面。
- 合理使用缓存策略。使用打包工具。
- SSR服务器端渲染
- 预置loading、骨架屏。在页面数据加载完成之前,先给用户展示出页面的大致结构(灰色占位图)。
渲染优化
- GPU加速。CSS中以下属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、webGL、Video)来触发 GPU 渲染动画加速。
- 减少回流、重绘。减少对dom节点的操作。table慎用
- 离屏渲染。
- 懒加载。图片进入可视区域之后再请求图片资源的方式称为图片懒加载。
js优化
- 防止内存泄漏:使用全局变量会有全局泄露的风险,用es5、es6严格模式使用工具可以检测。dom复制给js变量,dom引用没有清空,要手动清空。定时器忘记清除会导致泄漏,最好是可以使用自动封装的定时器。
- 循环尽早break。
- 合理使用闭包。滥用闭包可能导致内存泄漏。
- 减少DOM访问。
- 防抖、节流。防抖:在事件触发 n 秒后再执行回调函数,如果在这n秒内又被触发则重新计时。(输入搜索)节流:单位时间内只触发一次回调函数,如果在这个单位时间内有多次触发只有一次生效。(监听滚动事件)
- Web Workers 。
4.跨端容器
4.1 为什么需要跨端
通过跨端降低开发成本、提升开发效率,让用户获得一致性的体验、满足前端开发生态需求
4.2 有哪些跨端方案
webview:网页视图,用于加载网页URL,并展示其内容的控件,内嵌在移动端APP内,实现前端混合开发。常用webview,安卓、ios。
js调用native:API注入;使用webview URL Scheme跳转拦载
小程序:微信、支付宝、百度小程序。渲染层:webview,双线程:多webview架构。数据通信:native转发。
RN/weex:原生组件渲染,依赖于react和vue框架,virtual dom 对dom的描述,JSBridge
Lynx:vue、js core/v8、JSBinding、Native UI/Skia
Flutter:1、widget、dart vm、skia图形库
4.3 跨端方案对比
各个跨端方案的数据对比
课程总结
参考链接
进程和线程的区别:blog.csdn.net/ThinkWon/ar…
浏览器架构:www.yuque.com/huskyzhao/q…
事件循环:blog.csdn.net/qq_41499782…
相关内容参考: