字节青训营-客户端容器

255 阅读13分钟

一、浏览器架构

(一)浏览器架构演进

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

多进程架构:主进程、网络进程、GUI进程、渲染进程、插件进程。

面向服务架构:将原来的UI,数据库,文件、设备、网络等。作为一个独立的基础服务。

image.png

浏览器是多进程的,常见的进程有browser进程(主进程,用来管理浏览器,协调主控以及页面的前进后退等功能),GPU进程(3D绘制,最多一个),第三方插件进程(每种类型的插件对应一个进程,使用到的时候创建),浏览器渲染进程(默认每个Tab页面一个进程,互不影响,控制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白tab会合并成一个进程))。

这里补充一下关于javascript运行环境的解释

JavaScript运行环境指的是浏览器中负责执行JavaScript代码的部分。它包括JavaScript引擎(例如V8、SpiderMonkey等),以及提供给JavaScript代码访问的各种API(例如DOM API、Web API等)。

在单进程架构下,JavaScript运行环境与浏览器的其他模块(例如网络、插件等)都运行在同一个进程中。这意味着,如果JavaScript运行环境出现问题(例如内存泄漏、死循环等),可能会影响整个浏览器的稳定性。

(二)浏览器架构
浏览器架构-架构对比

image.png

浏览器架构-任务管理器

浏览器的任务管理器类似于操作系统的任务管理器,用来查看浏览器中各个进程和标签页的资源占用情况,例如CPU、内存、网络等。

Chrome浏览器中,点击菜单按钮(三个竖点),然后选择“更多工具”->“任务管理器”,来打开浏览器的任务管理器。

image.png

浏览器架构-多进程分工

浏览器采用多进程架构模型,通过进程之间的协作来实现网络请求、页面渲染、JavaScript 执行和 Web 安全防范等功能,并且提升了浏览器的稳定性、流畅性和安全性。

image.png

question1: 为什么会有单进程?

受到早期硬件的限制,内存比较贵。每个进程都会占用系统资源。

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

应该会替代,

二、渲染进程

常见浏览器内核

image.png

渲染进程、多进程架构

渲染进程:内部是多线程的实现,其中包括

1、GUI渲染线程 (与JS引擎线程互斥)

2、JS引擎线程

3、定时器线程

4、事件触发线程

5、网络请求执行线程

image.png

对比JS引擎和渲染引擎

JS引擎: 解析执行JS,当我们拿到JS源码(文本),通过JS引擎(解释器)进行解析,生成语法树,翻译为操作系统可以执行的字节码,v8优化:高频的函数,会转为机器码,转为机器码的好处就是下次可以直接执行,不需要像字节码一样解析执行,由此达到优化效果。

渲染引擎:HTML和CSS也是文本,根据XML解析器解析生成DOM树,CSS解析器会解析CSS样式,并将样式信息附加到DOM树上,生成渲染树。CSS解析可以与HTML解析同进行,布局render树,绘制render树(paint),绘制页面像素信息。

JS引擎和渲染引擎相互独立,如果我们想通过js操作DOM,其实是通过JSbridge操作的,因为需要通信是有一定延迟的

ps: JsBridge是一种通信方式,它允许JavaScript和原生应用之间进行通信。在Webview中,JsBridge可以实现JavaScript操作DOM。在原生应用中,前端和原生之间也可以通过JsBridge进行通信。

多进程的工作流程

image.png

练习题

以下代码在浏览器环境下输出顺序、内容

const now = Date. now( )
    setTimeout(() => {
    console. log('time10', Date.now() - now)
},10)

setTimeout(() => {
    console. log('time30', Date.now() - now)
},30)


while (true) {
    if (Date.now()- now>=20) {
        break
    }
}


console. log(Date. now() - now) // 输出? ?

答案: 先输出console.log,打印20,然后执行第一个定时器中的输出,原则上输出会晚于20,但不会太久,最后是第二个定时器的输出打印大概30左右的数。

image.png

三、Chrome的运行原理

浏览器输入了URL后发生了什么?
输入处理

image.png

开始导航

image.png

读取响应

image.png

寻找渲染进程

image.png

资源加载

image.png

构建渲染树

image.png

页面布局

image.png

页面绘制

image.png

性能工具performance

image.png

首屏优化

一、压缩、分包、删除无用代码。

  • 压缩:指使用特定的算法来压缩网页中的资源文件(如 HTML、CSS、JavaScript 和图片等),以减小文件大小,加快加载速度。例如,可以使用 Gzip 或 Brotli 等压缩算法来压缩文本文件,使用 PNGquant 或 JPEGmini 等工具来压缩图片。
  • 分包:指将网页中的资源文件分成多个包,按需加载。这样,用户在访问网页时只需要加载首屏所需的资源文件,而不必一次性加载所有文件。例如,可以使用 Webpack 或 Rollup 等打包工具来实现代码分包。
  • 删除无用代码:指删除网页中不再使用的代码和资源文件,以减小文件大小,加快加载速度。例如,可以使用 UglifyJS 或 Terser 等工具来删除 JavaScript 代码中的无用代码,使用 PurgeCSS 等工具来删除 CSS 代码中的无用样式。

