最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • flutter开发指南 for 前端开发工程师

    正文概述 掘金(kun坤)   2020-12-25   682

    flutter是一个高效跨平台的ui框架
    相较react native 和 weex ,它不需要转化成原生控件
    也不需要 jscore 作为中间层进行桥接
    它可以通过skia引擎直接进行ui渲染
    可谓优点颇多

    那本文呢,是以一个前端开发工程师视角来谈谈flutter
    以及响应式框架的一些东西~

    响应式框架

    flutter开发指南 for 前端开发工程师
    这是一个mvvm框架的基本结构
    基本上,mvvm框架在一切需要渲染UI的场景中,都能发挥强大的作用
    因为本质上它就是完全解耦了数据与视图

    mvc/p

    flutter开发指南 for 前端开发工程师 flutter开发指南 for 前端开发工程师
    再来回望下前端mvc/p框架,这是一些很纯粹的数据/视图分离框架
    它模块化程度很高,但是没有解决 数据与视图之间同步 的问题
    或者认为这个同步的事情应该交给平台(浏览器)去实现
    直到前端工程化开始流行起来之后,各种mvvm框架也就应运而生
    他们大致的原理都如下:

    flutter开发指南 for 前端开发工程师

    flutter中是如此:

    flutter开发指南 for 前端开发工程师

    组件的本质

    好,回到flutter来,这是flutter中的hello world

    import 'package:flutter/material.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Welcome to Flutter',
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('Welcome to Flutter'),
            ),
            body: new Center(
              child: new Text('Hello World'),
            ),
          ),
        );
      }
    }
    

    flutter中大多数东西都是widget,简单点说,可以理解为前端的组件
    它既可以单独存在,也可以父子组件方式嵌套存在
    那么大家有没有想过,组件(Component/Widget)的本质到底是什么?

    时间拉回jQuery年代,那时候非常流行jQuery插件
    比如什么日期插件,分页插件等等
    每一个插件都能输出实现特定的功能的DOM
    那么它是一个组件吗? 是的。

    再回到现在,我们也会写各式各样的Component/Widget去实现各种功能
    这也是一个个组件

    通俗点讲,组件就是 "生成可复用的UI的函数"
    只是他们的输出不同
    一个输出真的DOM,一个输出虚拟VNode

    在MVVM中,VIEWMODEL 能做到自动同步视图与数据,VNode功不可没
    它能保证视图的最小化变更,也能让框架拥有跨平台等等能力
    它始终贯穿组件的整个生命周期

    跨平台

    基于VNode,很多MVVM框架都拥有了跨平台的能力,
    因为它们只需要将VNode生成为对应平台的代码就行了
    Weex和Rn也是这么做的

    但是flutter不同,它直接通过skia引擎进行ui渲染了
    带来的好处就是不需要 jscore 作为中间层进行桥接
    在不同平台上的UI表现差异较少,性能上也会有较大的提升

    Flutter for web 则是重新实现了dart:ui库,用针对DOM和Canvas的代码替换了手机端使用的对Skia引擎的绑定。
    

    组件的种类

    在前端开发中,我们会使用到普通组件和函数式组件
    普通组件拥有自己的状态,生命周期
    而函数式组件则只是单纯输出指定的VNode,且自身不能更改状态
    在flutter中,尤其强调这点

    statelessWidget

    再来看一个简单的 flutter demo

    class CounterDisplay extends StatelessWidget {
      CounterDisplay({this.count});
    
      final int count;
    
      @override
      Widget build(BuildContext context) {
        return new Text('Count: $count');
      }
    }
    
    class CounterIncrementor extends StatelessWidget {
      CounterIncrementor({this.onPressed});
    
      final VoidCallback onPressed;
    
      @override
      Widget build(BuildContext context) {
        return new RaisedButton(
          onPressed: onPressed,
          child: new Text('Increment'),
        );
      }
    }
    
    

    flutter 鼓励大家多使用无状态组件
    其一当然是为了性能考虑,函数式组件在生成以及diff中都有明显的优势
    其二更重要的是我们要养成审视自己代码的习惯,很多组件其实不要一定需要状态管理

    stateFullWidget

    再来看看这个demo

    class Counter extends StatefulWidget {
      @override
      _CounterState createState() => new _CounterState();
    }
    
    class _CounterState extends State<Counter> {
      int _counter = 0;
    
      void _increment() {
        setState(() {
          ++_counter;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return new Row(children: <Widget>[
          new CounterIncrementor(onPressed: _increment),
          new CounterDisplay(count: _counter),
        ]);
      }
    }
    

    眼尖的同学们发现了几个关键字:State,setState,build
    有内味了,好像和我们平时写react差不多嘛

    flutter开发指南 for 前端开发工程师

    但是区别还是有的,stateFullWidget是flutter中的带状态组件
    我们始终要记住Widget是临时对象,可能被调用多次,用于构建当前状态下的应用程序,只是一个配置
    但是State对象在多次调用build()之间试保持不变的,允许它们记住状态

    调用setState告诉Flutter框架,某个状态发生了变化导致应用程序重新运行build方法,以便应用程序可以反映出来更改。
    build则相当于react中返回的jxs或者vue中的template(也就是render函数

    理解了这些之后,就可以朝着组件间的交互往下看了

    状态管理

    所有的MVVM框架组件间通信都遵循这样的逻辑:

    父类通过属性传值给子类  
    子类通过事件传递值给父类
    

    这是最清晰的数据流走向,我们能清晰的知道数据是怎么变更,呈现的
    在flutter中当然也是如此:

    class Parent extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return ParentState();
      }
    }
    class ParentState extends State<Parent> {
      String data = "无";
      Sring props = "some thing";
      
      void onChanged(val){
        setState(() {
          data = val;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return new Center(
          child: new Column(
            children: <Widget>[
              new Container(
                child: new Column(
                  children: <Widget>[
                    new Child(props: props,callBack: (value)=>onChanged(value)),
                    new Text('from child : $data'),
                  ],
                ),
              ),
            ],
          ),
        );
      }
    }
    

    但是现实中,业务永远不会这么简单,很多数据都是需要被共享的
    在MVVM里就是某一个数据变更了,有关联的视图都需要重新构建
    这里天然契合观察者模型
    Redux,Vuex这些状态管理工具都是基于此
    某种程度上说EventBus这种也是归于此,这里不多赘述

    InheritedWidget

    InheritedWidget是Flutter中非常重要的一个功能型组件,
    它提供了一种数据在widget树中从上到下传递、共享的方式

    本质上它就是添加了当前Widget作为依赖,当它的数据有变更时,就会通知所有依赖进行更新
    它的使用较为繁琐,我们来看看基于inheritedWidget实现的ChangeNotifierProvider的demo

     runApp(
        // Provide the model to all widgets within the app. We're using
        // ChangeNotifierProvider because that's a simple way to rebuild
        // widgets when a model changes. We could also just use
        // Provider, but then we would have to listen to Counter ourselves.
        //
        // Read Provider's docs to learn about all the available providers.
        ChangeNotifierProvider(
          // Initialize the model in the builder. That way, Provider
          // can own Counter's lifecycle, making sure to call `dispose`
          // when not needed anymore.
          create: (context) => Counter(),
          child: MyApp(),
        ),
      );
    }
    class Counter with ChangeNotifier {
      int value = 0;
    
      void increment() {
        value += 1;
        notifyListeners();
      }
    }
    return Scaffold(
          appBar: AppBar(
            title: Text('Flutter Demo Home Page'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
             //使用数据
                Consumer(
                  builder: (context, counter, child) => Text(
                    '${counter.value}',
                    style: Theme.of(context).textTheme.display1,
                  ),
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            //操作数据
            onPressed: () =>
                Provider.of(context, listen: false).increment(),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
    
    

    我们使用ChangeNotifierProvider来注册被观察者,使用Consumer注册观察者
    Provider.of(context)方法获得被观察者,处理数据逻辑然后通知观察者重新渲染

    当然你也可以不用像上边一样这么麻烦,直接引用也能做到这点
    前提是你真正了解了自己的意图,只能在特定的场景下使用
    (如果你在正常组件中使用了这些,要审视一下代码,这样做会造成数据流混乱)

    
          ParentState _p = context.findAncestorWidgetOfExactType<ParentState>().data;
          _p.setState(() {
              _p.data = "some thing";
          });
    
          globalState.setState(() {
                  globalState.name = "some thing";
          });
    

    可以通过context获得父组件
    或者你直接把父组件的State赋值到一个全局变量,都能直接修改State,也能顺利更新
    当然这都是不推荐

    复用性

    愉快的上手之后,我们又会发现新的问题来了,
    我们有可能写了很多的组件,但是其中有部分功能是可以共用的
    那怎么将他们复用呢

    我们很容易想到混合组件高阶组件hooks
    那么他们在flutter中能实现呢

    mixin

    这里混合在flutter中是dart语言天然就支持的

    mixin _dataStateMixin < T extends StatefulWidget> on State<T> {
      var _data = 0;
      void _incrementCounter() {
        setState(() {
          _data++;
        });
      }
    
    }
    
    class _CounterState extends State<CounterPage> with _dataStateMixin {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Counter:',
                ),
                Text(
                  '$_counter',
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    

    我们能轻而易举的在dart中创建一个混合类
    将公用的逻辑抽离成mixin复用
    (注意如果有相同的属性会报错)

    hoc

    至于高阶组件这里就有点遗憾了
    我们知道flutter中使用的dart是修改过的,移除了反射
    所以我们动态创建组件是无法实现的,高阶组件暂时也是实现不了的

    但是如果抛开动态创建这个条件,我们使用Builder也能做到一部分逻辑的复用
    和HOC一样,它也有一些缺陷:
    1.并列关系的状态逻辑被组合成了父子关系
    2.多层嵌套会十分难以阅读

    hooks

    如果说上面两种方式都有些许不爽的话,那么你可以尝试下使用Hooks
    hooks可以封装我们组件内不同生命阶段的通用逻辑,来看看官方的demo

    class Example extends StatefulWidget {
      final Duration duration;
    
      const Example({Key key, @required this.duration})
          : assert(duration != null),
            super(key: key);
    
      @override
      _ExampleState createState() => _ExampleState();
    }
    
    class _ExampleState extends State<Example> with SingleTickerProviderStateMixin {
      AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(vsync: this, duration: widget.duration);
      }
    
      @override
      void didUpdateWidget(Example oldWidget) {
        super.didUpdateWidget(oldWidget);
        if (widget.duration != oldWidget.duration) {
          _controller.duration = widget.duration;
        }
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    }
    

    这是一个典型的场景,每个使用AnimationController的组件都不可避免的重复写一些生命周期内的逻辑代码 而使用hooks之后,就变成这样

    class Example extends HookWidget {
      const Example({Key key, @required this.duration})
          : assert(duration != null),
            super(key: key);
    
      final Duration duration;
    
      @override
      Widget build(BuildContext context) {
        final controller = useAnimationController(duration: duration);
        return Container();
      }
    }
    
    

    会明显的感觉到比原先更细粒度的逻辑组织与复用
    (我们在同一个Widget内可以使用多个hooks)

    hooks的功能也不仅局限于此,它是一种新的组织方式,比如:
    useState可以重新组织我们使用State的方式
    useMemoized可以初始化和缓存一些东西
    自定义hooks等等 可以移步这里:github.com/rrousselGit…

    当然还是那句话,hooks也不是万能解药,它只应该是你组织代码逻辑中的一种方案
    平时多审视,优化自己代码的逻辑方是上策

    结语

    今天没有讲太多关于flutter api以及布局等等方面的东西
    只是从组件,框架方面结合前端的一些东西来分享一下自己的看法
    也是当作自己一个小经验总结吧,希望能稍微给到前端开发们一点启发


    起源地下载网 » flutter开发指南 for 前端开发工程师

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元