客户端容器 | 青训营笔记

106988222_p4

浏览器架构

浏览器架构演进

  1. 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等
  2. 多进程架构(现代浏览器的常用架构):主进程、网络进程、渲染进程、GPU进程、插件进程
  3. 面向服务架构:算是多进程架构的升级版。将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础网络服务

后两者运行在独立沙盒当中,进程相互隔离,安全性比较高,且充分利用系统资源(独立渲染)流畅度高

任务管理器

查看路径:【左上角三个点】→【更多工具】→【任务管理器】

多进程分工

进程名称 进程描述
浏览器(主进程) 主要负责页面展示逻辑,用户交互,子进程管理;包括地址栏、书签、前进、后退、收藏夹等
GPU进程 负责UI绘制,包含整个浏览器全部UI
网络进程 网络服务进程,负责网络资源加载
标签页(渲染进程) 控制tab内的所有内容,将HTML、CSS和JavaScript转换为用户可交互的网页
插件进程 控制网站运行的插件,比如flash、ModHeader等
其他进程 如上图所示:适用程序Storage/Network/Audio Service等

渲染进程

多线程架构

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

  1. 解释执行JS

    • JS引擎:JS引擎负责解析和执行JavaScript代码。
    • 渲染引擎:渲染引擎的主要任务是解析HTML和CSS,构建DOM树和渲染树,然后将渲染树转换为屏幕上的像素。
  2. XML解析生成渲染树,显示在屏幕

    • 渲染引擎:渲染引擎负责解析HTML和XML文档,构建DOM树。

    DOM树是由DOM元素及其属性组成的树状结构。渲染树构建完成后,渲染引擎会根据渲染树来绘制页面。

  3. 桥接方式通信

    • JS引擎:JS引擎提供了DOM API,使得JavaScript代码可以访问和操作DOM树。

image-20230511220603634

渲染进程 - 多线程工作流程

image-20230511221335790

  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树进行操作,从而触发渲染线程的更新。事件循环会持续进行,直到任务队列为空,或者浏览器关闭。

一道笔试题

问题

以下代码会输出什么?为什么?

const now = Date.now();

// 定时器1:10ms后输出"time10"和定时器1的执行延时
setTimeout(() => {
  console.log('time10', Date.now() - now); // 输出1:?
}, 10);

// 定时器2:30ms后输出"time30"和定时器2的执行延时
setTimeout(() => {
  console.log('time30', Date.now() - now); // 输出2:?
}, 30);

// 一直执行循环,直到距离当前时间戳至少过去20ms
while (true) {
  if (Date.now() - now >= 20) {
    break;
  }
}

console.log(Date.now() - now); 

解答

在我的Chrome上结果如下

20
time10 20
time30 31

原因: 函数 setTimeout 接受两个参数:待加入队列的消息和一个时间值(可选,默认为 0)。这个时间值代表了消息被实际加入到队列的最小延迟时间。如果队列中没有其它消息并且栈为空,在这段延迟时间过去之后,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。

Chrome运行原理

如何展示网页

ff4fd74d-0f30-46d5-bdad-2352ad904f37

输入处理

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

开始导航

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

读取响应

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

寻找渲染进程

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

渲染进程 - 资源加载

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

渲染进程 - 构建渲染树

  1. 构建DOM树,将HTML文本转化成浏览器能够理解的结构

  2. 构建CSSOM树,浏览器同样不认识CSS,需要将CSS代码转化为可理解的CSSOM

  3. 构建渲染树,渲染树是DOM树和CSSOM树的结合

    image (1)

渲染进程 - 页面布局

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

渲染进程 - 页面绘制

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

首屏优化

  • 压缩、分包、删除无用代码
  • 静态资源分离
  • JS脚本非阻塞加载

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

  • 缓存策略
  • SSR

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

  • 预置loading、骨架屏

渲染优化

  • GPU加速:一般是利用css3
  • 减少回流、重绘
  • 离屏渲染

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

  • 懒加载

JS优化

  • 防止内存泄漏

  • 循环尽早break

  • 合理使用闭包

  • 减少DOM访问

  • 防抖、节流

    1. 防抖(debounce): 防抖是指在一定时间内,如果连续触发事件,那么只执行一次目标函数,比如LOL的回城。常用于输入框实时搜索、窗口大小调整等场景。防抖的实现原理是:设置一个定时器,在指定的延迟时间内,如果再次触发事件,则重新计时。只有在延迟时间内没有再次触发事件时,才会执行目标函数。
    2. 节流(throttle): 节流是指在一定时间内,无论触发多少次事件,目标函数都只执行一次,理解为技能CD。常用于滚动事件、鼠标移动等场景。节流的实现原理是:设置一个间隔时间,在这个时间内,无论事件触发多少次,都只执行一次目标函数。一旦超过这个间隔时间,就会再次执行目标函数。

    总结: 防抖和节流的主要区别在于,防抖是在一定时间内只执行一次目标函数,而节流是在一定时间内控制目标函数执行次数。它们都可以有效地减少函数执行频率,降低性能开销,提高用户体验。

  • Web Worker

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

跨端容器

  • webview
  • 小程序
  • RN 、Weex
  • Lynx
  • Flutter

WebView

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

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

原生能力

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 通信

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

image (1)

以下是一个简单的JSBridge

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

interface CallArgs {
    callId: string; // 调用Id,唯一标识
    module: string; // 调用模块
    method: string; // 调用方法
    data: any; // 参数
}

const Callbacks = { } // 存放回调函数 callId为key

function applyNative = (payload:CallArgs,callback:Function)=>{
    const callId = prefix + callTime++
    Callbacks[callId] = callback
    const Args0:CallArgs = {
        callId:callId,
        module:payload.module || 'layout',
        method:payload.method || 'randomSize',
        data:payload.data
    }
    if(IOS){
        return window.webkitURL.messageHandler.postMessage(JSON.stringify(Args0))
    }else{
        // 安卓对window上约定的对象进行拦截
        return window.AndroidBridge(JSON.stringify(Args0))
    }
}

interface ResponseArgs{
    responseId: string;// 回调Id,与callId对应
    errCode: number; 
    errMsg?: string;
    data: unknown;
}
// Native 端调用webview,参数都经过序列化
const applyWebview = (res:string)=>{
    const response = JSON.parse(res) as ResponseArgs
    const {responseId} = response
    // 从Callbacks找到对应的回调处理方法
    if(typeof Callbacks[responseId]) === 'function'{
        Callbacks[responseId](response)
        // 回调后删除该次回调函数
        delete Callbacks[responseId]
    }
}

// 挂在在window上,供native直接调用
window.JSBridge = {
    applyNative,
    applyWebview
}

通用原理

  1. UI组件

  2. 渲染引擎

  3. 逻辑控制引擎

  4. 通信桥梁

  5. 底层API抹平表面差异

    image (1)

思考

  1. 同样是基于WebView渲染,为什么小程序体验比WebView流畅?

  2. 未来的跨端方案会是什么?

    回归WebView?(易上手)

posted @ 2023-05-11 23:34  520Enterprise  阅读(33)  评论(0编辑  收藏  举报