2、静态资源分离

  • 静态资源分离是指将网页中的静态资源(如图片、CSS、JavaScript 等)与网页本身分开存储,通常放在 CDN(内容分发网络)上。这样,当用户访问网页时,浏览器可以从最近的 CDN 节点获取静态资源,加快加载速度。

  • 当静态资源与网页本身位于同一个域名下时,浏览器在获取静态资源时会默认携带网页的 cookie。这是因为浏览器会将同一个域名下的所有请求视为同源请求,自动携带 cookie。

  • 示例:演示如何使用 CDN 来存储静态资源

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.example.com/css/style.css">
</head>
<body>

  <h1>Hello World</h1>
  <img src="https://cdn.example.com/img/logo.png" alt="Logo">

  <script src="https://cdn.example.com/js/app.js"></script>
</body>
</html>

在这个例子中,我们使用了一个假设的 CDN 域名 cdn.example.com 来存储静态资源。我们在网页中引用了这个域名下的 CSS 文件、图片和 JavaScript 文件。

当用户访问这个网页时,浏览器会自动从 cdn.example.com 获取静态资源。由于这个域名与网页本身的域名不同,浏览器不会携带网页的 cookie。

3、js脚本非阻塞加载/放在底部

4、缓存策略。

  • 通过文件内容生成content hash是一种常用的缓存策略。它可以根据静态文件(如CSS,JS,图片)的内容生成对应的HASH值作为它的版本号,当文件内容发生变化时,版本号也会随之变化。这样可以有效地解决浏览器缓存问题。文件不变可以使用缓存hash。

  • 例如,可以使用gulp来实现这个功能。它可以通过对js和css文件内容进行hash运算,生成一个文件的唯一hash字符串,然后替换html中的js和css文件名,生成一个带版本号的文件名。

5、SSR服务端渲染

服务端渲染(SSR)指的是将组件在服务端直接渲染成HTML字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的HTML“激活”为能够交互的客户端应用。

与客户端的单页应用(SPA)相比,SSR的优势主要在于更快的首屏加载和更好的SEO。但使用SSR时也有一些权衡之处需要考量,例如开发中的限制、更多的与构建配置和部署相关的要求以及更高的服务端负载

6、预制loading,骨架屏。

预制loading和骨架屏都是用来提高用户体验的技术。它们可以在页面加载时给用户一个提示,让用户知道页面正在加载。

实现骨架屏的方法有很多,例如可以利用图片切换实现骨架屏效果,也可以利用CSS和HTML实现骨架屏效果。此外,还可以通过预渲染手动书写的代码生成相应的骨架屏,例如使用vue-skeleton-webpack-plugin插件,通过vueSSR结合webpack在构建时渲染写好的vue骨架屏组件,将预渲染生成的DOM节点和相关样式插入到最终输出的html中。

骨架屏效果的HTML和CSS代码示例:

<!-- HTML -->
<div class="skeleton">
  <div class="skeleton-header"></div>
  <ul class="skeleton-list">
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>
/* CSS */
.skeleton {
  padding: 10px;
}

.skeleton-header {
  height: 20px;
  width: 30%;
  margin-bottom: 10px;
  background-color: #e0e0e0;
}

.skeleton-list li {
  height: 20px;
  margin-bottom: 10px;
  background-color: #e0e0e0;
}

@keyframes shine {
  from {
    background-position: -100px;
  }
  to {
    background-position: 140px;
  }
}

.skeleton-header,
.skeleton-list li {
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-iteration-count: infinite;
  animation-name: shine;
  animation-timing-function: linear;
  background-image: linear-gradient(
    to right,
    #e0e0e0 0%,
    #f5f5f5 20%,
    #e0e0e0 40%,
    #e0e0e0 100%
  );
}

这个示例中,我们使用HTML创建了一个简单的骨架屏结构,包括一个头部和一个列表。然后使用CSS定义了骨架屏中元素的样式,包括高度、宽度、外边距和背景颜色等。最后,使用CSS中的animation属性定义了一个简单的动画效果,让骨架屏看起来更加生动。

渲染优化

1、GPU加速。

透明度,transfom做动画,使用GPU加速一般会新建图层,会比较耗时,属性will-change ,提前告诉浏览器这个元素需要GPU加速,然后他会把元素放到新建的涂层里。

可以给元素设置 transform 或 will-change 属性,告诉浏览器这个元素需要 GPU 加速。

例如,可以使用以下 CSS 代码来启用 GPU 加速:

.accelerate {
  transform: translateZ(0);
  will-change: transform;
}

//需要注意的是,并不是所有情况下都需要启用 GPU 加速。
//过多地使用 GPU 加速可能会导致内存占用过高,反而降低性能。
//因此,在使用 GPU 加速时应当谨慎。

