客户端容器 | 青训营笔记

153 阅读21分钟

客户端容器

课程介绍

本节课程主要着重介绍浏览器的架构以及运行原理,并以一道八股文为例,讲解在Chrome浏览器里,网页是如何加载并且渲染成我们所见的画面。通过对本节课的学习,你还能了解到在浏览器里JS引擎和渲染引擎如何协同工作,如何从多个角度优化前端的性能体验。再以webview容器为扩展,带你认识一些常见的跨端方案。

课程重点

  • 浏览器架构

    • 浏览器架构变迁
    • 浏览器各架构对比
    • 多进程架构介绍
  • 渲染进程

    • 渲染进程多线程架构
    • JS引擎VS渲染引擎
    • 多线程工作流程
  • chrome运行原理

    • 主进程工作流程
    • 渲染进程工作流程
    • 浏览器性能优化
  • 跨端容器

    • 跨端的由来
    • 常见的跨端方案

浏览器架构

浏览器架构变迁

  1. 单进程架构: 所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等
  2. 多进程架构: 主进程、网络进程、渲染进程、GPU进程、插件进程
  3. 面向服务架构: 将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础服务

现在市面上大多数浏览器都是多进程架构,各进程相互独立,安全性比较高;

面向服务架构实际上也是多进程架构,进程之间访问需要事先定义好接口,通过api来进行基础交互

1681980854667.png

浏览器各架构对比

image.png

多进程架构介绍

image.png

  • 问题思考
  • 为什么会有单进程架构:节约资源(例如架构)和内存(早期内存昂贵)
  • 面向服务架构是否会替代多进程架构:性能较好的机器为了节约资源和优化就会选择面向服务

渲染进程

常见浏览器内核 1681983164253.png

渲染进程多线程架构

内部是多线程实现的,主要负责页面渲染,脚本执行,时间处理,网络请求等 image.png

JS引擎VS渲染引擎

  1. 解析执行JS
  2. XML解析生成渲染树,显示在屏幕
  3. 桥接方式通信

V8将高频函数转成机器码,下次可以直接执行,起到优化作用

JS引擎和渲染引擎是相互独立的,虽然JS代码执行是很快的,但是在操作dom的时候通过bridge桥接通信会造成延时 image.png

多线程工作流程

  1. 网络线程负责加载网页资源
  2. JS引擎解析JS脚本并且执行
  3. JS解析引擎空闲时,渲染线程立即工作
  4. 用户交互、定时器操作等产生回调函数放入任务队列中
  5. 事件线程进行事件循环,将队列里的任务取出交给JS引擎执行

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) // 输出: 20 30 50

执行顺序:while语句--知道等于20ms退出循环--》最底下的语句,输出20ms-->第一个延时器,等待10ms,控制台输出30ms----->第二个延时器,前一个已经等待了10ms,此时只需等待20ms,控制台输出(20+30)50ms

chrome运行原理

如何展示页面

image.png

浏览器地址输入url后发生了什么? 请你谈谈网站是如何进行访问的(改动Windows底层文件)

1. 输入域名;回车

2. 先检查hosts配置文件里面有没有www.xsn.com域名的映射(一个IP地址可以对应多个域名,一个域名对应一个IP)

2.1. 有则直接返回对应的IP地址127.0.0.1,这个地址有我们需要访问的web程序,可以直接访问

2.2. 没有则去DNS服务器(管理所有域名的地方)找

  1. 对于web程序的访问,浏览器主进程会处理输入,然后开始导航,导航后去浏览器读取响应,再寻找渲染进程去渲染这次加载,拿到html/css/js脚本后,构建dom,cssom,由这两者构成渲染树,之后开始布局,再绘制图层,最后合成,进行展示,展示完成,返回浏览器主进程。(比如输入index.html,导航到该html的位置,读取浏览器响应回来的html、css文件,交由渲染进程渲染页面,包括加载html文件和图片、CSS样式、JS脚本)

输入处理

image.png

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

开始导航

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

读取响应

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

image.png

寻找渲染进程

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

