客户端容器|青训营笔记

173 阅读24分钟

一、浏览器架构

1、浏览器架构演进

  • 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等

    • 在早期的浏览器中,所有的功能都在一个进程中完成,包括用户界面、JavaScript执行、CSS渲染和网络请求等。单进程架构的优势是简单、易于实现,但缺点也很明显,如:不稳定(一个标签页崩溃可能导致整个浏览器崩溃)、不安全(攻击者可以利用漏洞来控制整个浏览器)以及性能瓶颈(多个标签页争抢资源,导致性能下降)。
  • 多进程架构:主进程、网络进程、渲染进程、GPU进程、插件进程

    • 为了解决单进程架构的问题,浏览器厂商开始将浏览器的功能拆分为多个进程。例如,谷歌推出的Chrome浏览器就采用了多进程架构。在这种架构下,浏览器将每个标签页(或每个渲染实例)分配给一个单独的进程,从而实现了进程间的隔离,防止单个网页崩溃影响到其他界面。多进程架构提高了浏览器的稳定性、安全性和性能,但同时也增加了内存消耗
    • 每一个Tab运行在独立的沙盒里面
  • 面向服务架构:将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础服务

    • 面向服务架构 => 多进程架构升级版本
    • 随着Web平台的发展,浏览器需要处理越来越多的任务,这使得多进程架构变得复杂且难以维护。面向服务架构的出现旨在将浏览器的各个功能模块化,将它们作为独立的服务来运行和维护。在这种架构下,每个服务都可以独立地使用一个或多个进程,根据需求自动扩展或缩减资源。这样可以进一步提高浏览器的稳定性、安全性和性能,同时优化资源利用率

image.png

2、浏览器架构对比

image.png

3、浏览器架构任务器

在多进程浏览器架构中,任务管理器是一个非常重要的组件。任务管理器负责监控、管理和分配浏览器的各个进程,确保整个浏览器系统的稳定性和性能。

image.png

4、浏览器架构多进程分工

image.png

问题思考:

为什么会有单进程架构?

单进程架构在早期的浏览器中出现,当时的Web技术相对较为简单,浏览器需要处理的任务和复杂性远不及现在。在这个背景下,单进程架构具有一些优势:

简单性:单进程架构更易于实现和维护,因为它不需要处理多进程间的通信和资源分配问题。对于早期的浏览器开发者来说,单进程架构提供了一种快速实现浏览器功能的方法。

资源消耗:相较于多进程架构,单进程架构在内存和CPU资源消耗方面较低。因为多进程架构中,每个进程都需要占用一定的系统资源,而单进程架构只需要维护一个进程。在计算机硬件资源有限的早期,这一点是非常重要的。

兼容性:早期的Web技术和标准相对较为简单,单进程架构在兼容性方面表现优秀。因为不需要处理多进程间的兼容性问题,单进程浏览器更容易支持不同的操作系统和硬件平台。

面向服务架构是否会替代多进程架构?

面向服务架构在某种程度上可以看作是多进程架构的一种演进和优化。这种架构将浏览器的各个功能模块化,作为独立的服务来运行和维护。这样可以进一步提高浏览器的稳定性、安全性和性能,同时优化资源利用率。

尽管面向服务架构具有一定的优势,但是它不一定会完全替代多进程架构。这是因为两种架构各自有不同的应用场景和优缺点:

面向服务架构更适用于复杂的浏览器系统,它可以更好地管理和维护浏览器的各个功能模块。然而,在一些轻量级或特定用途的浏览器中,多进程架构可能仍然是一个合适的选择,因为它相对简单且易于实现。

面向服务架构的优势在于模块化和灵活性,它可以根据需求自动扩展或缩减资源。但是,这种架构可能需要更多的系统资源和开发维护成本。多进程架构在资源消耗和开发成本方面可能更具优势。 不同浏览器厂商可能会根据自己的产品定位和技术栈选择不同的架构。例如,谷歌Chrome浏览器采用了多进程架构,并在此基础上引入了一些面向服务架构的设计理念。而其他浏览器厂商可能会选择完全基于面向服务架构的实现。

二、渲染进程

1、常见浏览器内核

