客户端容器

62 阅读8分钟

浏览器架构

架构演进

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

image.png

架构对比

image.png

任务管理器

image.png

多进程分工

image.png

渲染进程

常见浏览器内核

image.png

多线程架构

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

image.png

image.png

JS引擎 VS 渲染引擎

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

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) //输出? ?

答案

image.png

Chrome 运行原理

如何展示网页

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. 渲染进程开始接收数据并告知主进程自己已开始处理,导航结束,进入文档加载阶段

资源加载

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

image.png

构建渲染树

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

image.png

页面布局

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

页面绘制

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

image.png

前端性能performance

image.png

首屏优化

  1. 压缩、分包、删除无用代码
  2. 静态资源分离
  3. JS脚本非阻塞加载
  4. 缓存策略
  5. SSR
  6. 预置loading、骨架屏

渲染优化

  1. GPU加速
  2. 减少回流、重绘
  3. 离屏渲染
  4. 懒加载

JS优化

  1. 防止内存泄漏
  2. 循环尽早break
  3. 合理使用闭包
  4. 减少Dom访问
  5. 防抖、节流
  6. Web Workers

跨端容器

为什么需要跨端

  1. 开发成本、效率
  2. 一致性体验
  3. 前端开发生态

image.png

跨端方案

image.png

WebView

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

常用WebView分类

image.png

使用WebView优势

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

使用原生能力

Javascript 调用Native

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

Native 调用 Javascript

  • 直接通过webview 暴露的 API 执行JS代码
  • IOS webview.stringByEvaluatingJavaScriptFromString
  • Android webview.evaluateJavascript

WebView<->Native 通信

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

image.png

实现一个简易JSBridge

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

Lynx

  1. Vue
  2. JS Core / V8
  3. JSBinding
  4. Native Ul / Skia

image.png

Flutter

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

image.png

跨端容器通用原理

  1. UI组件
  2. 渲染引擎
  3. 逻辑控制引擎
  4. 通信桥梁
  5. 底层API抹平表现差异

image.png

跨端方案对比(仅个人意见)

image.png

总结

通过这节课学习,我对客户端容器有了更好的理解,不过我对其中优化前端部分比较感兴趣,所以我去掘金网站上继续找了些资料,下面是我获取到对重绘重排的知识的理解

重绘重排

  1. HTML网页被HTML解析器(HTML Parser)解析生成DOM树
  2. css被css解析器解析生成CSSDOM树
  3. 结合DOM树和CSSDOM树,生成一棵渲染树(Render Tree)
  4. 生成布局(layout),即定位坐标和大小,是否换行,各种position, overflow, z-index属性 ……,将所有渲染树的所有节点进行平面合成
  5. 将布局绘制在屏幕上(Painting)

重排:当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排(回流)
重绘:- 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘

常见引起重排的属性和方法

  1. 添加或者删除可见的DOM元素
  2. 元素尺寸改变——边距、填充、边框、宽度和高度
  3. 内容变化,如在input框中输入文字
  4. 浏览器窗口尺寸改变——resize事件发生时
  5. 计算 offsetWidth 和 offsetHeight 属性
  6. 设置 style 属性的值
  7. 激活伪类,如 :hover

"重绘"不一定会出现"重排","重排"必然会出现"重绘"

优化

分离读写操作

div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);

这段代码会触发4次重排+重绘,因为在console中请求的这几个样式信息,无论何时浏览器都会立即执行渲染队列的任务,即使该值与你操作中修改的值没关联,因为队列中可能有影响到这些值的操作,为了给我们最精确的值,浏览器会立即重排+重绘

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);

还是上面触发4次重排+重绘的代码,这次只触发了一次重排:
在第一个 console 的时候,浏览器把之前上面四个写操作的渲染队列都给清空了。剩下的 console ,因为渲染队列本来就是空的,所以并没有触发重排,仅仅拿值而已

样式集中改变

  • 不要一条一条地修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 classNamecsstext
// bad
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

// good
div.className += " theclassname";
div.style.cssText += "; left: " + left + "px; top: " + top + "px;";

离线改变dom

  • 隐藏要操作的dom
    在要操作dom之前,通过display隐藏dom,当操作完成之后,才将元素的display属性为可见,因为不可见的元素不会触发重排和重绘(触发两次重排,display的隐藏和显示各一,适用对dom进行触发超过两次重排的多次修改)

position属性为absolute或fixed

减小重排开销,不用考虑对其他元素的影响

优化动画

  • 牺牲平滑 适当牺牲平滑,换取速度,自己权衡 比如实现一个动画,以1个像素为单位移动这样最平滑,但是reflow就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多
  • 启用GPU加速
    • GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率
    • GPU 加速通常包括以下几个部分:Canvas2D,布局合成, CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)
/*
 * 根据上面的结论
 * 将 2d transform 换成 3d
 * 就可以强制开启 GPU 加速
 * 提高动画性能
 */
div {
  transform: translate3d(10px, 10px, 0);
}

参考:浏览器重绘(repaint)重排(reflow)与优化浏览器机制 - 掘金 (juejin.cn)
DocumentFragment - Web API 接口参考 | MDN (mozilla.org)
浏览器的渲染原理简介 | 酷 壳 - CoolShell