image.png

渲染进程工作流程

资源加载

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

image.png

构建渲染树

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

image.png

页面布局

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

image.png

页面绘制

  1. 构建图层:为特定的节点生成专用图
  2. 绘制图层: 一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程
  3. 合成线程接收指令生成图块
  4. 栅格线程将图块进行栅格化(栅格化其实就是把图块转成绘图,具体成一个个像素点,会使用GPU加速栅格化过程)
  5. 展示在屏幕上(第四步调用显卡就可以展示了)

image.png

浏览器性能优化

前端性能performance

image.png

  1. 时间都花在哪:解析,等待资源加载
  2. 什么情况下卡顿:接口加载时间太长,来不及刷新

首屏优化

  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动画或骨架屏,以提高用户体验。这些技术可以在页面加载完成之前,先显示一些占位元素,给用户一个等待的感觉,从而减少用户等待的焦虑和不安

渲染优化

  1. GPU加速:将复杂的图形处理任务交给GPU来处理,可以加快页面的渲染速度。可以使用CSS3的transformopacity等属性来开启GPU加速
  2. 减少回流、重绘:回流和重绘是影响页面性能的主要因素之一。可以通过避免使用影响布局的属性、批量修改DOM元素等技术来减少回流和重绘操作
  3. 离屏染染:离屏渲染是将页面中的部分内容在单独的图层中进行渲染,从而减少对主渲染线程的阻塞。可以使用CSS3的transformposition等属性来开启离屏渲染。
  4. 懒加载:将页面中的非必要资源(如图片和视频等)延迟加载,可以加快页面的加载速度。可以使用Intersection ObserverLazyload等技术来实现懒加载

JS优化

  1. 防止内存泄漏:
  • 有可能出现内存泄漏的场景

    • 全局变量:全局变量会一直存在于内存中,直到程序结束才会被释放。如果程序中定义了大量的全局变量,就会导致内存占用过多,从而导致内存泄漏。
    • 闭包:闭包会在函数中保存局部变量和参数,如果函数执行后,闭包中的变量没有被释放,就会导致内存泄漏。为了避免内存泄漏,应该合理使用闭包,并注意释放不需要的变量。
    • 循环引用:循环引用是指两个或多个对象之间相互引用,形成了一个死循环,导致内存无法释放。为了避免循环引用,应该及时释放不需要的引用,并使用垃圾回收机制来自动释放内存。
    • 定时器和事件监听器:定时器和事件监听器会持续占用内存,直到被清除或被解除绑定。如果程序中存在大量的定时器和事件监听器,就会导致内存占用过多,从而导致内存泄漏。
    • DOM节点:DOM节点也会占用内存空间,如果程序中存在大量的DOM节点,就会导致内存占用过多,从而导致内存泄漏。为了避免内存泄漏,应该及时清除不需要的DOM节点
  • 内存泄漏会导致不必要的内存占用和程序崩溃。可以使用letconst关键字声明变量,避免变量污染和内存泄漏

  1. 循环尽早break:在循环中,如果已经找到了需要的结果,可以使用break语句尽早结束循环,避免无用的迭代和计算
  2. 合理使用闭包:闭包可以在函数中保存局部变量和参数,避免全局变量的污染和泄漏。但是,如果使用不当,也会导致内存泄漏和性能下降
  3. 减少Dom访问:DOM操作是JavaScript性能的一个瓶颈。可以使用缓存和批量操作等技术来减少DOM访问次数,从而提高JavaScript的性能
  4. 防抖、节流:防抖是在一定时间内只执行一次目标函数,而节流是在一定时间内控制目标函数执行次数。它们都可以有效地减少函数执行频率,降低性能开销,提高用户体验。防抖和节流是用来控制函数调用频率的技术。可以使用setTimeoutrequestAnimationFrame等API来实现防抖和节流(或者用第三方库也行)
  5. Web Workers:Web Workers是一种在后台线程中执行JavaScript代码的技术。可以将耗时的计算任务和数据处理等操作放到Web Workers中执行,避免阻塞主线程,提高页面的响应速度

