加入收藏 | 设为首页 | 会员中心 | 我要投稿 辽源站长网 (https://www.0437zz.com/)- 云专线、云连接、智能数据、边缘计算、数据安全!
当前位置: 首页 > 综合聚焦 > 酷站推荐 > 酷站 > 正文

闲鱼专家详解:Flutter React编程范式实践

发布时间:2019-02-06 08:54:43 所属栏目:酷站 来源:匠修
导读:副标题#e# 作者:闲鱼技术-匠修 Flutter Widget的设计灵感来源于React,是一款原生就立足于响应式的UI框架。本文基于Flutter特点,试图结合闲鱼在Flutter的工程应用来谈下我们对Flutter React编程范式的思考和践行。 Reactive的诞生 谈起UI总会讲到MVC,它出

  首先是业务逻辑和界面分离,界面是无状态(Stateless)的,我们也正在尝试自动化的方法直接生成界面代码,所以Widget中是不会有业务逻辑代码的。当我们把一个能描述当前界面的数据(State)交给View层时,界面就应该能正常展示。用户和界面交互会产生Action,Action代表了用户交互的意图,Action可以携带信息(比如用户使用输入留言,Action中就应该携带用户留言的内容信息)。Action会输入给Store,Store会通过注册的Interrupters对Action做前期拦截处理,可以通过Interrupter截拦Action,也可以把一个Action重新改写成另外的Action。Store然后收集相应绑定的Reducers对Action做一次reduce操作,产生新的State,并通知界面刷新。

  通常我们在创建Store的时候就组册好Reducer和Interrupter:

  Store<PublishState> buildPublishStore(String itemId) {//设置状态初始值

  PublishState initState = new PublishState();

  initState.itemId = itemId;

  initState.isLoading = true;//创建Reducer和对应Action的绑定

  var reducerBinder = ActionBinder.reducerBinder<PublishState>()

  ..bind(PublishAction.DETAIL_LOAD_COMPLETED, _loadCompletedReducer)

  ..bind(PublishAction.DELETE_IMAGE, _delImageReducer)

  ..bind(PublishAction.ADD_IMAGE, _addImageReducer);//创建Interrupter和对应Action的绑定

  var interrupterBinder = ActionBinder.interrupterBinder<PublishState>()

  ..bind(PublishAction.LOAD_DETAIL, _loadDataInterrupter)

  ..bind(PublishAction.ADD_IMAGE, UploadInterruper.imageUploadInterrupter); //创建Store

  return new CommonStore<PublishState>(

  name: 'Publish',

  initValue: initState,

  reducer: reducerBinder,

  interrupter: interrupterBinder);

  }

  Reducer中就是处理用户交互时产生的Action的逻辑代码,接收3个参数,一个是执行上下文,一个要处理的Action,一个是当前的State,处理结束后必须返回新的State。函数式理想的Reducer应该是一个无副作用的纯函数,显然我们不应该在Reducer中去访问或者改变全局域的变量,但有时候我们会对前面的计算结果有依赖,这时可以将一些运行时数据寄存在ReduceContext中。Reducer中不应该有异步逻辑,因为Store做Reduce操作是同步的,产生新State后会立即通知界面刷新,而异步产生对State的更新并不会触发刷新。

  PublishState _delImageReducer(ReduceContext<PublishState> ctx, Action action, PublishState state) {int index = action.args.deleteId;

  state.imageUplads.removeAt(index);return state;

  }

  Interrupter形式上和Reducer类似,不同的是里面可以做异步的逻辑处理,比如网络请求就应该放在Interrupter中实现。

  *为什么会有Interrupter呢?换一个角度,我们可以把整个Store看成一个函数,输入是Action,输出的是State。函数会有副作用,有时我们输入参数并不一定得会相应有输出,比如日志函数( void log(String) ),我们输入String只会在标准输出上打印一个字符串,log函数不会有返回值。同样,对Store来说,也不是所有的Action都要去改变State,用户有时候触发Action只要想让手机震动下而已,并不会触发界面更新。所以,Interrupter就是Store用来处理副作用的。

  ///截拦一个网络请求的Action,并在执行请求网络后发出新Action

  bool _onMtopReq(InterrupterContext<S> ctx, Action action) {

  NetService.requestLight(

  api: action.args.api,

  version: action.args.ver,params: action.args.params,

  success: (data) {

  ctx.store.dispatch(Action.obtain(Common.MTOP_RESPONSE)

  ..args.mtopResult = 'success'

  ..args.data = data);

  },

  failed: (code, msg) {

  ctx.store.dispatch(Action.obtain(Common.MTOP_RESPONSE)

  ..args.mtopResult = 'failed'

  ..args.code = code

  ..args.msg = msg);

  });return true;

  }

  通常我们会让一个界面根部的InheritedWidget来持有Store,这样界面上的任何Widget都能方便的访问到Store,并和Store建立联系。这种做法可以参考redux_demo,再此不详细展开。

  最后简单的说说Store的实现,Store能够接收Action,然后执行reduce,最后向widget提供数据源。Widget可以基于提供的数据源建立数据流,响应数据变更来刷新界面。这其中最核心的就是Dart的Stream。

  ......

  //创建分发数据的Stream

  _changeController = new StreamController.broadcast(sync: false);//创建接收Action的Stream

  _dispatchController = new StreamController.broadcast(sync: false);//设置响应Action的函数

  _dispatchController.stream.listen((action) {

  _handleAction(action);

  });

  ......

  //向Store中分发Action

  void dispatch(Action action) {

  _dispatchController.add(action);

  }

  //Store向外提供的数据源

  Stream<State> get onChange => _changeController.stream;

  Store中最核心的对Action进行reduce操作:

  //收集该Action绑定的Reducer

  final List<ReduceContext<State>> reducers = _reducers.values

  .where((ctx) => ctx._handleWhats.any((what) => what == action.what))

  .toList();

  //执行reduce

  Box<Action, State> box = new Box<Action, State>(action, _state);

  box = reducers.fold(box, (box, reducer) {

  box.state = reducer._onReduce(box.action, box.state);return box;

  });

  //触发更新

  _state = box.state;

  _changeController.add(_state);

  Widget基于Store暴露的数据源建立数据流:

  store.onChange//将Store中的数据转换成Widget需要的数据

  .map((state) => widget.converter(state))

  //比较前一次数据,如果想等则不用更新界面

  .where((value) => (value != latestValue))

  //更新界面

  .listen((value){

  ...

  setState()

  ...

  })

  组件化的扩展

(编辑:辽源站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读