客户端容器-web浏览器以及跨端方案|青训营笔记
浏览器架构
浏览器架构演进
0. 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等 复制代码
- 在早期的浏览器中,所有的功能都在一个进程中完成,包括用户界面、JavaScript执行、CSS渲染和网络请求等。单进程架构的优势是简单、易于实现,但缺点也很明显,如:不稳定(一个标签页崩溃可能导致整个浏览器崩溃)、不安全(攻击者可以利用漏洞来控制整个浏览器)以及性能瓶颈(多个标签页争抢资源,导致性能下降)。
1. 多进程架构:主进程、网络进程、渲染进程、GPU进程、插件进程 复制代码
- 为了解决单进程架构的问题,浏览器厂商开始将浏览器的功能拆分为多个进程。例如,谷歌推出的Chrome浏览器就采用了多进程架构。在这种架构下,浏览器将每个标签页(或每个渲染实例)分配给一个单独的进程,从而实现了进程间的隔离,防止单个网页崩溃影响到其他界面。多进程架构提高了浏览器的稳定性、安全性和性能,但同时也增加了内存消耗
- 每一个Tab运行在独立的沙盒里面
面向服务架构:将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础服务
- 面向服务架构 => 多进程架构升级版本
- 随着Web平台的发展,浏览器需要处理越来越多的任务,这使得多进程架构变得复杂且难以维护。面向服务架构的出现旨在将浏览器的各个功能模块化,将它们作为独立的服务来运行和维护。在这种架构下,每个服务都可以独立地使用一个或多个进程,根据需求自动扩展或缩减资源。这样可以进一步提高浏览器的稳定性、安全性和性能,同时优化资源利用率
浏览器架构 - 任务管理器
在多进程浏览器架构中,任务管理器是一个非常重要的组件。任务管理器负责监控、管理和分配浏览器的各个进程,确保整个浏览器系统的稳定性和性能。
任务管理器主要负责以下几方面的功能:
- 进程监控:任务管理器可以实时查看浏览器中的各个进程,包括标签页进程、插件进程和扩展进程等。它可以展示各个进程的资源使用情况,如内存占用、CPU使用率和网络活动等。
- 进程管理:任务管理器负责启动和关闭浏览器的进程。当用户打开一个新的标签页或启动一个插件时,任务管理器会创建相应的进程。当标签页被关闭或插件停止运行时,任务管理器会结束相应的进程,以释放系统资源。
- 资源分配:任务管理器根据各个进程的资源需求,动态地分配系统资源。例如,对于需要大量CPU资源的进程,任务管理器会分配更多的CPU时间片。同时,任务管理器还可以确保关键进程(如用户界面进程)始终获得足够的资源,从而保证整个浏览器的流畅运行。
- 进程隔离:为了提高浏览器的安全性,任务管理器会对各个进程进行隔离。这意味着一个进程无法直接访问另一个进程的内存空间。进程隔离可以防止潜在的安全漏洞影响到整个浏览器系统。
- 异常处理:任务管理器负责检测和处理进程中的异常情况。当某个进程发生崩溃或无响应时,任务管理器会尝试自动恢复该进程。如果无法恢复,任务管理器会提示用户关闭相应的标签页或插件,以防止整个浏览器崩溃。
通过任务管理器,浏览器可以有效地监控和管理其各个进程,确保整个系统的稳定性、性能和安全性。不同浏览器的任务管理器实现可能会有所差异,但它们的核心功能和目标都是类似的。在谷歌Chrome浏览器中,用户可以通过按下
Shift + Esc快捷键或者在菜单中选择“更多工具” > “任务管理器”来打开任务管理器。
浏览器架构 - 多进程分工
渲染进程
常见浏览器内核
渲染进程 - 多线程架构
在这里我们涉及到了(多)线程,前面涉及到了(多)进程。这里补充额外知识来说明两者:
进程和线程都是操作系统进行任务管理的基本单位,它们之间有一定的关联性,但也有明显的区别
- 进程:
进程是一个独立的程序执行实例,它包括程序的代码、数据以及运行时所需的系统资源(如文件描述符、内存空间等)。每个进程都有自己的独立地址空间,操作系统会为每个进程分配和管理资源。
进程的特点:
- 独立性:进程具有独立的地址空间,一个进程无法直接访问另一个进程的资源,除非通过进程间通信(IPC)机制。
- 隔离性:操作系统会为每个进程分配和管理资源,确保它们之间的隔离。这有助于提高系统的稳定性和安全性。
- 资源开销:创建和维护进程需要消耗一定的系统资源,如内存、CPU时间等。
应用场景:进程通常用于执行独立的、需要隔离的任务。例如,在多进程浏览器中,每个Tab标签页都运行在一个单独的进程中,以实现进程间的隔离和资源管理。
- 线程:
线程是进程内的一个执行单元,它共享进程的地址空间和资源,但拥有自己的执行堆栈和程序计数器。一个进程可以包含多个线程,它们可以并发地执行任务。
线程的特点:
- 轻量级:线程比进程更轻量级,创建和切换线程的开销较小。
- 共享资源:线程之间可以共享进程的地址空间和资源,这使得线程间的通信和数据共享变得更加简单高效。
- 上下文切换:线程之间的上下文切换成本较低,因为它们共享相同的地址空间。
应用场景:线程通常用于实现并发执行和任务分解。例如,在一个图形编辑器中,可以使用多个线程分别处理用户输入、图形渲染和文件保存等任务,从而提高程序的响应速度和执行效率。
总结:进程是一个独立的程序执行实例,具有独立的地址空间和资源;线程是进程内的一个执行单元,共享进程的地址空间和资源。进程通常用于执行独立的、需要隔离的任务,而线程则用于实现并发执行和任务分解
- 内部是多线程实现,主要负责页面渲染,脚本执行,事件处理,网络请求等
JS引擎 VS 渲染引擎
JS引擎和渲染引擎是浏览器中两个非常重要的组件。它们分别负责处理JavaScript代码和渲染网页内容
-
解析执行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引擎执行完毕,渲染引擎会继续执行后续的渲染任务。
-
XML解析生成渲染树,显示在屏幕
- JS引擎:JS引擎主要处理JavaScript代码,不负责解析XML或生成渲染树。然而,JavaScript可以通过DOM API操作DOM树,从而间接地影响渲染树的生成和更新。
- 渲染引擎:渲染引擎负责解析HTML和XML文档,构建DOM树。然后,渲染引擎会解析CSS样式,并将样式信息附加到DOM树上,生成渲染树。接下来,渲染引擎会计算每个节点的布局信息(如位置和大小),并将渲染树转换为屏幕上的像素。这个过程被称为“绘制”或“渲染”。
-
桥接方式通信
- JS引擎和渲染引擎之间需要进行通信,以确保JavaScript代码的执行和网页内容的渲染能够协同工作。这种通信通常通过浏览器内部的API和事件机制实现。
- JS引擎:JS引擎提供了DOM API,使得JavaScript代码可以访问和操作DOM树。当JavaScript代码需要对DOM树进行操作时(如添加、删除或修改节点),JS引擎会通过浏览器内部的API通知渲染引擎进行相应的
渲染进程 - 多线程工作流程
-
网络线程负责加载网页资源
- 网络线程的主要任务是从服务器加载网页资源,如HTML、CSS、JavaScript、图片等。加载过程中,网络线程会将HTML和CSS交给渲染线程进行解析,并将JavaScript交给JS引擎进行解析和执行
-
JS引擎解析JS脚本并且执行
- JS引擎将接收到的JavaScript代码解析成抽象语法树(AST),然后通过JIT编译器将AST编译成机器代码,最后执行机器代码。在这个过程中,JS引擎还会进行优化,提高代码的执行效率
-
JS解析引擎空闲时,渲染线程立即工作
- 当JS引擎空闲时(即没有JavaScript代码需要执行),渲染线程开始工作。渲染线程负责解析HTML和CSS,构建DOM树和渲染树,计算布局信息,并将渲染树转换为屏幕上的像素。如果在此过程中遇到JavaScript代码(如
<script>标签或事件处理程序),渲染线程会暂停,将控制权交给JS引擎
- 当JS引擎空闲时(即没有JavaScript代码需要执行),渲染线程开始工作。渲染线程负责解析HTML和CSS,构建DOM树和渲染树,计算布局信息,并将渲染树转换为屏幕上的像素。如果在此过程中遇到JavaScript代码(如
-
用户交互、定时器操作等产生回调函数放入任务队列中
- 当用户与网页进行交互(如点击、滚动等),或者有定时器触发(如
setTimeout或setInterval)时,相应的回调函数会被放入任务队列中。任务队列是一个先进先出(FIFO)的数据结构,用于存储待处理的任务
- 当用户与网页进行交互(如点击、滚动等),或者有定时器触发(如
-
事件线程进行事件循环,将队列里的任务取出交给JS引擎执行
- 事件线程负责管理事件循环。事件循环的主要任务是检查任务队列,将队列中的任务按顺序取出,并交给JS引擎执行。JS引擎执行任务时,可能会对DOM树进行操作,从而触发渲染线程的更新。事件循环会持续进行,直到任务队列为空,或者浏览器关闭。
Chrome运行原理
Chrome运行原理 - 如何展示网页
- 浏览器地址输入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或应用新的样式。这些修改可能会导致浏览器重新布局和绘制页面的部分或全部内容。
- 关闭或卸载:当用户导航到其他页面或关闭浏览器选项卡时,浏览器将触发相应的页面卸载事件,如
beforeunload和unload。这给开发者一个机会来执行清理操作,如保存用户数据或取消挂起的网络请求。一旦完成这些操作,浏览器将卸载页面并释放相关资源。总结:当您在浏览器地址栏输入URL后,浏览器会经历一系列的操作,从DNS查询和TCP连接建立,到HTTP请求、响应处理、HTML解析、资源加载、渲染树构建、布局、绘制以及用户交互等。这些操作共同构成了从输入URL到显示完整网页的整个过程
输入处理
- 用户Url框输入内容的后,UI线程会判断输入的是一个URL地址呢,还是一个query查询条件
- 如果是URL,直接请求站点资源
- 如果是query,将输入发送给搜索引擎
开始导航
- 当用户按下回车,UI线程通知网络线程发起一个网络请求,来获取站点内容
- 请求过程中,tab处于loading(转圈等待)状态
读取响应
- 网络线程接收到HTTP响应后,先检查响应头的媒体类型(MIME Type)
- 如果响应主体是一个HTML文件,浏览器将内容交给渲染进程处理
- 如果拿到的是其他类型文件,比如Zip、exe等,则交给下载管理器处理
寻找渲染进程
- 网络线程做完所有检查后,会告知主进程数据已准备完毕,主进程确认后为这个站点寻找一个渲染进程
- 主进程通过IPC消息告知渲染进程去处理本次导航
- 渲染进程开始接收数据并告知主进程自己已开始处理,导航结束,进入文档加载阶段
渲染进程 - 资源加载
- 收到主进程的消息后,开始加载HTML文档
- 除此之外,还需要加载子资源,比如一些图片,CSS样式文件以及JavaScript脚本
渲染进程 - 构建渲染树
- 构建DOM树,将HTML文本转化成浏览器能够理解的结构
- 构建CSSOM树,浏览器同样不认识CSS,需要将CSS代码转化为可理解的CSSOM
- 构建渲染树,渲染树是DOM树和CSSOM树的结合
渲染进程 - 页面布局
- 根据渲染树计算每个节点的位置和大小
- 在浏览器页面区域绘制元素边框
- 遍历渲染树,将元素以盒模型的形式写入文档流
渲染进程 - 页面绘制
- 构建图层:为特定的节点生成专用图层
- 绘制图层:一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程(减少绘制次数)
- 合成线程接收指令生成图块
- 栅格线程将图块进行栅格化
- 展示在屏幕上
前端性能performance
performance对象中常用的属性和方法:
navigationStart:浏览器开始导航到当前页面的时间戳。
fetchStart:浏览器开始检索文档的时间戳。
responseStart:浏览器收到第一个字节的时间戳。
domLoading:浏览器开始解析文档的时间戳。
domInteractive:浏览器完成解析文档的时间戳。
domContentLoadedEventStart:文档DOMContentLoaded事件开始的时间戳。
domComplete:文档解析完成的时间戳。
loadEventStart:文档加载事件开始的时间戳。
loadEventEnd:文档加载事件结束的时间戳。
- 最常用的是
navigationStart和loadEventEnd。navigationStart表示浏览器开始导航到当前页面的时间戳,loadEventEnd表示文档加载事件结束的时间戳。这两个时间戳之差就是页面的加载时间
0.### 屏优化
- 压缩、分包、删除无用代码
- 通过压缩代码、分包加载和删除无用代码等技术,可以减小页面的体积,加快页面的加载速度。
- 静态资源分离
- 将页面中的静态资源(如CSS、JavaScript和图像等)与HTML文档分离,可以使得浏览器可以并行加载这些资源,从而提高页面的加载速度
- JS脚本非阻塞加载
- 将JS脚本异步加载,可以减少页面的渲染阻塞,从而提高页面的加载速度。可以使用
defer和async等属性来实现JS的非阻塞加载
- 缓存策略
- 合理地设置缓存策略,可以减少对服务器的请求,加快页面的加载速度。可以使用HTTP响应头中的
Cache-Control和Expires等属性来设置缓存策略
- SSR
- 服务器端渲染(Server Side Rendering)可以在服务器端生成HTML文档,减少客户端渲染的工作量,从而提高页面的加载速度。SSR适用于复杂的单页面应用或对SEO有要求的应用
- 预置loading、骨架屏
- 在页面加载过程中,可以预置一个loading动画或骨架屏,以提高用户体验。这些技术可以在页面加载完成之前,先显示一些占位元素,给用户一个等待的感觉,从而减少用户等待的焦虑和不安
综合利用这些技术,可以大大提高页面的加载速度和用户体验。在进行首屏优化时,需要综合考虑页面的性能、体验和功能等方面,找到最合适的优化方案
渲染优化
- GPU加速
- 将复杂的图形处理任务交给GPU来处理,可以加快页面的渲染速度。可以使用CSS3的
transform和opacity等属性来开启GPU加速
- 减少回流、重绘
- 回流和重绘是影响页面性能的主要因素之一。可以通过避免使用影响布局的属性、批量修改DOM元素等技术来减少回流和重绘操作
- 离屏渲染
- 离屏渲染是将页面中的部分内容在单独的图层中进行渲染,从而减少对主渲染线程的阻塞。可以使用CSS3的
transform和position等属性来开启离屏渲染。
- 懒加载
- 将页面中的非必要资源(如图片和视频等)延迟加载,可以加快页面的加载速度。可以使用
Intersection Observer和Lazyload等技术来实现懒加载
JS优化
-
防止内存泄漏
-
有可能出现内存泄漏的场景
- 全局变量:全局变量会一直存在于内存中,直到程序结束才会被释放。如果程序中定义了大量的全局变量,就会导致内存占用过多,从而导致内存泄漏。
- 闭包:闭包会在函数中保存局部变量和参数,如果函数执行后,闭包中的变量没有被释放,就会导致内存泄漏。为了避免内存泄漏,应该合理使用闭包,并注意释放不需要的变量。
- 循环引用:循环引用是指两个或多个对象之间相互引用,形成了一个死循环,导致内存无法释放。为了避免循环引用,应该及时释放不需要的引用,并使用垃圾回收机制来自动释放内存。
- 定时器和事件监听器:定时器和事件监听器会持续占用内存,直到被清除或被解除绑定。如果程序中存在大量的定时器和事件监听器,就会导致内存占用过多,从而导致内存泄漏。
- DOM节点:DOM节点也会占用内存空间,如果程序中存在大量的DOM节点,就会导致内存占用过多,从而导致内存泄漏。为了避免内存泄漏,应该及时清除不需要的DOM节点
-
内存泄漏会导致不必要的内存占用和程序崩溃。可以使用
let和const关键字声明变量,避免变量污染和内存泄漏
-
-
循环尽早break
- 在循环中,如果已经找到了需要的结果,可以使用
break语句尽早结束循环,避免无用的迭代和计算
- 在循环中,如果已经找到了需要的结果,可以使用
-
合理使用闭包
- 闭包可以在函数中保存局部变量和参数,避免全局变量的污染和泄漏。但是,如果使用不当,也会导致内存泄漏和性能下降
-
减少Dom访问
- DOM操作是JavaScript性能的一个瓶颈。可以使用缓存和批量操作等技术来减少DOM访问次数,从而提高JavaScript的性能
-
防抖、节流
- 防抖(debounce): 防抖是指在一定时间内,如果连续触发事件,那么只执行一次目标函数,理解为王者荣耀的回城。常用于输入框实时搜索、窗口大小调整等场景。防抖的实现原理是:设置一个定时器,在指定的延迟时间内,如果再次触发事件,则重新计时。只有在延迟时间内没有再次触发事件时,才会执行目标函数。
- 节流(throttle): 节流是指在一定时间内,无论触发多少次事件,目标函数都只执行一次,理解为技能CD。常用于滚动事件、鼠标移动等场景。节流的实现原理是:设置一个间隔时间,在这个时间内,无论事件触发多少次,都只执行一次目标函数。一旦超过这个间隔时间,就会再次执行目标函数。
总结: 防抖和节流的主要区别在于,防抖是在一定时间内只执行一次目标函数,而节流是在一定时间内控制目标函数执行次数。它们都可以有效地减少函数执行频率,降低性能开销,提高用户体验。
- 防抖和节流是用来控制函数调用频率的技术。可以使用
setTimeout和requestAnimationFrame等API来实现防抖和节流(或者用第三方库也行)
-
Web Workers
- Web Workers是一种在后台线程中执行JavaScript代码的技术。可以将耗时的计算任务和数据处理等操作放到Web Workers中执行,避免阻塞主线程,提高页面的响应速度
跨端容器
跨端方案
- webview
- 小程序
- RN/WeeX
- Lynx
- Flutter
跨端容器 - WebView
跨端容器中的WebView是一种在移动应用中嵌入网页内容的组件。WebView允许开发者将HTML、CSS和JavaScript等Web技术直接嵌入到移动应用中,从而实现跨平台的应用开发。通过WebView,可以在原生应用中展示网页内容,同时为开发者提供了一些与原生功能交互的能力
WebView的主要优点包括:
- 跨平台兼容性:使用WebView,开发者可以利用Web技术为多个平台(如iOS和Android)创建应用。这可以节省开发时间和成本,一次开发处处使用,学习成本低,同时确保应用在各个平台上提供一致的用户体验。
- 代码重用:WebView允许开发者在移动应用中重用现有的Web代码。这意味着,对于已有Web应用的公司来说,可以更容易地将其产品扩展到移动平台。
- 简化更新过程:随时发布,即时更新,不用下载安装包。WebView使得应用内容的更新变得更加简单。因为WebView直接加载Web内容,开发者可以在服务器端更新应用内容,而无需重新提交整个应用到应用商店进行审核。
- 通过JSBridge和原生系统交互,能够实现一些更加复杂的功能
然而,WebView也有一些局限性:
- 性能:相比于原生应用,基于WebView的应用性能可能较差。这主要是因为WebView需要加载和运行Web内容,这会消耗更多的系统资源。在某些情况下,这可能导致较慢的加载速度和不流畅的用户体验。
- 原生功能访问限制:虽然WebView提供了一些与原生功能交互的能力,但它仍然受到一定的限制。为了实现某些特定的原生功能,开发者可能需要编写额外的平台特定代码。
- 用户体验差异:尽管WebView可以确保跨平台的一致性,但它可能无法完全符合不同平台的设计规范。因此,在某些情况下,基于WebView的应用可能无法提供与原生应用相同水平的用户体验。
0. Webview,即网页视图,用于加载网页Url,并展示其内容的控件
1. 可以内嵌在移动端App内,实现前端混合开发,大多数混合框架都是基于Webview的二次开发;比如lonic、Cordova
复制代码
跨端容器 - 常用WebView分类
- 常用webview、Android,IOS,国产Android
- WebView(Android):这是Android平台的原生WebView组件,用于在Android应用中加载并显示Web内容。根据Android版本和设备制造商的不同,WebView的表现可能会有所差异。这可能导致一些兼容性和性能问题。
- X5 WebView(腾讯X5内核):这是由腾讯公司推出的一种WebView解决方案,用于解决Android系统上WebView的碎片化问题。X5内核基于腾讯QQ浏览器的内核,提供了更稳定、更高性能的WebView组件。它可以在各种Android设备和系统版本上提供一致的表现,减少兼容性问题。
- UIWebView(iOS,已弃用):这是iOS平台的原生WebView组件,用于在iOS应用中加载并显示Web内容。UIWebView自iOS 2.0开始引入,但在iOS 8.0中被WKWebView取代。自iOS 12.0以来,UIWebView已被官方弃用,不再推荐使用。
- WKWebView(iOS):这是iOS平台上的新一代WebView组件,取代了已弃用的UIWebView。WKWebView自iOS 8.0开始引入,它具有更好的性能和更丰富的功能,如支持多进程、JavaScript性能改进等。苹果公司推荐开发者使用WKWebView来在iOS应用中加载并显示Web内容。
跨端容器 -WebView使用原生能力
Javascript调用Native
- API注入:Native获取]avascript3环境上下文,对其挂载的对象或者方法进行拦截
- 使用Webview URL Scheme跳转拦截
- IOS上window.webkit.messageHandler直接通信
Native调用Javascript
- 直接通过webview暴露的API执行JS代码
- IOS webview.stringByEvaluatingJavaScriptFromString
- Android webview.evaluateJavascript
跨端容器 - WebView< - >Nactive 通信
- JS环境中提供通信的 JSBridge
- Native端提供 SDK 响应 JSBridge 发出的调用
- 前端和客户端分别实现对应功能模块
跨端容器 - 实现一个简易JSBridge
`> JSBridge是一种在WebView中实现原生与JavaScript之间通信的技术 // 创建一个空对象,用于存储回调函数 const callbacks = {};// 生成唯一ID的函数,用于标识每个回调函数 function generateUniqueId() { return 'callback_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6); }// 调用原生方法的函数 function callNative(action, params, callback) { // 生成唯一ID const callbackId = generateUniqueId();
// 将回调函数存储到callbacks对象中 callbacks[callbackId] = callback; // 创建一个包含操作、参数和回调ID的对象,用于发送给原生端 const message = { action, params, callbackId, }; // 将对象转换为JSON字符串 const messageStr = JSON.stringify(message); // 调用原生方法(具体实现取决于平台和环境) // 在Android中,通常会使用JavaScriptInterface // 在iOS中,通常会使用window.webkit.messageHandlers..postMessage window.AndroidBridge.postMessage(messageStr); }// 原生调用JS的函数 function nativeCallback(callbackId, result) { // 从callbacks对象中获取对应的回调函数 const callback = callbacks[callbackId]; // 判断回调函数是否存在 if (callback) { // 调用回调函数并传入结果参数 callback(result); // 从callbacks对象中删除已经调用过的回调函数 delete callbacks[callbackId]; } }// 将方法暴露给全局对象,以便原生代码调用 window.JSBridge = { callNative, nativeCallback, };//代码块内容由ChatGPT4给出 `
跨端容器 - 小程序
- 微信、支付宝、百度小程序、小米直达号
- 渲染层-webview
- 双线程,多webview架构
- 数据通信,Native转发
跨端容器 - React Native/WeeX
- 原生组件渲染
- React/Vue框架
- virtual dom
- JSBridge
跨端容器 - 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应用在渲染复杂界面时能够保持流畅的帧率和高质量的视觉效果
跨端容器 - Flutter
Flutter是Google开发的一个开源UI工具包,旨在为开发者提供一种构建优美、高性能的跨平台应用的解决方案。Flutter具有一些独特的特点,包括基于Widget的设计、Dart VM以及使用Skia图形库
-
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应用具有高度的可定制性,同时还保持了流畅的性能和优美的视觉效果。
跨端容器 - 通用原理
- UI组件
- 渲染引擎
- 逻辑控制引擎
- 通信桥梁
- 底层API抹平表面差异
跨端思考
- 同样是基于webview渲染,为什么小程序体验比webview流畅?
这主要归功于小程序在架构和优化方面的一些特点。以下是一些关键因素:
- 优化的渲染引擎: 小程序的渲染引擎经过针对性优化,以适应移动设备的性能特点。这使得小程序在处理动画、滚动等界面交互时更加流畅。此外,小程序还采用了一些高效的Web渲染技术,如硬件加速、层合成等,从而提高了渲染性能。
- 分离的逻辑层和渲染层: 小程序将逻辑层(JavaScript代码)和渲染层(UI界面)分开处理。逻辑层运行在Web容器中,而渲染层则运行在一个独立的原生视图中。通过这种分离,小程序可以在不影响UI渲染的情况下执行JavaScript代码,从而提高了应用的流畅度。
- 限制和优化的API: 小程序提供了一组限制和优化的API,这些API专门针对移动设备的性能和用户体验进行了调整。通过使用这些API,开发者可以更高效地编写适用于移动设备的应用,从而提高应用的流畅度。
- 资源限制: 小程序对资源的大小和数量有严格的限制,这迫使开发者在设计应用时充分考虑性能优化。较小的资源包意味着更快的加载速度,从而提高了应用的启动速度和运行性能。
- 预加载和缓存机制: 小程序具有预加载和缓存机制,这使得应用在首次启动时可以快速加载和渲染,从而提高了用户体验。此外,缓存机制还可以减少网络请求和数据传输,降低应用的延迟。
总之,虽然小程序和基于WebView的应用都是通过WebView渲染,但小程序在渲染引擎、架构设计、API、资源限制以及预加载和缓存等方面进行了针对性优化,这些优化使得小程序的体验比WebView应用更流畅。