1.ReactNative源碼分析 - 概述
2.ReactNative源碼分析 - JavaScriptCore C語言篇
3.ReactNative源碼分析 - 啟動(dòng)流程
4.ReactNative源碼分析 - 通信機(jī)制
5.ReactNative源碼分析 - 渲染原理
一、JavaScriptCore簡介
- JavaScriptCore是iOS、macOS中使用的WebKit框架中的內(nèi)嵌JavaScript引擎,在iOS7開始引入系統(tǒng)庫,命名為JavaScriptCore.framework。WebKit是開源的,感興趣可以自行下載源碼。
- JavaScriptCore主要功能是解析執(zhí)行JavaScript腳本。它支持在原生環(huán)境(Objective-C、Swift、C)中執(zhí)行JavaScript代碼,同時(shí)支持把原生對(duì)象注入到JavaScript環(huán)境中供其使用,結(jié)合這兩點(diǎn)JavaScriptCore就具備JS&Native交互的能力。
二、JavaScriptCore與ReactNative
- JavaScriptCore可以執(zhí)行JS腳本,具備JS&Native交互的能力。ReactNative正是利用它的這一特性,在JavaScriptCore的基礎(chǔ)上構(gòu)建一座橋梁(Bridge),使得Native與JS可以高效且便捷地互相調(diào)用,這便是ReactNative框架的核心。
- ReactNative使用React做業(yè)務(wù)開發(fā)卻達(dá)到了原生開發(fā)的用戶體驗(yàn),原理大致是:JavaScriptCore執(zhí)行React模式編寫的JS業(yè)務(wù)邏輯代碼,通過以JavaScriptCore為基礎(chǔ)的Bridge,JS端把執(zhí)行結(jié)果傳遞到原生端執(zhí)行原生組件渲染、調(diào)用原生端導(dǎo)出函數(shù)驅(qū)動(dòng)所有的原生功能;原生端給予JS端反饋(回調(diào))、或調(diào)起JS端功能……JS端收到反饋,進(jìn)行新一輪計(jì)算,再次驅(qū)動(dòng)原生端功能……不斷循環(huán)這個(gè)過程。
- 注:V0.60.4版本開始,ReactNative集成了專們?yōu)橹蛟斓腏avaScript引擎Hermes,據(jù)官方表示:Hermes引擎使得Android平臺(tái)上ReactNative應(yīng)用在啟動(dòng)時(shí)間、內(nèi)存占用、包體積都有很大程度的優(yōu)化。
三、JavaScriptCore C語言版本
-
業(yè)界有很多OC版本的JavaScriptCore優(yōu)秀教程(eg:深入淺出 JavaScriptCore 、打通前端與原生的橋梁:JavaScriptCore 能干哪些事情?),筆者就不廢話了。
本文僅對(duì)OC/C語言版JavaScriptCore做個(gè)簡單對(duì)比,并給出一個(gè)C語言版本JavaScriptCore進(jìn)行Native&JS互調(diào)的例子。
OC版本的JavaScriptCore接口相對(duì)友好,學(xué)習(xí)C語言版本JavaScriptCore,建議與上述博客對(duì)比,并結(jié)合Xcode中JavaScriptCore.framework的接口文檔注釋來理解。
OC&C語言版本JavaScript主要類型對(duì)比圖.jpg
- 上圖為JavaScriptCore的OC版本與C版本的主要類型對(duì)比圖。OC版本是對(duì)C版本的一個(gè)面向?qū)ο蠓庋b,大部分的類型看名字就可以知曉其對(duì)于關(guān)系。JSVirtualMachine不容易看出來,不過看源碼即可發(fā)現(xiàn)它其實(shí)就是對(duì)JSContextGroupRef的封裝,兩者是對(duì)應(yīng)關(guān)系。
@implementation JSVirtualMachine {
JSContextGroupRef m_group;
Lock m_externalDataMutex;
NSMapTable *m_contextCache;
NSMapTable *m_externalObjectGraph;
NSMapTable *m_externalRememberedSet;
}
- (instancetype)init
{
JSContextGroupRef group = JSContextGroupCreate();
self = [self initWithContextGroupRef:group];
// The extra JSContextGroupRetain is balanced here.
JSContextGroupRelease(group);
return self;
}
四、JavaScript與Native交互
- ReactNative底層庫jsi使用C++對(duì)JavaScriptCore進(jìn)行了封裝,并以JSCRuntime供外界使用。因此本文使用C++演示JS&Native互調(diào)。JSCRuntime為了達(dá)到隔離、通用性等目的,做了很多相對(duì)復(fù)雜的封裝,但其基本原理就是以下演示的調(diào)用邏輯,可以先理解這個(gè)流程,再去看源碼,避免被繞暈。
- 附上DEMO
- 值得注意的是:
關(guān)聯(lián)了原生對(duì)象的JS對(duì)象,屬性取值回調(diào)函數(shù)、函數(shù)調(diào)用回調(diào)函數(shù)的執(zhí)行線程,與JS腳本執(zhí)行的線程一致
。在主線程執(zhí)行JS腳本,則回調(diào)函數(shù)執(zhí)行線程是主線程;在異步線程執(zhí)行JS腳本,則回調(diào)函數(shù)執(zhí)行線程是異步線程。這一點(diǎn)關(guān)系到后面分析Native&JS交互的執(zhí)行線程控制。
1.Native 調(diào)用 JS
const char *script = "var factorial = function (n) {\
if (n < 0) return;\
if (n == 0) return 1;\
return n * factorial(n-1);\
};\
var Person = function () {\
this.age = 18;\
this.sayHello = function(name) {\
return \"hello \" + name;\
}\
};\
var myName = \"fuyou\";\
var p = new Person();";
JSStringRef scriptStrRef = JSStringCreateWithUTF8CString(script);
// 創(chuàng)建JS Context用于執(zhí)行JS腳本
JSContextGroupRef group = JSContextGroupCreate();
JSGlobalContextRef ctx = JSGlobalContextCreateInGroup(group, NULL);
JSObjectRef globalObj = JSContextGetGlobalObject(ctx);
JSEvaluateScript(ctx, scriptStrRef, NULL, NULL, 1, NULL);
JSStringRelease(scriptStrRef);
/*
1. 獲取全局變量 myName
*/
JSStringRef myName = JSStringCreateWithUTF8CString("myName");
JSValueRef myNameValue = JSObjectGetProperty(ctx, globalObj, myName, NULL);
JSStringRef myNameStr = JSValueToStringCopy(ctx, myNameValue, NULL);
CFStringRef myNameCfStr = JSStringCopyCFString(kCFAllocatorSystemDefault, myNameStr);
// CFStringRef轉(zhuǎn)string
const CFIndex kCStringSize = 30;
char temporaryCString[kCStringSize];
bzero(temporaryCString,kCStringSize);
CFStringGetCString(myNameCfStr, temporaryCString, kCStringSize, kCFStringEncodingUTF8);
string name(temporaryCString);
cout << "myName = " << name << endl << endl;
JSStringRelease(myName);
/*
2. 獲取全局函數(shù)factorial,并執(zhí)行
*/
// 獲取函數(shù)
JSStringRef factorialStr = JSStringCreateWithUTF8CString("factorial");
JSValueRef factorialValue = JSObjectGetProperty(ctx, globalObj, factorialStr, NULL);
JSObjectRef factorialObj = JSValueToObject(ctx, factorialValue, NULL);
// 構(gòu)造參數(shù)
JSValueRef argument = JSValueMakeNumber(ctx, 3);
JSValueRef arguments[1];
arguments[0] = argument;
// 執(zhí)行函數(shù),返回結(jié)果
JSValueRef factorialResultValue = JSObjectCallAsFunction(ctx, factorialObj, NULL, 1, arguments, NULL);
double factorialResult = JSValueToNumber(ctx, factorialResultValue, NULL);
cout << "factorialResult = " << factorialResult << endl << endl;
/*
3. 獲取全局對(duì)象,并調(diào)用對(duì)象函數(shù)
*/
// 獲取全局對(duì)象
JSStringRef pStr = JSStringCreateWithUTF8CString("p");
JSValueRef pStrVal = JSObjectGetProperty(ctx, globalObj, pStr,NULL);
JSStringRelease(pStr);
JSObjectRef pObj = JSValueToObject(ctx, pStrVal, NULL);
// 獲取對(duì)象的sayHello函數(shù)(屬性)
JSStringRef pFuncStr = JSStringCreateWithUTF8CString("sayHello");
JSValueRef pFuncVal = JSObjectGetProperty(ctx, pObj, pFuncStr, NULL);
JSStringRelease(pFuncStr);
JSObjectRef pFuncObj = JSValueToObject(ctx, pFuncVal, NULL);
// 構(gòu)造函數(shù)參數(shù)
JSStringRef argStr = JSStringCreateWithUTF8CString("wuyanzu");
JSValueRef arg = JSValueMakeString(ctx, argStr);
JSStringRelease(argStr);
JSValueRef args[1];
args[0] = arg;
// 調(diào)用函數(shù)
JSValueRef pResVal = JSObjectCallAsFunction(ctx, pFuncObj, NULL, 1, args, NULL);
// 函數(shù)返回值類型轉(zhuǎn)化
JSStringRef pResStr = JSValueToStringCopy(ctx, pResVal, NULL);
CFStringRef pRes = JSStringCopyCFString(kCFAllocatorSystemDefault, pResStr);
// CFStringRef轉(zhuǎn)string
bzero(temporaryCString, kCStringSize);
CFStringGetCString(pRes, temporaryCString, kCStringSize, kCFStringEncodingUTF8);
string result(temporaryCString);
cout << "sayHello() = " << result << endl << endl;
JSStringRelease(myName);
JSContextGroupRelease(group);
JSGlobalContextRelease(ctx);
- 執(zhí)行結(jié)果為:
myName = fuyou
factorialResult = 6
sayHello() = hello wuyanzu
2.JS 調(diào)用 Native
- 構(gòu)建原生類,js屬性、函數(shù)回調(diào)
// 原生C++類
class Worker {
private:
int salary = 9999;
public:
int money = 0;
void work() {
money += salary;
}
};
/*
JSObjectGetPropertyCallback:js獲取屬性時(shí),會(huì)調(diào)用該函數(shù)
參數(shù) ctx:會(huì)話; object:對(duì)象; propertyName:屬性名
返回值 返回對(duì)象屬性值
*/
JSValueRef getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
Worker *w = static_cast<Worker*>(JSObjectGetPrivate(object));
return JSValueMakeNumber(ctx, w->money);
}
/*
JSObjectCallAsFunctionCallback:js調(diào)用函數(shù)是,會(huì)調(diào)用該函數(shù)
參數(shù) ctx:會(huì)話;function:被調(diào)用的函數(shù)(函數(shù)即對(duì)象) thisObject:this對(duì)象; argumentCount:參數(shù)個(gè)數(shù); arguments:參數(shù)值
執(zhí)行js語句myObject.myFunction()是, function為myFunction對(duì)象, thisObject為調(diào)用者myObject.
返回值 返回函數(shù)調(diào)用結(jié)果
*/
JSValueRef callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[],JSValueRef *exception)
{
Worker *w = static_cast<Worker*>(JSObjectGetPrivate(thisObject));
w->work();
return JSValueMakeUndefined(ctx);
}
- 構(gòu)建JS對(duì)象、原生對(duì)象,并把原生對(duì)象關(guān)聯(lián)到JS對(duì)象
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
JSObjectRef globalObj = JSContextGetGlobalObject(ctx);
// 定義類屬性
/*
typedef struct {
const char* name; 屬性名
JSObjectGetPropertyCallback getProperty; 取值回調(diào) 獲取屬性值時(shí)調(diào)用該函數(shù),通過該函數(shù)返回屬性值
JSObjectSetPropertyCallback setProperty; 設(shè)值回調(diào) 設(shè)置屬性值時(shí)調(diào)用該函數(shù),通過該函數(shù)設(shè)置屬性值
JSPropertyAttributes attributes;
} JSStaticValue;
*/
JSStaticValue values[] = {
{"money", &getProperty, 0, kJSPropertyAttributeNone },
{ 0, 0, 0, 0}
};
// 定義類函數(shù)
/*
typedef struct {
const char* name; 函數(shù)名
JSObjectCallAsFunctionCallback callAsFunction; 函數(shù)被調(diào)用時(shí),調(diào)用該函數(shù),通過該函數(shù)返回調(diào)用結(jié)果
JSPropertyAttributes attributes;
} JSStaticFunction;
*/
JSStaticFunction functions[] = {
{"work", &callAsFunction, kJSPropertyAttributeNone },
{ 0, 0, 0 }
};
// 定義類
JSClassDefinition classDef = kJSClassDefinitionEmpty;
classDef.version = 0;
classDef.attributes = kJSClassAttributeNone;
classDef.className = "Worker";
classDef.parentClass = 0;
classDef.staticValues = values;
classDef.staticFunctions = functions;
// 創(chuàng)建一個(gè) JavaScript Worker類
JSClassRef t = JSClassCreate(&classDef);
// 新建一個(gè)JavaScript類對(duì)象,并使之綁定原生Worker對(duì)象w
Worker w;
JSObjectRef classObj = JSObjectMake(ctx,t, &w);
// 將新建的對(duì)象注入JavaScript中
JSStringRef objName = JSStringCreateWithUTF8CString("w");
JSObjectSetProperty(ctx, globalObj, objName, classObj, kJSPropertyAttributeNone, NULL);
// 執(zhí)行js代碼,“兩次”調(diào)用w對(duì)象work()函數(shù)
JSStringRef workScript = JSStringCreateWithUTF8CString("w.work()");
JSEvaluateScript(ctx, workScript, classObj, NULL, 1, NULL);
JSEvaluateScript(ctx, workScript, classObj, NULL, 1, NULL);
JSStringRelease(workScript);
// 執(zhí)行js代碼,獲取w對(duì)象money屬性
JSStringRef getMoneyscript = JSStringCreateWithUTF8CString("var money = w.money;");
JSEvaluateScript(ctx, getMoneyscript, classObj, NULL, 1, NULL);
JSStringRelease(getMoneyscript);
// 對(duì)比原生對(duì)象、js對(duì)象 屬性值
JSStringRef moneyRef = JSStringCreateWithUTF8CString("money");
JSValueRef moneyValue = JSObjectGetProperty(ctx, globalObj, moneyRef, NULL);
JSStringRelease(moneyRef);
double money = JSValueToNumber(ctx, moneyValue, NULL);
cout << endl << "JS環(huán)境中, w.money = " << money << endl;
cout << "原生環(huán)境中,w.money = " << w.money << endl << endl;
JSGlobalContextRelease(ctx);
- 執(zhí)行結(jié)果如下
JS環(huán)境中, w.money = 19998
原生環(huán)境中,w.money = 19998