image.png

2、多线程架构

内部是多线程实现,主要负责页面渲染,脚本执行,事件处理,网络请求等。

image.png

3、JS引擎 VS 渲染引擎

  • JS引擎和渲染引擎是浏览器中两个非常重要的组件。它们分别负责处理JavaScript代码和渲染网页内容。

(1)解析执行JS

  • JS引擎:JS引擎负责解析和执行JavaScript代码。它会将JavaScript源代码解析成抽象语法树(AST),然后通过JIT编译器将AST编译成机器代码,最后执行机器代码。在这个过程中,JS引擎还会进行优化,提高代码的执行效率。常见的JS引擎包括V8(用于Chrome和Node.js)、SpiderMonkey(用于Firefox)和JavaScriptCore(用于Safari)。

  • 渲染引擎:渲染引擎的主要任务是解析HTML和CSS,构建DOM树和渲染树,然后将渲染树转换为屏幕上的像素。在这个过程中,渲染引擎并不直接处理JavaScript代码。然而,当遇到需要执行的JavaScript代码时(例如,通过<script>标签或事件处理程序),渲染引擎会将控制权交给JS引擎。一旦JS引擎执行完毕,渲染引擎会继续执行后续的渲染任务。

(2)XML解析生成渲染树,显示在屏幕

  • JS引擎:JS引擎主要处理JavaScript代码,不负责解析XML或生成渲染树。然而,JavaScript可以通过DOM API操作DOM树,从而间接地影响渲染树的生成和更新。
  • 渲染引擎:渲染引擎负责解析HTML和XML文档,构建DOM树。然后,渲染引擎会解析CSS样式,并将样式信息附加到DOM树上,生成渲染树。接下来,渲染引擎会计算每个节点的布局信息(如位置和大小),并将渲染树转换为屏幕上的像素。这个过程被称为“绘制”或“渲染”。

(3) 桥接方式通信

  • JS引擎和渲染引擎之间需要进行通信,以确保JavaScript代码的执行和网页内容的渲染能够协同工作。这种通信通常通过浏览器内部的API和事件机制实现。
  • JS引擎:JS引擎提供了DOM API,使得JavaScript代码可以访问和操作DOM树。当JavaScript代码需要对DOM树进行操作时(如添加、删除或修改节点),JS引擎会通过浏览器内部的API通知渲染引擎进行相应的处理。

image.png

4、多线程工作流程

(1) 网络线程负责加载网页资源

  • 网络线程的主要任务是从服务器加载网页资源,如HTML、CSS、JavaScript、图片等。加载过程中,网络线程会将HTML和CSS交给渲染线程进行解析,并将JavaScript交给JS引擎进行解析和执行。

(2)JS引擎解析JS脚本并且执行

  • JS引擎将接收到的JavaScript代码解析成抽象语法树(AST),然后通过JIT编译器将AST编译成机器代码,最后执行机器代码。在这个过程中,JS引擎还会进行优化,提高代码的执行效率。

(3)JS解析引擎空闲时,渲染线程立即工作

  • 当JS引擎空闲时(即没有JavaScript代码需要执行),渲染线程开始工作。渲染线程负责解析HTML和CSS,构建DOM树和渲染树,计算布局信息,并将渲染树转换为屏幕上的像素。如果在此过程中遇到JavaScript代码(如<script>标签或事件处理程序),渲染线程会暂停,将控制权交给JS引擎。

(4)用户交互、定时器操作等产生回调函数放入任务队列中

  • 当用户与网页进行交互(如点击、滚动等),或者有定时器触发(如setTimeoutsetInterval)时,相应的回调函数会被放入任务队列中。任务队列是一个先进先出(FIFO)的数据结构,用于存储待处理的任务。

(5)事件线程进行事件循环,将队列里的任务取出交给JS引擎执行

  • 事件线程负责管理事件循环。事件循环的主要任务是检查任务队列,将队列中的任务按顺序取出,并交给JS引擎执行。JS引擎执行任务时,可能会对DOM树进行操作,从而触发渲染线程的更新。事件循环会持续进行,直到任务队列为空,或者浏览器关闭。

image.png

三、Chrome运行原理

