1.浏览器结构
主要架构模式介绍:
- 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等
- 多进程架构:主进程、网络进程、渲染进程、GPU进程、插件进程
- 面向服务架构:算是多进程架构的升级版。将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础网络服务
进程和线程
- 进程是系统内存分配的一小部分内存空间
- 进程之间相互独立(不同进程之间可以相互通信(IPC),不过代价很大)
- 进程由单个或多个线程组成
- 多个线程之间是可以相互协作完成工作的
- 同一个进程中各个线程之间共享同一块内存空间
浏览器结构——多进程分工
浏览器都多进程
chrome浏览器使用的是多进程多线程模式,因为现在的网页复杂性非常高。如果整个浏览器是单进程的,有可能某个page界面的抛错就会导致整个浏览器的crash。同时多个界面互相可以访问相同的内存和相同的执行环境,安全性也是一个大的问题,所以浏览器需要采用多进程模式。
进程分工
- 1,浏览器主进程(Browser进程):控制chrome的地址栏,书签栏,返回和前进按钮,同时提供存储功能
- 2,第三方插件进程:每种插件一个进程,插件运行时才会创建
- 3,浏览器渲染进程(浏览器内核,内部是多线程的):负责界面渲染,脚本执行,事件处理等
- 4,GPU进程:用于3D绘制
- 5, 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里边的,最近独立为了一个进程
思考
为什么会有单进程架构?
早期受到硬件限制,为了节约资源。
面向服务架构是否会替代多进程架构?
有可能会被替代。
2.渲染进程
常见浏览器内核
渲染进程——多线程架构
渲染进程内部是多线程实现,主要负责页面渲染,脚本执行,事件处理,网络请求等。
对比JS引擎和渲染引擎
JS引擎: 解析执行JS,当我们拿到JS源码(文本),通过JS引擎(解释器)进行解析,生成语法树,翻译为操作系统可以执行的字节码,v8优化:高频的函数,会转为机器码,转为机器码的好处就是下次可以直接执行,不需要像字节码一样解析执行,由此达到优化效果。
渲染引擎:HTML和CSS也是文本,根据XML解析器解析生成DOM树,CSS解析器会解析CSS样式,并将样式信息附加到DOM树上,生成渲染树。CSS解析可以与HTML解析同进行,布局render树,绘制render树(paint),绘制页面像素信息。
JS引擎和渲染引擎相互独立,如果我们想通过js操作DOM,其实是通过JSbridge操作的,因为需要通信是有一定延迟的
渲染进程——多进程的工作流程
- 网络线程负责加载网页资源
- JS引擎解析JS脚本并执行
- JS解析引擎空闲时,渲染进程立即工作
- 用户交互、定时器操作等产生回调函数放入任务队列中
- 事件线程进行事件循环,将队列里的任务 取出交给JS引擎执行
3.Chrome运行原理
如何展示网页
浏览器输入了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
时间都花在哪里?
什么情况下卡顿?
First Paint 首次绘制(FP)
First contentful paint 首次内容绘制 (FCP)
Largest contentful paint 最大内容绘制 (LCP)
First input delay 首次输入延迟 (FID)
Time to Interactive 首次可交互时间 (TTI)
Total blocking time 总阻塞时间 (TBT)
Cumulative layout shift 累积布局偏移 (CLS)
首屏优化
- 压缩、分包、删除无用代码
- 静态资源分离
- JS脚本非阻塞加载
- 缓存策略
- SSR
- 预置loading、骨架屏
渲染优化
- GPU加速:一般是利用css3
- 减少回流、重绘
- 离屏渲染
- 懒加载
JS优化
- 防止内存泄漏
- 循环尽早break
- 合理使用闭包
- 减少DOM访问
- 防抖、节流
- Web Worker
4.跨端容器
为什么需要跨端
- 开发成本、效率
- 一致性体验
- 前端开发生态多
有哪些跨端方案
- webview
- 小程序
- RN 、Weex
- Lynx
- Flutter
webview
1.Webview,即网页视图,用于加载网页Url,并展示其内容的控件。 2.可以内嵌在移动端App内,实现前端混合开发,大多数混合框架都是基于Webview的二次开发:比如lonic、Cordova、uniapp。
使用webview的优势
- 一次开发,处处使用,学习成本低。
- 随时发布,即时更新,不用下载安装包。
- 移动端设备性能不断提升,性能有保障,
- 通过jsBridge和原生系统交互,实现复杂功能。
webview使用原生能力
- js调用native
1、注入 API:native获取js环境上下文对其挂载的对象或方法进行拦截。
2、拦截 URL SCHEME:跳转拦截。
3、IOS上 window webkit messageHandler 直接通信。
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通信:
- JS环境中提供通信的JSBridge
- Native端提供SDK响应JSBridge发出的调用
- 前端和客户端分别实现对应功能模块
实现一个简单的JSBridge:
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
}
小程序
RN/Week
React Native / Weex
- 原生组件渲染
- React / Vue 框架
- virtual dom
- JSBridge
Lynx
字节自研的一款跨端框架。(暂未开源,字节旗下使用较多)
- Vue
- JS Core / V8
- JSBinding
- Native UI / Skia
Flutter
Flutter是一个移动应用程序SDK,用于构建高性能、高保真的iOS和Android应用程序。Flutter中的Widget是构建应用程序用户界面的基本构建块。它们描述了应用程序在给定时刻应该呈现什么样子,并且可以响应用户交互。
- wideget
- dart vm
- skia图形库
通用原理
- UI组件
- 渲染引擎
- 逻辑控制引擎
- 通信桥梁
- 底层API抹平表现差异
跨端方案对比
同样是基于webview渲染,为什么小程序体验比webview流畅?
- 小程序会先把需要的资源下载下来,在使用的时候直接使用;限制了相关DOM操作。