目前越来越多的移动应用采用Hybird App模式来开发,即混合了 Native技术 与 Web技术 进行开发。在iOS开发中,JavaScriptCore框架支持JS与OC直接互相调用,从而实现动态化方案。
JavaScriptCore框架简介
JavaScriptCore框架是苹果在iOS7引入的一个框架,该框架让 Objective-C 和 JavaScript 代码 互通,即支持在Objective-C中执行js代码,也支持js代码中执行OC代码。
浏览器中能执行JS脚本是因为浏览器中内置了JavaScript引擎。JavaScriptCore是苹果Safari浏览器的JavaScript引擎,而JavaScriptCore框架是基于Objective-C实现了对JavaScriptCore的封装,提供了Objective-C接口,让开发者能够在在iOS App中处理JavaScript脚本。
简单使用
先来简单了解下在iOS Native开发中如何使用JavaScriptCore框架
#import "JavaScriptCore/JavaScriptCore.h" // 引入头文件
...
...
// 创建一个JSContext对象
JSContext *jsContext = [[JSContext alloc] init];
// 执行JS代码 计算js变量a和b之和
[jsContext evaluateScript:@"var a = 1;var b = 2;"];
JSValue *result = [jsContext evaluateScript:@"a + b"];
NSInteger sum = [result toInt32];
NSLog(@"%ld", (long)sum); // 3
上面代码中做了如下几件事情
- 引入"JavaScriptCore/JavaScriptCore.h"头文件
- 创建了一个JSContext类对象jsContext
- jsContext调用evaluateScript:方法执行了两条js语句,并得到执行结果result,是一个JSValue类对象
var a = 1;var b = 2; a + b
- result调用toInt32方法,返回数值类型的sum并打印,输出结果为3。
这里涉及了JavaScriptCore框架中的两个核心类:JSContext类 和 JSValue类。这里先说明下他们的作用,后续再详细介绍。
JSContext类: 一个JSContext表示了一次JS的执行环境。在iOS开发中,可以通过创建一个JSContext去调用JS脚本,访问一些JS定义的值和函数,同时也提供了让JS访问Native对象、方法的接口。
JSValue类: JS侧的代码执行结果都可以从JSContext中获取然后赋值给JSValue对象,JSValue是保证JS端和Native的方法能互相调用的桥梁。
更多使用
OC代码中执行JS语句
- (void)ocEvaluateScript {
// 创建一个JSContext对象
JSContext *jsContext = [[JSContext alloc] init];
// 执行JS代码 计算js变量a和b之和
[jsContext evaluateScript:@"var a = 1;var b = 2;"];
NSInteger sum = [[jsContext evaluateScript:@"a + b"] toInt32];
NSLog(@"%ld", (long)sum); // 3
// 通过下标获取变量、方法
[jsContext evaluateScript:@"var names = ['Same','Jack','Bob']"];
JSValue *names = jsContext[@"names"];
JSValue *initialName = names[0];
NSLog(@"%@", initialName.toString); // 'Same'
// 定义方法并调用
[jsContext evaluateScript:@"var addFunc = function(a, b) { return a + b }"];
JSValue *result = [jsContext evaluateScript:@"addFunc(a, b)"];
NSLog(@"%@", result.toNumber); // 3
// 也可以OC传参
JSValue *addFunc = jsContext[@"addFunc"];
JSValue *addResult = [addFunc callWithArguments:@[@10, @30]];
NSLog(@"%d", addResult.toInt32); // 40
}
OC代码中执行JS脚本内容
ocEvaluateScript.js脚本
function subtractFunc(a, b) {
return a - b;
}
console.log(subtractFunc(15, 7))
console.log("Hello ocEvaluateScript")
OC代码中加载ocEvaluateScript.js脚本并执行
- (void)ocEvaluateScriptFile {
JSContext *jsContext = [[JSContext alloc] init];
NSString *path = [[NSBundle mainBundle] pathForResource:@"ocEvaluateScript" ofType: @"js"];
NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[jsContext evaluateScript:layoutJS];
JSValue *subtractFunc = jsContext[@"subtractFunc"];
JSValue *subtractResult = [subtractFunc callWithArguments:@[@20, @10]];
NSLog(@"%d", subtractResult.toInt32); // 10
}
- OC代码中加载并执行了ocEvaluateScript.js,然后调用了subtractFunc方法,控制台输出结果为10;
- 打开safari浏览器,运行程序后,会弹出网页检查器,其控制台输出ocEvaluateScript.js脚本的执行结果