1、如何展示网页

  • 浏览器地址输入URL后发生了什么?

    • 输入解析:浏览器首先解析输入的内容,判断它是一个有效的URL还是搜索查询。如果输入的内容被识别为搜索查询,浏览器将使用默认的搜索引擎进行搜索。

    • DNS查询:如果输入的内容是一个有效的URL,浏览器将进行域名系统(DNS)查询以将域名解析为对应的IP地址。浏览器会首先检查本地DNS缓存,如果找不到对应的记录,它会向DNS服务器发送查询请求。

    • 建立连接:浏览器与目标服务器建立一个TCP连接。这通常包括三次握手过程,以确保双方都准备好进行数据传输。

    • 发送HTTP请求:TCP连接建立后,浏览器向服务器发送一个HTTP请求。请求通常包括请求方法(如GET或POST)、请求的资源路径、HTTP协议版本、请求头(包含诸如用户代理、接受的编码和语言等信息)以及可能的请求体(如POST请求所包含的表单数据)。

    • 接收响应:服务器处理HTTP请求,并将响应数据发送回浏览器。响应通常包括HTTP状态码(如200表示成功,404表示未找到等)、响应头(包含诸如内容类型、内容长度等信息)以及响应体(通常是HTML文档)。

    • 关闭或重用连接:一旦浏览器接收到完整的响应数据,它可以选择关闭TCP连接或将其保持在活动状态以用于后续请求。

    • 解析HTML:浏览器解析HTML文档,构建DOM树。在解析过程中,浏览器可能遇到<script>标签或其他需要立即执行的脚本,这时浏览器将暂停解析,执行脚本,然后继续解析。

    • 请求其他资源:HTML文档通常包含其他资源的引用,如CSS、JavaScript和图片等。浏览器将发送额外的HTTP请求来获取这些资源。这些请求可能与初始请求的服务器相同,也可能涉及其他服务器。

    • 构建渲染树:浏览器解析CSS样式,并将其应用于DOM树,生成渲染树。渲染树包含了所有可见元素及其样式信息。

    • 布局:浏览器计算渲染树中每个元素的位置和大小,生成布局信息。

    • 绘制:浏览器根据渲染树和布局信息将元素绘制到屏幕上。这个过程称为绘制或渲染。浏览器会将各个层的元素绘制到位图中,然后将这些位图合成到屏幕上显示的最终图像。

    • 交互:在完成页面绘制后,浏览器开始接收和处理用户的交互,如点击、滚动、输入等。这些交互可能会触发JavaScript事件处理程序,从而修改DOM或应用新的样式。这些修改可能会导致浏览器重新布局和绘制页面的部分或全部内容。

    • 关闭或卸载:当用户导航到其他页面或关闭浏览器选项卡时,浏览器将触发相应的页面卸载事件,如beforeunloadunload。这给开发者一个机会来执行清理操作,如保存用户数据或取消挂起的网络请求。一旦完成这些操作,浏览器将卸载页面并释放相关资源。

image.png

2、输入处理

  • 用户Url框输入内容的后,UI线程会判断输入的是一个URL地址呢,还是一个query查询条件
  • 如果是URL,直接请求站点资源
  • 如果是query,将输入发送给搜索引擎

image.png

3、开始导航

  • 当用户按下回车,UI线程通知网络线程发起一个网络请求,来获取站点内容
  • 请求过程中,tab处于loading(转圈等待)状态

image.png

4、读取响应

  • 网络线程接收到HTTP响应后,先检查响应头的媒体类型(MIME Type)
  • 如果响应主体是一个HTML文件,浏览器将内容交给渲染进程处理
  • 如果拿到的是其他类型文件,比如Zip、exe等,则交给下载管理器处理

image.png

5、寻找渲染进程

  • 网络线程做完所有检查后,会告知主进程数据已准备完毕,主进程确认后为这个站点寻找一个渲染进程
  • 主进程通过IPC消息告知渲染进程去处理本次导航
  • 渲染进程开始接收数据并告知主进程自己已开始处理,导航结束,进入文档加载阶段

image.png