2、减少回流,重绘。transfom代替left,top,display:none设置元素可见不可见会引起回流,可以使用visibilty替代,尽量不要使用table布局,很大的表单,里面任何东西改了都会重新渲染,消耗较高。

3、离屏渲染。开辟一块缓存区(内存),先进行渲染,完成渲染在显示到屏幕上。

4、懒加载。将需要的资源提前加载到本地,直接从缓存里去拿。

image.png

JS优化

一、防止内存泄露。

  • 当我们使用全局变量时,有风险。日常开发在严格模式下,辅助工具的情况下,可以检测到一些泄露风险问题,

  • 比如有一些dom,我们将把它赋值给js变量,当dom被删除,js变量还没有被清空,并且DOM占用内存很大,需要手动删除。

  • 定时器,忘记清除也会导致内存泄露。

二、循环尽早break.

三、合理使用闭包。

  • 正确的使用闭包,可以减少元素的创建,在我们使用单例的时候,闭包函数创建,即使调用多次也只创建一个变量。

四、减少dom访问。

  • js引擎和渲染引擎交互比较费时间。减少访问次数,style,className形式,可以用变量缓存起来。不要每次都去window查询。

五、防抖,节流。

  • 防抖:防止多次提交,只会提交最后一次。应用:input 输入查询搜索、scroll 滚动、表单验证、按钮提交。

  • 节流:规定时间内,多次调用也只会执行一次。dom 拖拽功能的实现、计算鼠标移动的距离、监听scroll滚动。

六、web work:

  • 新的浏览器支持的属性。js引擎相互独立,它在执行时不会阻塞浏览器渲染,有问题是传输是文本传输,大的数据传输也会有性能问题,优点是:ArrayBuffer可以直接传输,图片视频,音频处理上。

对于大量数据,可以使用可转移对象(Transferrable Object)进行零拷贝传输。例如,可以使用以下代码将 ArrayBuffer 对象传输到 worker 线程:

var worker = new Worker('js2.js');
worker.postMessage(buffer, [buffer]);

四、跨端容器

为什么需要跨端

跨端开发是指使用一种技术或方案来开发能够在多个平台(如iOS、Android和Web)上运行的应用程序。使用跨端开发可以帮助降低成本和提高开发效率。开发者只需编写一份代码,就可以在多个平台上运行,

image.png

有哪些跨端方案?
webview:
  • 网页视图,用于加载网页URL,展示其内容的控件,
  • WebView提供了一种简单的方法来在原生应用中嵌入网页内容,可以让开发者利用Web技术(如HTML、CSS和JavaScript)来构建应用程序的一部分或全部功能。这样,开发者可以利用Web技术的优势,例如快速迭代、丰富的生态系统和跨平台能力,来提高开发效率和降低成本。
webview的分类

image.png

使用webview的优势
  • 一次开发,处处使用,学习成本低。

  • 随时发布,即时更新,不用下载安装包。

  • 移动端设备性能不断提升,性能有保障,

  • 通过jsBridge和原生系统交互,实现复杂功能。

webview使用原生能力
  • js调用native

1、注入 API:native获取js环境上下文对其挂载的对象或方法进行拦截。

2、拦截 URL SCHEME:跳转拦截。

3、IOS上 window webkit messageHandler 直接通信。

  • native调用js

1、直接通过webview暴露的api执行代码。

2、对于 iOS 的 UIWebView

result = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString];

3、andriod: 使用loadUrl方法来执行一段JS代码,或者使用evaluateJavascript方法来执行一段JS函数。

可以这样调用一个名为myFunction的JS函数:

webView.loadUrl("javascript:myFunction()");
webview《==》通信

image.png

实现一个简易的JSbridge

小程序

image.png

RN/Week

image.png

Lynx

首屏幕渲染:相比RN/Week比较快,RN/Week的dom通过js创建的,虚拟dom,而Lynx的dom节点的计算放在了native层,js只关注业务逻辑,不会阻碍DOM渲染。jscore v8引擎,绑定

image.png

Flutter

Flutter是一个移动应用程序SDK,用于构建高性能、高保真的iOS和Android应用程序。Flutter中的Widget是构建应用程序用户界面的基本构建块。它们描述了应用程序在给定时刻应该呈现什么样子,并且可以响应用户交互。

Dart VM是Dart语言的虚拟机。它提供了一个即时编译器(JIT)和增量重新编译(支持热重载),实时度量收集(支持DevTools)和丰富的调试支持。当应用程序准备部署到生产环境时,Dart的预先编译(AOT)编译器可以将代码编译为本地ARM或x64机器代码,以便在移动设备或桌面计算机上运行。

Skia是一个开源的2D图形库,用于绘制文本、几何图形和图像。它提供了一组通用的API,可以在多种硬件和软件平台上使用。Skia被广泛应用于多种操作系统和应用程序中,包括Android、Chrome和Chrome OS。

image.png

通用原理

image.png

五、跨端方案对比

image.png

课程总结

image.png