跨端容器

跨端的由来

为什么需要跨端:

  1. 开发成本低、效率高:使用跨端技术,开发者只需编写一份代码,就可以在多个平台(如iOS、Android和Web)上运行。这可以减少开发和维护的工作量,节省时间和资源。同时,开发团队可以更快地推出新功能和修复问题,因为他们只需关注一份代码库。
  2. 一致性体验: 跨端开发可以确保在不同平台上提供一致的用户体验。使用跨端技术,开发者可以更容易地保持应用的外观和功能一致,无论用户在什么设备上使用。这有助于提高用户满意度和用户留存率。
  3. 前端开发生态:跨端开发受益于强大的前端生态系统。许多流行的前端框架和库,如React Native、Flutter和Ionic,都支持跨端开发。这些工具为开发者提供了丰富的资源和丰富的社区支持,帮助他们更轻松地实现跨端功能。

image.png

常见的跨端方案

image.png

跨端容器-WebView

  1. Webview,即网页视图,用于加载网页Url,并展示其内容的控件

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

  1. 可以内嵌在移动端App内,实现前端混合开发,大多数混合框架都是基于Webview的二次开发;比如lonic、Cordova

跨端容器-常用WebView分类

常用webview,Android,IOS、国产Android

image.png

  • 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优势

  1. 一次开发,处处使用,学习成本低
  2. 随时发布,即时更新,不用下载安装包
  3. 移动设备性能不断提升,性能有保障
  4. 通过JSBridge和原生系统交互,实现复杂功能

WebView的主要优点包括: 跨平台兼容性,代码重用,简化更新过程(随时发布,即时更新,不用下载安装包),通过JSBridge与原生系统交互,实现更复杂的功能。

局限:性能下降,原生功能访问限制(可能需要编写额外代码),用户体验不一致有差异。

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

  1. Javascript 调用Native
  • API注入: Native获取Javascript环境上下文,对其挂载的对象或者方法进行拦截
  • 使用Webview URL Scheme 跳转拦截
  • IOS上 window.webkit.messageHandler 直接通信
  1. Native 调用 Javascript
  • 直接通过webview 暴露的 API 执行JS代码
  • IOS webview.stringByEvaluatingJavaScriptFromString
  • Android webview.evaluateJavascript

跨端容器-WebView<->Native 通信

  1. JS环境中提供通信的JSBridge
  2. Native 端提供 SDK 响应JSBridge 发出的调用
  3. 前端和客户端分别实现对应功能模块 JSBridge是一种在WebView中实现原生与JavaScript之间通信的技术 image.png

image.png

回调后删除回调函数,防止缓存越来越大

跨端容器-小程序

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

image.png

跨端容器-React Native/WeeX

  1. 原生组件渲染
  2. React/Vue框架
  3. virtual dom
  4. JSBridge

image.png

0. React Native: React Native是Facebook开发的一个开源框架,它允许开发者使用React和JavaScript编写原生移动应用。React Native的主要特点是"Learn once, write anywhere"(学习一次,随处编写),这意味着开发者可以在不同平台(如iOS和Android)上使用相同的技术栈构建原生应用。

React Native的优势包括:

  • 代码复用:React Native允许开发者在多个平台上复用大部分代码,从而减少开发时间和成本。

  • 热重载:React Native支持热重载功能,允许开发者在不重新编译整个应用的情况下查看代码更改的效果,提高开发效率。

  • 原生性能:虽然React Native使用JavaScript编写,但它将JavaScript代码转换为原生组件,从而提供了接近原生应用的性能。

  • 丰富的生态系统:React Native拥有庞大的社区支持,开发者可以利用许多第三方库和组件来加速开发过程。

  1. Weex: Weex是由阿里巴巴开发的一个开源框架,它允许开发者使用Vue.js和JavaScript编写原生移动应用。Weex的目标是实现"Write once, run everywhere"(编写一次,随处运行),即使用一套代码构建多个平台的原生应用。