6、资源加载

  • 收到主进程的消息后,开始加载HTML文档
  • 除此之外,还需要加载子资源,比如一些图片,CSS样式文件以及JavaScript脚本

image.png

7、构建渲染树

  • 构建DOM树,将HTML文本转化成浏览器能够理解的结构
  • 构建CSSOM树,浏览器同样不认识CSS,需要将CSS代码转化为可理解的CSSOM
  • 构建渲染树,渲染树是DOM树和CSSOM树的结合

image.png

8、页面布局

  • 根据渲染树计算每个节点的位置和大小
  • 在浏览器页面区域绘制元素边框
  • 遍历渲染树,将元素以盒模型的形式写入文档流

image.png

9、页面绘制

  • 构建图层:为特定的节点生成专用图层
  • 绘制图层:一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程(减少绘制次数)
  • 合成线程接收指令生成图块
  • 栅格线程将图块进行栅格化
  • 展示在屏幕上

image.png

10、前端性能performance

  • 时间都花在哪?什么情况下卡顿?

    卡顿一般是由于浏览器在渲染页面时遇到了阻塞或耗时操作导致的。以下是一些可能导致卡顿的情况:

  • JavaScript执行时间过长:JavaScript的执行会阻塞页面的渲染。如果脚本执行时间过长,会导致页面无响应或卡顿现象。

  • 大量的DOM操作:DOM操作会影响页面的渲染,特别是在大量DOM操作时。如果需要频繁地更新DOM元素,可以考虑使用虚拟DOM等技术来减少DOM操作次数。

  • 大量的网络请求:浏览器在渲染页面时需要下载和解析HTML、CSS、JavaScript和图像等资源。如果有大量的资源需要下载,会导致页面加载时间过长。

  • 大量的样式和布局计算:如果页面包含大量的样式和布局计算,会影响页面的渲染性能。

  • 阻塞渲染的JavaScript:如果脚本阻塞了页面的渲染,就会导致卡顿或页面无响应。

image.png

11、首屏优化

(1)压缩、分包、删除无用代码

  • 通过压缩代码、分包加载和删除无用代码等技术,可以减小页面的体积,加快页面的加载速度。

(2)静态资源分离

  • 将页面中的静态资源(如CSS、JavaScript和图像等)与HTML文档分离,可以使得浏览器可以并行加载这些资源,从而提高页面的加载速度。

(3)JS脚本非阻塞加载

  • 将JS脚本异步加载,可以减少页面的渲染阻塞,从而提高页面的加载速度。可以使用deferasync等属性来实现JS的非阻塞加载。

(4)缓存策略

  • 合理地设置缓存策略,可以减少对服务器的请求,加快页面的加载速度。可以使用HTTP响应头中的Cache-ControlExpires等属性来设置缓存策略。

(5)SSR

  • 服务器端渲染(Server Side Rendering)可以在服务器端生成HTML文档,减少客户端渲染的工作量,从而提高页面的加载速度。SSR适用于复杂的单页面应用或对SEO有要求的应用。

(6)预置loading、骨架屏

  • 在页面加载过程中,可以预置一个loading动画或骨架屏,以提高用户体验。这些技术可以在页面加载完成之前,先显示一些占位元素,给用户一个等待的感觉,从而减少用户等待的焦虑和不安。

12、渲染优化

(1) GPU加速

  • 将复杂的图形处理任务交给GPU来处理,可以加快页面的渲染速度。可以使用CSS3的transformopacity等属性来开启GPU加速。

(2)减少回流、重绘

  • 回流和重绘是影响页面性能的主要因素之一。可以通过避免使用影响布局的属性、批量修改DOM元素等技术来减少回流和重绘操作。

(3)离屏渲染

  • 离屏渲染是将页面中的部分内容在单独的图层中进行渲染,从而减少对主渲染线程的阻塞。可以使用CSS3的transformposition等属性来开启离屏渲染。

(4)懒加载

  • 将页面中的非必要资源(如图片和视频等)延迟加载,可以加快页面的加载速度。可以使用Intersection ObserverLazyload等技术来实现懒加载。

13、JS优化

(1)防止内存泄漏

