- 页面js逻辑:逻辑lib_hotel_listview.fair.js 业务逻辑调用FairNet.requestData插件中的js方法
- js插件:通用插件fair_net_plugin.js FairNet类requestData方法的实现
- fair基础通用js文件: fair_core.js 定义通用js方法invokeFlutterCommonChannel
- native js:方法jsInvokeFlutterChannelSync定义 _context[FairExecuteDartFunctionSync]
页面JS文件
页面逻辑js文件加载的逻辑:
在页面生成的js逻辑文件中,会调用js方法,这个js方法的实现将会在插件js文件中定义,这里只是对 FairNet.requestData进行调用,执行传递请求参数:
页面js逻辑文件是随着页面FairWidget的渲染而每次都会加载到内存的,通过channel调用loadMainJs方法加载native得JSContext js环境中。
并且会为每一个页面动态实时的生成一个唯一ID state2key
state2key pageName和自增id组成,每一个逻辑js文件是fairwiget创建时候加载js文件到内存的
页面加载时 #FairKey# 将会被动态替换为state2key
替换代码:
scriptSource = scriptSource.replaceFirst(RegExp(r'#FairProps#'), fairProps);
scriptSource = scriptSource.replaceAll(RegExp(r'#FairKey#'), pageName);
var map = <dynamic, dynamic>{};
map[FairMessage.PATH] = scriptSource;
map[FairMessage.PAGE_NAME] = pageName;
页面逻辑JS文件中插件方法调用:
此段代码将会调用 FairNet.requestData方法,此方法将会在插件js文件fair_net_plugin.js中单独实现
requestData: function requestData() {
const __thiz__ = this;
with(__thiz__) {
_page++;
FairNet.requestData({
method: 'GET',
url: 'https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3be6c61070d3b48c8165af5d18464c0e_hotel_list_data.json',
data: convertObjectLiteralToSetOrMap({
['page']: _page,
['pageName']: '#FairKey#',//会被替换成 state2key 即'$pageName#${GlobalState._counter++}';
}),
success: function dummy(resp) {
if (resp == null) {
return null;
}
let data = resp.__op_idx__('data');
data.forEach(function dummy(item) {
let dataItem = HotelModel();
try {
dataItem.imagePath = item.imagePath;
dataItem.titleTxt = item.titleTxt;
dataItem.subTxt = item.subTxt;
dataItem.dist = item.dist + ' km';
dataItem.reviews = item.reviews + ' reviews';
dataItem.perNight = item.perNight + '';
} catch (e) {
dataItem.imagePath = item.__op_idx__('imagePath');
dataItem.titleTxt = item.__op_idx__('titleTxt');
dataItem.subTxt = item.__op_idx__('subTxt');
dataItem.dist = item.__op_idx__('dist') + ' km';
dataItem.reviews = item.__op_idx__('reviews') + ' reviews';
dataItem.perNight = item.__op_idx__('perNight') + '';
}
_listData.add(dataItem);
});
setState('#FairKey#', function dummy() {});
}
});
}
},
插件JS文件在根级FairApp创建时候被加载
插件JS文件:
插件的js基础js文件时在app启动时候通过
Runtime().loadCoreJs(package: package, jsPlugins: jsPlugins, baseJsSources: baseJsSources).then((value) => runApp(app));
方法加载的。这里没有#FairKey#模式字符串的替换过程
map[FairMessage.PATH] = baseJsSource + ' ; ' + pluginJsSource;
map[FairMessage.PAGE_NAME] = 'loadCoreJs';
参数解析
js插件fair_net_plugin.js 实现
static requestData(req) {
let respMap = {};
let id = 'FairNet$' + (callBackId++); // ID规则 callBackId全局变量自增
// 设置回调
let reqFunc = {}; //回调block 存储3个回调block
if (req.complete) {
reqFunc['complete'] = req.complete;
}
if (req.success) {
reqFunc['success'] = req.success;
}
if (req.failure) {
reqFunc['failure'] = req.failure;
}
FairNetCallBack[id] = reqFunc; //FairNetCallBack 全局变量将结果回调函数存储,通过ID获取callback,这样将结果回调
// 处理参数
let method = '';
if (req.method) {
method = req.method;
}
let url = '';
if (req.url) {
url = req.url;
}
let data = {};
if (req.data) {
data = mapOrSetToObject(req.data); //业务参数
}
let reqMap = {
pageName: '#FairKey#', //这个PageName其实也没啥用
funcName: 'invokePlugin',
'className': 'FairNet#requestData',
args: {
callId: id,
method: method,
url: url,
data: data //这里面的 ['pageName']: '#FairKey#',就是state2key,即'$pageName#${GlobalState._counter++}';
}
};
//将会被转化成:
// {
// "pageName": "#FairKey#",
// "funcName": "invokePlugin",
// "className": "FairNet#requestData",
// "args":
// {
// "callId": "FairNet$0",
// "method": "GET",
// "url": "https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3be6c61070d3b48c8165af5d18464c0e_hotel_list_data.json",
// "data":
// {
// "page": 1,
// "pageName": "HotelListView#0"
// }
// }
// }
invokeFlutterCommonChannel(JSON.stringify(reqMap), (resultStr) =>{//调用通用的方法invokeFlutterCommonChannel这个方法在fair_core.js中定义
let responseMap = JSON.parse(resultStr);
let data = responseMap['data'];
responseMap['data'] = data.data;
let id = responseMap['callId'];
//处理需要返回的结果值
let callback = FairNetCallBack[id]; //从全局变量中获取回调callback,将结果回调
if (callback == null) {
return
}
let complete = callback['complete'];
let failure = callback['failure'];
let success = callback['success'];
if (responseMap['statusCode'] === 200) {
if (complete != null) {
complete();
}
if (success != null) {
success(convertObjectLiteralToSetOrMap(responseMap));
}
} else {
if (complete != null) {
complete();
}
if (failure != null) {
failure(responseMap['statusMessage']);
}
}
})
}
dart 插件内置逻辑实现,fair_net_plugin.js插件的落地调用实际会走到dart,有dart实现网络请求
static Future<dynamic> _request(dynamic map) async {
if (map == null) {
return;
}
var req;
bool isDart;
if (map is Map) {
isDart = true;
req = map;
} else {
isDart = false;
req = jsonDecode(map);
}
var pageName = req['pageName'];
var args = req['args'];
if (isDart) {
args = req;
}
var url = args['url'];
var callId = args['callId'];
var successCallback = args['success'];
var failureCallback = args['failure'];
var completeCallback = args['complete'];
Map<String, dynamic> reqData = args['data'];//业务参数
var method = args['method'];
Response<dynamic>? response;
if (method == null) {
return Future.value();
}
switch (method) {
case 'GET':
response = await _get(url, queryParameters: reqData);
break;
case 'POST':
response = await _post(url, queryParameters: reqData);
break;
}
var statusCode = response?.statusCode;
var data = response?.data;
var statusMessage = response?.statusMessage;
/// 需要判断发起方的请求是dart端还是js端
if (isDart) {
/// 实际处理结合自身app的业务逻辑场景
if (200 == statusCode) {
successCallback?.call(data);
completeCallback?.call();
} else {
failureCallback?.call(statusMessage);
completeCallback?.call();
}
return Future.value();
} else {
// let reqMap = {
// pageName: '#FairKey#',
// funcName: 'invokePlugin',
// 'className': 'FairNet#requestData',
// args: {
// callId: id,
// method: method,
// url: url,
// data: data
// }
// };
//将会被转化成: map:
// {
// "pageName": "#FairKey#",
// "funcName": "invokePlugin",
// "className": "FairNet#requestData",
// "args":
// {
// "callId": "FairNet$0",
// "method": "GET",
// "url": "https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3be6c61070d3b48c8165af5d18464c0e_hotel_list_data.json",
// "data":
// {
// "page": 1,
// "pageName": "HotelListView#0"
// }
// }
// }
var resp = {
'callId': callId,// 透传回调,用于调用发起方通过id寻找回调callback函数
'pageName': pageName, //这个pageName其实就只是pageName,不是state2key
'statusCode': response?.statusCode,
'data': response?.data,//
'statusMessage': response?.statusMessage,
};
return Future.value(jsonEncode(resp));
}
}
通用js fair_core.js
const invokeFlutterCommonChannel = (invokeData, callback) => {
console.log("invokeData" + invokeData)
jsInvokeFlutterChannel(invokeData, (resultStr) => { //调用native定义的jsInvokeFlutterChannel方法
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
};
native FairExecuteDartFunctionAsync方法实现
_context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) { //native定义jsInvokeFlutterChannel方法
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
}
};
页面逻辑js标识
state2key算法:
state2key = GlobalState.id(widget.name);
static String id(String? prefix) {
return '$prefix#${GlobalState._counter++}';
}
总结
本文主要通过从FairNet插件的使用实例Fair的ListView模版,研究插件的运行流程和工作机制。官方的用法步骤,点击查看
- 通用js,通用的js方法调用,通过funcName,arg,pageName解析,然后进行转发。所有动态逻辑都是同一个方法名字invokeFlutterCommonChannel。
- 插件js,页面js生成逻辑中,没有识别的js方法将需要通过插件实现,这是一个app全局级别的加载,在FairApp启动时加载到naitve JavascriptCore环境中。
- 页面js,将页面转化为js方法,不会识别的js方法,将在js插件中查找,通过调用js中的插件方法,将参数从js逻辑中传递,调用js-native-dart-js的执行流程。另外一点就是每一个页面js都会随着页面加载动态加载,随着页面释放而释放。
- native通用js函数名,js调用dart,dart将结果返回
- dart实现内置逻辑使用dart实现功能











网友评论