58fair动态界面对手势点击函数添加@FairWell注解生成@和#表达式的区别

89 阅读1分钟

测试代码

用在方法上,FairWell测试:

GestureDetector(
  onTap: click1,
  child: Text('click1'),
),
GestureDetector(
  onTap: click_fairwell,
  child: Text('click_fairwell'),
),

void click1() {}

@FairWell('click_fairwell')
void click_fairwell() {}

生成json:


{
  "className": "GestureDetector",
  "na": {
    "onTap": "@(click1)",
    "child": {
      "className": "Text",
      "pa": [
        "click1"
      ]
    }
  }
},
{
  "className": "GestureDetector",
  "na": {
    "onTap": "#(click_fairwell)",
    "child": {
      "className": "Text",
      "pa": [
        "click_fairwell"
      ]
    }
  }
},  

生成js:


click1: function click1() {
    const __thiz__ = this;
    with(__thiz__) {}
},
click_fairwell: function click_fairwell() {
    const __thiz__ = this;
    with(__thiz__) {}
},

所以主要区别在于 添加@FairWell 则为

#(click_fairwell) 和不添加FairWell则为: @(click)

用在对象上:

 
getS4() {}
 
@FairWell('getS')
getS() {}
 

转化json如下:

       {
                "className": "Container",
                "na": {
                  "color": "#(Colors.red)",
                  "child": "$(getS)"
                }
              },
              {
                "className": "Container",
                "na": {
                  "color": "#(Colors.red)",
                  "child": "%(getS4)"
                }
              }

即:

对象:添加FairWell为 %(getS4) 不添加为 $(getS)

方法:添加FairWell为#(click_fairwell) 不添加FairWell则为: @(click)

运行widget

1、添加FairWell- #(click_fairwell)

#对应:

class ComponentExpression extends Expression {
  @override
  R onEvaluate(
      ProxyMirror? proxy, BindingData? binding,Domain? domain, String? exp, String? pre) {
    var processed = exp?.substring(2, exp.length - 1);
    var widget = proxy?.componentOf(processed);
    if (widget != null) return R(widget, exp: processed);
    if (processed?.contains('.') == true) {
      var s = processed!.split('.');
      assert(s.length == 2, 'expression is not supported => $exp');
      var obj = s[0];
      var prop = s[1];
      var r = proxy!.componentOf(obj);
      if (r is Map) {
        assert(!(r[prop] is Function),
            'should be an instance of widget or const value');
        return R(r[prop], exp: processed);
      }
    }
    return R(null, exp: processed);
  }

  @override
  bool hitTest(String? exp, String? pre) {
    return RegExp('#\\(.+\\)', multiLine: true).hasMatch(exp ?? '');
  }
}

ComponentExpression主要是 proxy?.componentOf(processed); 和 proxy!.componentOf(obj); 是为了查找组件block映射表 _provider.loadTag(key);查找是否有对应的block生成组件的映射对象。

在namedString和positioned递归解析Json页面widget树过程中,遇到

"onTap": "@(click1)",
"onTap": "#(click_fairwell)",

调用

 var r = proxyMirror?.evaluate(context, bound, v, domain: domain);

还原dart界面过程中使用到这个方法进行处理,但是这个方法有个特别之处:

  W<dynamic> evaluate(
      BuildContext? context, BindingData? bindingData, String text, {Domain? domain}) {
    var pre = '';
    for (var exp in _expressions) {
      if (!exp.hitTest(text, pre)) {
        continue;
      }
      var result = exp.onEvaluate(this, bindingData, domain, text, pre);
      pre = result.exp??'';
      if (result.valid()) {
        return W<dynamic>(result.data, result.needBinding);
      }
    }
    return W<dynamic>(text, false);
  }

如果 通过 #(click_fairwell) 即click_fairwell找不到对应的组件(从fair_version或者GeneratedModule的组件映射库中不存在这个名字的组件: 即从 FairWidgetBinding 中查找)

已实现的FairWidgetBinding有:

  • common.dart
  • flow.dart
  • geometry.dart
  • fair_version package中实现的FairWidgetBinding
  bool valid() {
    if (data is String) {
      String strData = data as String;

      return strData.length > 0;
    }
    return data != null;
  }

result.valid()是为false的。 则继续for循环遍历剩余的_expressions 而我们一共有这么多表达式

  final List<Expression> _expressions = [
    ComponentExpression(),
    InlineExpression(),
    InlineObjectExpression(),
    WidgetParamExpression(),
    FunctionExpression(),
    GestureExpression(),
    PropValueExpression(),
    ValueExpression(),
  ];

所以最后都会执行到 ValueExpression这个表达式

class ValueExpression extends Expression {
  @override
  R onEvaluate(
      ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
    var prop = binding?.bindDataOf(pre ?? '') ??
        binding?.modules?.moduleOf(pre ?? '')?.call();
    if (prop is ValueNotifier) {
      var data = _PropBuilder(pre ?? '', prop, proxy, binding);
      binding?.addBindValue(data);
      return R(data, exp: exp, needBinding: true);
    }
    return R(prop, exp: exp, needBinding: false);
  }

  @override
  bool hitTest(String? exp, String? pre) {
    return true;
  }
}