有可能出现内存泄漏的场景:

  • 全局变量:全局变量会一直存在于内存中,直到程序结束才会被释放。如果程序中定义了大量的全局变量,就会导致内存占用过多,从而导致内存泄漏。

  • 闭包:闭包会在函数中保存局部变量和参数,如果函数执行后,闭包中的变量没有被释放,就会导致内存泄漏。为了避免内存泄漏,应该合理使用闭包,并注意释放不需要的变量。

  • 循环引用:循环引用是指两个或多个对象之间相互引用,形成了一个死循环,导致内存无法释放。为了避免循环引用,应该及时释放不需要的引用,并使用垃圾回收机制来自动释放内存。

  • 定时器和事件监听器:定时器和事件监听器会持续占用内存,直到被清除或被解除绑定。如果程序中存在大量的定时器和事件监听器,就会导致内存占用过多,从而导致内存泄漏。

  • DOM节点:DOM节点也会占用内存空间,如果程序中存在大量的DOM节点,就会导致内存占用过多,从而导致内存泄漏。为了避免内存泄漏,应该及时清除不需要的DOM节点。

  • 内存泄漏会导致不必要的内存占用和程序崩溃。可以使用letconst关键字声明变量,避免变量污染和内存泄漏。

(2)循环尽早break

  • 在循环中,如果已经找到了需要的结果,可以使用break语句尽早结束循环,避免无用的迭代和计算。

(3)合理使用闭包

  • 闭包可以在函数中保存局部变量和参数,避免全局变量的污染和泄漏。但是,如果使用不当,也会导致内存泄漏和性能下降。

(4)减少Dom访问

  • DOM操作是JavaScript性能的一个瓶颈。可以使用缓存和批量操作等技术来减少DOM访问次数,从而提高JavaScript的性能。

(5)防抖、节流

  • 防抖和节流是用来控制函数调用频率的技术。可以使用setTimeoutrequestAnimationFrame等API来实现防抖和节流(或者用第三方库也行)。

(6)Web Workers

  • Web Workers是一种在后台线程中执行JavaScript代码的技术。可以将耗时的计算任务和数据处理等操作放到Web Workers中执行,避免阻塞主线程,提高页面的响应速度。

四、跨端容器

1、为什么需要跨端

  • 开发成本、效率

    • 跨端开发可以帮助降低成本和提高开发效率。使用跨端技术,开发者只需编写一份代码,就可以在多个平台(如iOS、Android和Web)上运行。这可以减少开发和维护的工作量,节省时间和资源。同时,开发团队可以更快地推出新功能和修复问题,因为他们只需关注一份代码库。
  • 一致性体验

    • 跨端开发可以确保在不同平台上提供一致的用户体验。使用跨端技术,开发者可以更容易地保持应用的外观和功能一致,无论用户在什么设备上使用。这有助于提高用户满意度和用户留存率。
  • 前端开发生态

    • 跨端开发受益于强大的前端生态系统。许多流行的前端框架和库,如React Native、Flutter和Ionic,都支持跨端开发。这些工具为开发者提供了丰富的资源和丰富的社区支持,帮助他们更轻松地实现跨端功能。

image.png

2、跨端方案

  • webview
  • 小程序
  • RN/WeeX
  • Lynx
  • Flutter

image.png

3、跨端容器 - WebView

跨端容器中的WebView是一种在移动应用中嵌入网页内容的组件。WebView允许开发者将HTML、CSS和JavaScript等Web技术直接嵌入到移动应用中,从而实现跨平台的应用开发。通过WebView,可以在原生应用中展示网页内容,同时为开发者提供了一些与原生功能交互的能力。

  • Webview,即网页视图,用于加载网页Url,并展示其内容的控件
  • 可以内嵌在移动端App内,实现前端混合开发,大多数混合框架都是基于Webview的二次开发;比如lonic、Cordova

4、跨端容器 - 常用WebView分类

  • 常用webview、Android,IOS,国产Android

image.png

5、跨端容器 -WebView使用原生能力

  • avascript调用Native

    • API注入:Native获取]avascript3环境上下文,对其挂载的对象或者方法进行拦截
    • 使用Webview URL Scheme跳转拦截
    • IOS上window.webkit.messageHandler直接通信
  • Native调用Javascript

    • 直接通过webview暴露的API执行JS代码

    • IOS webview.stringByEvaluatingJavaScriptFromString

    • Android webview.evaluateJavascript

