了解WebKit
我们每天都会接触浏览器,使用浏览器进行工作、娱乐。让浏览器能够正常工作最核心的部分就是浏览器的内核,每个浏览器都有自己的内核,Safari的内核就是WebKit。
在上面的WebKit组成图中,我们可以发现只有WebCore是红色的。这是因为时至今日,WebKit已经有很多的分支以及各大厂家也进行了很多优化改造,唯独WebCore这个部分是所有WebKit共享的。是整个WebKit中最核心的渲染引擎。
WebKit的渲染流程:
简单点讲,WebKit就是一个页面渲染以及逻辑处理引擎
- 浏览器获取到HTML、CSS、JS组成的资源
- 通过加载器交给WebCore
- HTML Parser会把HTML解析成DOM树,CSS Parser会把CSS解析成CSSOM树
- 合并两棵树并布局,生成最终的渲染树,通过WebKit Ports渲染接口输出屏幕
JSCore基本概念
JSCore是WebKit默认内嵌的JS引擎,之所以说是默认内嵌,是因为很多基于WebKit分支开发的浏览器引擎都开发了自家的JS引擎,其中最出名的就是Chrome的V8。
可以看到,相比静态编译语言生成语法树之后,还需要进行链接,装载生成可执行文件等操作,解释型语言在流程上要简化很多。
它主要分为以下三个部分:词法分析、语法分析以及解释执行。
JSVirtualMachine
- JSVM是我们要学习的重要概念,一个JSVM就代表一个JS虚拟机,
- 一个JSVirtualMachine(以下简称JSVM)实例代表了一个自包含的JS运行环境,或者是一系列JS运行所需的资源。该类有两个主要的使用用途:一是支持并发的JS调用,二是管理JS和Native之间桥对象的内存。
- 一个JSVM中,只有一条线程可以跑JS代码,所以我们无法使用JSVM进行多线程处理JS任务。如果我们需要多线程处理JS任务的场景,就需要同时生成多个JSVM,从而达到多线程处理的目的。
iOS中的JSCore
iOS7之后,苹果对WebKit中的JSCore进行了Objective-C的封装,并提供给所有的iOS开发者。JSCore框架给Swift、OC以及C语言编写的App提供了调用JS程序的能力。同时我们也可以使用JSCore往JS环境中去插入一些自定义对象。
JSCore常常被认为是一个JS语言的优化虚拟机,它做着JVM类似的事情,只是相比静态编译的Java,它还多承担了把JS源代码编译成字节码的工作。
JSCore Framework的15个开放头文件来“管中窥豹”很有必要了解的概念只有4个:JSVM、JSContext、JSValue、JSExport。
如下图所示:
JSContext
一个JSContext表示了一次JS的执行环境。我们可以通过创建一个JSContext去调用JS脚本,访问一些JS定义的值和函数,同时也提供了让JS访问Native对象,方法的接口。
同理,JSContext就是JS语言的执行环境,所有JS代码的执行必须在一个JSContext之中,在WebView中也是一样,我们可以通过KVC的方式获取当时WebView的JSContext。通过JSContext运行一段JS代码十分简单。
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"var a = 1;var b = 2;"];
NSInteger sum = [[context evaluateScript:@"a + b"] toInt32];//sum=3
我们还可以通过KVC的方式,给JSContext塞进去很多全局对象或者全局函数:
JSContext *context = [[JSContext alloc] init];
context[@"globalFunc"] = ^() {
NSArray *args = [JSContext currentArguments];
for (id obj in args) {
NSLog(@"拿到了参数:%@", obj);
}
};
context[@"globalProp"] = @"全局变量字符串";
//console输出:“拿到了参数:全局变量字符串”
[context evaluateScript:@"globalFunc(globalProp)"];
在JSContext的API中,有一个值得注意的只读属性 -- JSValue类型的globalObject。它返回当前执行JSContext的全局对象,例如在WebKit中,JSContext就会返回当前的Window对象。
而这个全局对象其实也是JSContext最核心的东西,当我们通过KVC方式与JSContext进去取值赋值的时候,实际上都是在跟这个全局对象做交互,几乎所有的东西都在全局对象里,可以说,JSContext只是globalObject的一层壳。对于上述两个例子,本文取了context的globalObject,并转成了OC对象,可以看到这个globalObject保存了所有的变量与函数,这更加印证了上文的说法(globalObject对应OC对象是NSDictionary类型)
JSValue
JSValue实例是一个指向JS值的引用指针。我们可以使用JSValue类,在OC和JS的基础数据类型之间相互转换。同时我们也可以使用这个类,去创建包装了Native自定义类的JS对象,或者是那些由Native方法或者Block提供实现JS方法的JS对象。
每个JSValue都存在于一个JSContext之中,这也就是JSValue的作用域,都是因为JSCore帮我们用JSValue在底层自动做了OC和JS的类型转换。
JSExport
实现JSExport协议可以开放OC类和它们的实例方法,类方法,以及属性给JS调用。
总结
JSCore给iOS App提供了JS可以解释执行的运行环境与资源。
对于我们实际开发而言,最主要的就是JSContext和JSValue这两个类。JSContext提供互相调用的接口,JSValue为这个互相调用提供数据类型的桥接转换。让JS可以执行Native方法,并让Native回调JS,反之亦然。
所有基于JSCore的Hybrid开发基本就是靠上图的原理来实现互相调用,区别只是具体的实现方式和用途不大相同。大道至简,只要正确理解这个基本流程,其它的所有方案不过是一些变通,都可以很快掌握。
桥方法的实现是怎么通过JSCore交互的?
市面上常见的桥方法调用有两种:
通过UIWebView的delegate方法:shouldStartLoadWithRequest来处理桥接JS请求。JSRequest会带上methodName,通过WebViewBridge类调用该method。执行完之后,会使用WebView来执行JS的回调方法,当然实际上也是调用的WebView中的JSContext来执行JS,完成整个调用回调流程。
通过UIWebView的delegate方法:在webViewDidFinishLoadwebViewDidFinishLoad里通过KVC的方式获取UIWebView的JSContext,然后通过这个JSContext设置已经准备好的桥方法供JS环境调用。
Reference:
https://mp.weixin.qq.com/s/H5wBNAm93uPJDvCQCg0_cg