本文继续记录fair界面的不同写法对fair delegate方法调用的处理的源码实现过程。 fair delegate
FairWidget界面解析2种正则匹配方式
在FairWidget 的build方法的界面解析过程 两种方式的写法将会走不同的流程。
- GestureExpression
正则:
python
RegExp(r'@(.+)', multiLine: false).hasMatch(exp ?? '');
将使用bindFunctionOf方法。
- FunctionExpression
正则:
python
RegExp(r'%(.+)', multiLine: false).hasMatch(exp ?? '');
将使用runFunctionOf 方法。
GestureExpression方式
这种方式为@(medalsInfoWidget) @开头的这种样式。
写法为:
css
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Builder(
builder: medalsInfoWidget,
),
),
Widget medalsInfoWidget(context) {
return Container();
}
他转化成json内容为:
css
{
"className": "Padding",
"na": {
"padding": {
"className": "EdgeInsets.symmetric",
"na": {
"horizontal": 20
}
},
"child": {
"className": "Builder",
"na": {
"builder": "@(medalsInfoWidget)"
}
}
}
},
"medalsInfoWidget": {
"className": "Container"
}
JS文件中这种方式将不会有medalsInfoWidget js函数生成。
运行过程中: 将会匹配到GestureExpression 执行bindFunction逻辑:
kotlin
dynamic bindFunctionOf(String funcName, ProxyMirror? proxyMirror,
BindingData? bound, Domain? domain,
{String? exp}) {
if (_functions?[funcName] == null) {
if (RegExp(r'.+(.+)', multiLine: false).hasMatch(funcName)) {
var rFuncName = funcName.substring(0, funcName.indexOf('('));
var params = funcName.substring(
funcName.indexOf('(') + 1, funcName.lastIndexOf(')'));
var args = params.split(',').map((e) {
if (RegExp(r'^(index)', multiLine: false).hasMatch(e) &&
domain is IndexDomain?) {
return domain?.index;
} else if (domain != null && domain.match(e)) {
return domain.bindValue(e);
} else {
var r = proxyMirror?.evaluate(null, bound, e, domain: domain);
if (r?.data == null) {
return e;
} else {
return r?.data is ValueNotifier ? r?.data.value : r?.data;
}
}
}).toList();
return ([props]) {
var arguments = [];
if (props != null) {
arguments.add(props);
}
if (args != null) {
arguments.addAll(args);
}
_functions?['runtimeInvokeMethod']?.call(rFuncName, true, arguments);
};
} else {
return ([props]) =>
_functions?['runtimeInvokeMethod']?.call(funcName, false, props);
}
} else {
return _functions?[funcName]; //因为我们实现了delegate 所以medalsInfoWidget 会执行delegate方法
}
}
对应我们的代理方法实现为:
javascript
Map<String, Function> bindFunction() {
var bindFunction = super.bindFunction();
bindFunction.addAll({
'medalsInfoWidget': _medalsInfoWidget,
});
return bindFunction;
}
Widget _medalsInfoWidget(context) { //这个context 不是从js传递过来的。是从build过程中获取传递的对应层的context
//context
}
整体的运行过程如下:
调用1:
调用2:
调用3:
调用4:
FunctionExpression 方式
这种方式为%(medalsInfoWidget2) %开头的这种样式。
Widget的写法为:
css
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: medalsInfoWidget2(),
),
// 不能写不支持的返回值类型,返回值不能是widget这种js不支持的类型。写返回值不会生成JS方法,也不会走delegate
medalsInfoWidget2() {}
转化成json为
css
{
"className": "Padding",
"na": {
"padding": {
"className": "EdgeInsets.symmetric",
"na": {
"horizontal": 20
}
},
"child": "%(medalsInfoWidget2)"
}
},
js方法为:
javascript
medalsInfoWidget2: function medalsInfoWidget2() {
const __thiz__ = this;
with(__thiz__) {}
},
对应我们的代理方法实现为:
javascript
Map<String, Function> bindFunction() {
var bindFunction = super.bindFunction();
bindFunction.addAll({
'medalsInfoWidget2': _medalsInfoWidget2,
});
return bindFunction;
}
Widget _medalsInfoWidget2() {
}
运行过程中: 将会匹配到 FunctionExpression 执行 runFunctionOf 逻辑:
%(medalsInfoWidget2)执行 : dynamic runFunctionOf(String funcName, ProxyMirror? proxyMirror, BindingData? bound, Domain? domain, {String? exp})
ini
_functions?[funcName]?.call();
runFunctionOf完整实现为:
kotlin
dynamic runFunctionOf(String funcName, ProxyMirror? proxyMirror,
BindingData? bound, Domain? domain,
{String? exp}) {
if (_functions?[funcName] == null) {
var result;
if (RegExp(r'.+(.+)', multiLine: false).hasMatch(funcName)) {
var rFuncName = funcName.substring(0, funcName.indexOf('('));
var params = funcName.substring(
funcName.indexOf('(') + 1, funcName.lastIndexOf(')'));
var args = params.split(',').map((e) {
if (RegExp(r'^(index)', multiLine: false).hasMatch(e) &&
domain is IndexDomain?) {
return domain?.index;
} else if (domain != null && domain.match(e)) {
return domain.bindValue(e);
} else {
var r = proxyMirror?.evaluate(null, bound, e, domain: domain);
if (r?.data == null) {
return e;
} else {
return r?.data is ValueNotifier ? r?.data.value : r?.data;
}
}
}).toList();
result = _functions?['runtimeInvokeMethodSync']?.call(rFuncName, args);
} else {
result = _functions?['runtimeInvokeMethodSync']?.call(funcName);
}
try {
var value = jsonDecode(result);
return value['result']['result'];
} catch (e) {
throw RuntimeError(errorMsg: result);
}
} else {
return _functions?[funcName]?.call();//所以这里的delegate的方法也是不能支持携带参数
}
}
结论:
GestureExpression @开头的,bindFunctionOf 调用的 return _functions?[funcName];
还是匹配到 FunctionExpression % 开头的 runFunctionOf调用的 _functions?[funcName]?.call();
在执行时,delegate都是不支持带参数。
- 所以遇到需要带参数时,需要用其他方法代替。
比如在delegate中 通过调用runtime?.variablesSync 和 runtime?.invokeMethodSync 获取js运行时的变量或者调用js函数。实例为:
csharp
var invokeMethodSync = runtime?.variablesSync(pageName, {
'medalsInfo': null,
'noMedalsIcon': null,
'noMedalsName': null,
});
var json = jsonDecode(invokeMethodSync!);
var result = json['result'];
或者调用
ini
var invokeMethodSync =
runtime?.invokeMethodSync(pageName, 'getScoreTip', null);
var json = jsonDecode(invokeMethodSync!);
var tip = json['result']['result'];
- 使用jsPlugin。可以使用通过插件可以传递更多参数
- js函数里没有办法再调用delegate了,可以用jsPlugin
- delegate是fair一期的产物,原本就不支持传参。delegate要支持传参比较复杂
- jsPlugin context不能传参,需要想办法内置到jsPlugin的dart部分
- plugin回调暂时不能支持同步调用,现在都是异步的