WebKit
WebKit 由四个部分组成,
- WebKit Embedding API:负责浏览器 UI 与 WebKit 进行交互的部分
- Platform API(WebKit Ports):让 Webkit 更加方便的移植到各个操作系统、平台上,提供的一些调用 Native Library 的接口
- WebCore:渲染引擎
- JavaScriptCore:JavaScript 引擎,也叫虚拟机
WebCore
- 首先浏览器通过URL定位到HTML、CSS、JS 组成的资源文件;
- 通过 loader(加载器)把资源文件传给 WebCore;
- HTML Parser 把 HTML 解析成 DOM 树,CSS Parser 把 CSS 解析成 CSSOM 树;
- 合并 DOM 树 和 CSSOM 树,生成最终需要的 Render Tree(渲染树);
- 再经过布局,与具体 WebKit Ports 的渲染接口,把 Render Tree(渲染树)在浏览器渲染为 Web 页面
JavaScriptCore
分四部分:
- Lexer analysis(词法分析) - 是指把一段 JavaScript 源码分解成 Token 序列的过程,该过程也叫分词(Scanner)。
- Parser(Syntactic analysis,语法分析)- 是对词法分析后生成的 token 序列进行语法分析,并生成对应的一棵抽象语法树(AST)。
- ByteCodeGenerator(字节码生成) - ByteCodeGenerator 会根据 AST 来生成字节码(ByteCode),至此完成整个语法解析步骤。
- LLInt & JIT(解释执行)- 同时,解释和执行字节码,在低延时和高吞吐之间采用的平衡方式:由低延时的 LLInt(Low Level Interpreter) 来解释执行字节码,当遇到多次重复调用、递归或循环等条件会通过 OSR 切换成 JIT 进行解释执行来加快速度。
单线程机制
当 JS 遇到耗时任务时,可以把这个任务交给一个由 JS 宿主提供的工作线程(WebWorker)去处理。待工作线程处理完后,通过事件告知 JS 线程,并在 JS 线程上去执行相应的事件处理程序(回调函数)。
JS 线程和工作线程,以及浏览器事件之间的通信机制叫做事件循环(EventLoop),其中 Call Stack 是宏任务,Task Queue 是微任务。执行的过程: 当工作线程完成异步任务之后,会把消息推到 Task Queue,消息就是注册时的回调函数;当 Call Stack 为空的时候,主线程会从 Task Queue 里取一条消息放入 Call Stack 来执行 JS,主线程会一直重复这个动作直到消息队列为空
Apple 在 iOS7 之后,对 JSCore 进行了 Objective-C 的封装,使 Native 有了执行 JS 源码的能力,例如 iOS 中的 UIWebView 和 WKWebView。
随着使用场景的不同,以及 WebKit 团队自身不停的优化,JSCore 逐渐分化出不同的版本。React Native 目前在 iOS 平台使用 JavaScriptCore(Apple要求),Android 平台默认使用 JavaScriptCore,也可切换使用 V8 或 Hermes(React Native 0.60.2 版本后支持)
JSVirtualMachine
分两部分:解释器部分,用来运行高级语言编译生成的字节码;运行时(Runtime)部分,用来负责运行时的内存空间开辟、管理等等。
支持并发的 JS 调用,管理 JS 和 Native 之间桥对象的内存。在 App 中可同时运行多个 JSVM 来执行不同的任务,但由于每个 JSVM 都有自己独立的堆空间,所以不同的 JSVM 之间是无法传递值的。
JSContext
JSContext 代表了 JS 的执行环境,通过创建一个 JSContext 去调用 JS 脚本,访问一些 JS 定义的值和函数,同时也提供了让 JS 访问 Native 对象和方法的接口。
JSContext 和 JSVM 是多对一的关系,一个 JSContext 只能绑定一个 JSVM,但是一个 JSVM 可以同时持有多个 JSContext,可通过 JSContext 的 virtualMachine API 获取当前 JSContext 所属的 JSVM 信息。
文章参考