JS脚本执行OC代码
scriptEvaluateOC.js脚本内容,addFunc方法和subtractFun方法是在OC代码中定义的。
console.log(addFunc(5, 5))
console.log(subtractFunc(8, 5))
console.log("Hello scriptEvaluateOC")
OC代码中定义addFunc方法和subtractFun方法,并加载执行scriptEvaluateOC.js
- (void)scriptEvaluateOC {
JSContext *jsContext = [[JSContext alloc] init];
jsContext[@"addFunc"] = ^(NSInteger a, NSInteger b) {
return a + b;
};
JSValue *addResult = [jsContext evaluateScript:@"addFunc(3, 4)"];
NSLog(@"%@", addResult.toNumber); // 7
// setObject:forKeyedSubscript:方法用来向JSContext环境的全局对象中添加属性
[jsContext setObject:^(NSInteger a, NSInteger b) {
NSArray *args = [JSContext currentArguments];
NSLog(@"argu are %@", args);
return a - b;
} forKeyedSubscript:@"subtractFunc"];
JSValue *subtractResult = [jsContext evaluateScript:@"subtractFunc(4, 3)"];
NSLog(@"%@", subtractResult.toNumber); // 1
NSString *path = [[NSBundle mainBundle] pathForResource:@"scriptEvaluateOC" ofType: @"js"];
NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[jsContext evaluateScript:layoutJS];
}
打开safari浏览器,运行程序后,会弹出网页检查器,其控制台输出scriptEvaluateOC.js脚本的执行结果

JavaScriptCore框架详解
JavaScriptCore框架包含的头文件真的是非常的少,如下所示,

整个框架的结构图参考JavaScriptCore框架详解

JavaScript和JavaScriptCore

JSContent

JSValue


JSManagedValue

JSExport

JSVirtualMachine

Native UI动态化方案
Native UI动态化是指在js脚本中编写Native的UI信息,然后由Native加载执行,解析转换成Native的UI实现。这种方案可以支持js层做了关于渲染信息的一些修改后,Native只要重新加载执行js就可以更新UI信息而不需要重新编译,打包发版。 这里简单的实现一个UILabel标签的示例来说明下思路 view.js
(function(){
return render();
})();
//JS标签类
function Label(rect,text,color){
this.rect = rect;
this.text = text;
this.color = color;
this.typeName = "Label";
}
//JS Rect类
function Rect(x,y,width,height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
//渲染方法 界面的渲染写在这里面
function render(){
var rect = new Rect(20,100,280,30);
var label = new Label(rect,"Hello World", "0xff0000");
return label
}
Native逻辑 在ViewController.m中定义render方法并执行,主要逻辑如下:
#define HEXCOLOR(hexValue) [UIColor colorWithRed : ((CGFloat)((hexValue & 0xFF0000) >> 16)) / 255.0 green : ((CGFloat)((hexValue & 0xFF00) >> 8)) / 255.0 blue : ((CGFloat)(hexValue & 0xFF)) / 255.0 alpha : 1.0]
#define HEXACOLOR(hexValue, alphaValue) [UIColor colorWithRed : ((CGFloat)((hexValue & 0xFF0000) >> 16)) / 255.0 green : ((CGFloat)((hexValue & 0xFF00) >> 8)) / 255.0 blue : ((CGFloat)(hexValue & 0xFF)) / 255.0 alpha : (alphaValue)]
-(void)render {
NSString *path = [[NSBundle mainBundle] pathForResource:@"view" ofType: @"js"];
NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
JSContext *jsContext = [[JSContext alloc] init];
JSValue * subValue = [jsContext evaluateScript:layoutJS];
UILabel * label = [UILabel new];
label.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);
label.text = subValue[@"text"].toString;
label.textColor = HEXCOLOR(subValue[@"color"].toInt32);
[self.view addSubview:label];
}
界面展示结果