6、跨端容器 - WebView< - >Nactive 通信**

  • JS环境中提供通信的 JSBridge
  • Native端提供 SDK 响应 JSBridge 发出的调用
  • 0前端和客户端分别实现对应功能模块

image.png

7、跨端容器 - 实现一个简易JSBridge

  • JSBridge是一种在WebView中实现原生与JavaScript之间通信的技术。

image.png

8、跨端容器 - 小程序

  • 微信、支付宝、百度小程序、小米直达号
  • 渲染层-webview
  • 双线程,多webview架构
  • 数据通信,Native转发

image.png

9、跨端容器 - React Native/WeeX

  • 原生组件渲染
  • React/Vue框架
  • virtual dom
  • JSBridge

image.png

10、跨端容器 - Lynx

  • 基于Vue框架

    • Lynx采用Vue.js作为其基础框架,允许开发者使用Vue.js编写应用。Vue.js是一种渐进式JavaScript框架,它使开发者能够轻松地构建可扩展、高性能的应用。基于Vue.js的设计原则,Lynx可以提供简洁、模块化的代码结构,以及良好的开发体验。
  • 绑定于JS Core / V8

    • Lynx选择JS Core(iOS平台)或V8(Android平台)作为其JavaScript引擎。这意味着Lynx应用在运行时,JavaScript代码将在高性能的JavaScript引擎中执行。通过使用这些优秀的JavaScript引擎,Lynx能够确保应用在不同平台上具有稳定、高性能的运行表现。
  • JSBinding

    • Lynx使用JSBinding技术实现JavaScript与原生代码之间的通信。这种技术允许JavaScript直接调用原生方法,并使原生代码能够执行JavaScript回调。通过JSBinding,Lynx实现了高效的原生与JavaScript之间的通信,降低了性能损失。
  • Native UI /Skia

    • Lynx使用Native UI组件和Skia作为其渲染引擎。Native UI组件意味着Lynx应用在运行时,界面将使用原生组件进行渲染。这可以确保应用具有接近原生应用的性能和用户体验。同时,Lynx还采用了Skia图形库,它是一种高性能的2D图形渲染引擎,用于绘制图形和文本。Skia使Lynx应用在渲染复杂界面时能够保持流畅的帧率和高质量的视觉效果。

image.png

11、跨端容器 - Flutter

  • wideget

    • 在Flutter中,所有UI元素都被称为Widget。Widget是Flutter应用的基本构建块,它们可以嵌套、组合以及自定义,从而创建复杂的用户界面。Flutter提供了丰富的预制Widget,如文本、按钮、列表等,开发者可以直接使用这些Widget,也可以通过组合和扩展它们来构建自定义的Widget。
  • dart vm

    • Flutter使用Dart语言进行开发,Dart是一种强类型、面向对象的编程语言,它既可以编译成JavaScript代码(用于Web应用),也可以编译成机器码(用于移动应用)
    • 在移动端,Flutter应用运行在Dart VM(虚拟机)中。Dart VM提供了即时编译(JIT)和预编译(AOT)两种编译方式。在开发过程中,Dart VM采用即时编译,这使得Flutter具有热重载功能,开发者可以在不重新编译整个应用的情况下查看代码更改的效果。在发布应用时,Dart VM会采用预编译,将Dart代码编译成高效的机器码,以提高应用的性能。
  • skia图形库

    • Flutter使用Skia图形库进行UI渲染。Skia是一种高性能的2D图形渲染引擎,用于绘制图形和文本。由于Flutter直接使用Skia进行渲染,它无需依赖于原生UI组件,可以实现统一的跨平台UI渲染。这使得Flutter应用具有高度的可定制性,同时还保持了流畅的性能和优美的视觉效果。

image.png

12、跨端容器 - 通用原理

  • UI组件
  • 渲染引擎
  • 逻辑控制引擎
  • 通信桥梁
  • 底层API抹平表面差异

image.png

13、跨端方案对比

image.png

五、课程总结

image.png