这个binding?.bindDataOf(pre ?? '')方法实现如下:

  dynamic bindDataOf(String key) {
    if (_values != null && _values![key] != null) {
      return Function.apply(_values![key]!, null);
    } else if (data != null && data![key] != null) {
      return data![key];
    }
    return functionOf(key);
  }

所以这里同样也是会先查找 FairDelegate 中有没有定义变量,如果没有就找有没有定义function

2、不添加- @(click)

@对应:

class GestureExpression extends Expression {
  @override
  R onEvaluate(
      ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
    var prop = binding?.bindFunctionOf(exp?.substring(2, exp.length - 1) ?? '', proxy, binding, domain, exp: exp);
    return R(prop, exp: exp, needBinding: false);
  }

  @override
  bool hitTest(String? exp, String? pre) {
    return RegExp(r'\@\(.+\)', multiLine: false).hasMatch(exp ?? '');
  }
}

bindFunctionOf 绑定方法:

 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.add(args);
          }
          _functions?['runtimeInvokeMethod']?.call(rFuncName, arguments);
        };
      } else {
        return ([props]) =>
            _functions?['runtimeInvokeMethod']?.call(funcName, props);
      }
    } else {
    //返回的是一个function函数,不解析参数
      return _functions?[funcName];
    }
  }

@(click1) _functions 如果delegate中定义了为 delegate实现的方法。 如果没有实现则为([props]) => _functions?['runtimeInvokeMethod']?.call();这样的一个block 绑定的是走js中的函数。

3、添加FairWell- %(getS4)

onEvaluate :

    var regexp = RegExp(r'\%\(.+\)');
    var matches = regexp.allMatches(exp ?? '');
    var builder = _FunctionBuilder(
        matches: matches, domain: domain, data: exp, proxyMirror: proxy, binding: binding);
    binding?.addBindValue(builder);

_FunctionBuilder

 _FunctionBuilder(
      {this.matches, this.domain,
      String? data,
      ProxyMirror? proxyMirror,
      BindingData? binding})
      : super(data, proxyMirror, binding) {
    matches?.forEach((e) {
      var bindProp;
      bindProp = binding
          ?.runFunctionOf(e.group(0)!.substring(2, e.group(0)!.length - 1), proxyMirror, binding, domain);
      if (bindProp is ValueNotifier) {
        _watchedProps.add(bindProp);
      }
    });
    attach();
  }

runFunctionOf:

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();
      //执行js,从js获取结果,可以携带参数
      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 {
  //调用fair delegate的function,不处理函数参数
    return _functions?[funcName]?.call();
  }
}

4、 不添加-$(getS)

匹配不到,走 ValueExpression

R onEvaluate(
    ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
  var prop = binding?.bindDataOf(pre ?? '') ??
      binding?.modules?.moduleOf(pre ?? '')?.call();
  if (prop is ValueNotifier) {
    var data = _PropBuilder(pre ?? '', prop, proxy, binding);
    binding?.addBindValue(data);
    return R(data, exp: exp, needBinding: true);
  }
  return R(prop, exp: exp, needBinding: false);
}

bindDataOf:

dynamic bindDataOf(String key) {
 delegate 中的value
  if (_values != null && _values![key] != null) {
    return Function.apply(_values![key]!, null);
  } else if (data != null && data![key] != null) {
 // data是fairProps数据  
    return data![key];
  }
  return functionOf(key);
}

functionOf

 //delegate中的function
Function? functionOf(String key) {
  return _functions != null ? _functions![key] : null;
}

binding?.modules?.moduleOf:

FairModuleBuilder? moduleOf(String name) => _modules[name];

总结

  • 函数名前面添加@FairWell 转化的json区别是

方法:添加FairWell为 #(xx) 不添加为 @(xx)

对象:添加FairWell为 %(xx) 不添加为 $(xx)

  • @表达式实现为GestureExpression,返回的是一个函数类型对象,如果fairdelegate中绑定了function走代理,如果没有则为调用fair运行时函数调js函数。
  • #表达式实现为先在ComponentExpression中查找name-组件的映射{"组件名字":(props)=>XXWiget()}如果没有找到再通过 ValueExpression 处理,先在 FairDelegate 中定义的的变量_values中找,然后在到页面的FairProps 数据data中找,最后从代理中的Funcitons中找。
  • 因此对于这里的点击方法(该方法名字为在map映射组件库中没有定义的name,并且我们是想执行delegate实现的function),在这种实例情况下,在编写动态界面时是否添加FairWell注解效果是相同的,都是走代理。
  • @FairWell注解在新版本fair中一般可以不用了
表达式类字面量实例正则匹配
ComponentExpression#(xxx)'#\(.+\)'
InlineObjectExpression$xxxr'${\w.+}'
InlineExpression${xxx}r'$\w+'
WidgetParamExpression$(widget.xxx)'#\(widget..+\)'
FunctionExpression%(xxx)、支持嵌套:%(xxx(^(xxx1),^(xxx2))) 、特殊名字 index:%(xxx(^(index)) r'%(.+)'
GestureExpression@(xxx)r'@(.+)'
PropValueExpression^(xxx)r'^(\w+)'
ValueExpression任何其他字符串----