Weex的优势包括:

  • 代码复用:类似于React Native,Weex也支持在不同平台上复用代码,从而降低开发成本。
  • 原生渲染:Weex将Vue.js组件转换为原生组件进行渲染,从而实现了较高的性能。
  • 插件系统:Weex提供了一个丰富的插件系统,开发者可以使用这些插件来轻松实现原生功能,如地图、支付等。
  • 模块化:Weex允许开发者将应用拆分为多个模块,这有助于实现高度模块化的开发过程,提高代码可维护性。

跨端容器-Lynx

  1. Vue:Vue.js是一种渐进式JavaScript框架,它使开发者能够轻松地构建可扩展、高性能的应用。基于Vue.js的设计原则,Lynx可以提供简洁、模块化的代码结构,以及良好的开发体验
  2. JS Core / V8:Lynx选择JS Core(iOS平台)或V8(Android平台) 作为其JavaScript引擎。这意味着Lynx应用在运行时,JavaScript代码将在高性能的JavaScript引擎中执行。通过使用这些优秀的JavaScript引擎,Lynx能够确保应用在不同平台上具有稳定、高性能的运行表现
  3. JSBinding(高效的JSBridge):Lynx使用JSBinding技术实现JavaScript与原生代码之间的通信。这种技术允许JavaScript直接调用原生方法,并使原生代码能够执行JavaScript回调。通过JSBinding,Lynx实现了高效的原生与JavaScript之间的通信,降低了性能损失
  4. Native Ul / Skia:Lynx使用Native UI组件和Skia作为其渲染引擎。Native UI组件意味着Lynx应用在运行时,界面将使用原生组件进行渲染。这可以确保应用具有接近原生应用的性能和用户体验。同时,Lynx还采用了Skia图形库,它是一种高性能的2D图形渲染引擎,用于绘制图形和文本。Skia使Lynx应用在渲染复杂界面时能够保持流畅的帧率和高质量的视觉效果

image.png

跨端容器-Flutter

Flutter是Google开发的一个开源UI工具包,旨在为开发者提供一种构建优美、高性能的跨平台应用的解决方案。Flutter具有一些独特的特点,包括基于Widget的设计、Dart VM以及使用Skia图形库

  1. wideget
  2. dart vm
  3. skia图形库

image.png

  • 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应用具有高度的可定制性,同时还保持了流畅的性能和优美的视觉效果。

跨端容器-通用原理

  1. UI组件
  2. 渲染引擎
  3. 逻辑控制引擎(一般是JS,但Flutter使用dart vm)
  4. 通信桥梁
  5. 底层API抹平表现差异

image.png

image.png

跨端容器-思考??

同样是基于webview渲染,为什么小程序体验比webview流畅

因为小程序做了离线缓存,大部分资源是本地的。而webview要去服务器加载资源,进入页面先有loading页面,会先去加载需要的资源。小程序是针对webview做了一些精致的,对于一些比较危险的操作,比如dom操作,是封死的。

未来的跨端方案会是什么:webview(最广)

总结

image.png

感想

这门课程是目前为止觉得最陌生也最有意思的课程。系统讲解了浏览器的架构以及运行原理,结合我之前学的服务器域名查找web程序的知识就可以总结出输入url时浏览器会发生什么过程。输入域名;回车。先检查hosts配置文件里面有没有www.xsn.com域名的映射(一个IP地址可以对应多个域名,一个域名对应一个IP), 有则直接返回对应的IP地址127.0.0.1,这个地址有我们需要访问的web程序,可以直接访问。 没有则去DNS服务器(管理所有域名的地方)找。 对于web程序的访问,浏览器主进程会处理输入,然后开始导航,导航后去浏览器读取响应,再寻找渲染进程去渲染这次加载,拿到html/css/js脚本后,构建dom,cssom,由这两者构成渲染树,之后开始布局,再绘制图层,最后合成,进行展示,展示完成,返回浏览器主进程。(比如输入index.html,导航到该html的位置,读取浏览器响应回来的html、css文件,交由渲染进程渲染页面,包括加载html文件和图片、CSS样式、JS脚本)