Flutter小技巧总结之dart/flutter 中的代码规范

一,概述

  官方规范文档

  正文文档中图片的说明

  • 绿色部分为正例,右上角带good标识。
  • 红色是反例,右上角带bad标识

二,规范总结

  • 标识方案

 

    • 在dart有3种常规标识方案
      • 第一个为大写字母开头的驼峰式 如 UserInterface每个词的首字母为大写
      • 第二个是小写开头的驼峰式,如testRun,第一个单词是小写,后续每个单词首字母大写
      • 第三个是每个单词均为小写,以下划线分隔,如user_response
    •  项目中的常规用法
      • 文件名: 小写+下划线

      • 类型名(类名,函数类型名):大写开头驼峰

      • 变量名(包含const final 常量):使用小写开头驼峰, 项目有特殊要求 const可以使用大写+下划线的方式,如同java中一样

      • 导包as后的名称为小写+下划线

      • 不要用匈牙利命名法中的kXXXX 这样的命名方式,应该去掉k

      • 超过两位的英文缩写一律按该单词为普通小写单词处理,使用小写

      • 导包有顺序要求,且每"部分"间空行分隔开,每部分内按字母排序,按如下顺序排序

        • dart sdk内的库

        • flutter内的库

        • 第三方库

        • 自己的库

        • 相对路径引用

 

        • 先全部import再export,不要交替进行
      • 单行字符建议不要超过80个
      • 流程控制语句无论如何都使用花括号包裹,即使只有单行
  • 类型名称

    • 适用于类名,注解名,typedef定义的函数名
    • 这里有一个特例,当你的注解是一个const的常量时,使用@foo的方式作为注解
  • 库名称,文件名用小写+下划线

 
    • 使用小写+下划线方式命名library,文件名
    • 原因如下:
      某些文件系统不区分大小写,因此许多项目要求文件名全部为小写。使用分隔字符允许名称仍以该形式可读。使用下划线作为分隔符可确保名称仍然是有效的Dart标识符
  • 导入时

 
 
    • 当导入包时, 如果涉及到as, 一律使用小写+下划线方式

  • 其他标识符
    • 包含顶级成员,类成员,方法内成员,参数名,命名参数名,一律使用小写驼峰式

  • 常量名称

    • 建议使用小写驼峰式命名
    • 但是你的项目中如果使用大写+下划线分割单词的方式,则可以继续使用这种方式
    • 这里有个小说明:最初dart中采用的大写+下划线方式,但是后来有一些变量需要修改为非const变量,就需要修改为小写驼峰式,后一律使用小写驼峰式
  • 缩写相关
 
    • 超过两位的使用常规方式,两位以内使用大写
    • 在小写驼峰式中,会出现一些约定俗成的缩写,如httpftpio等,这些在英文词法中都应该是大写,但大写连续会破坏可读性,如HTTPSFTP,你不知道是HTTPS FTP 还是 HTTP SFTP,所以采用如上图绿色的方式来命名

  • 不要使用前缀字母
 
    • 匈牙利命名法中使用缩写开头的小写驼峰命名法时,是因为当时编辑器无法很好帮助理解你的代码,这样命名法能够很好的帮助你去理解代码,现在dart语言中,编辑器可以很好的通过声明等方式帮你理解你的代码,故,不要使用前缀字母,如上图绿色
  • 规定代码的顺序
    为了使文件保持整洁,我们有一个规定代码顺序。每个“部分”应该用空行分隔。

 


 

 
    • 保证dart的导入顺序在所有其他包之前
 

    • 保证带包名的引用方式在相对路径引用之前


 
    • 首先import第三方的包,
      导入自己的包在后面一个单独的部分


 
    • 全部导入完毕后,再export 导出

    • 每个部分按字母顺序排序
  • 流程控制使用花括号
 
  • 异步
    • 优先使用async/await代替原始的futures
      async/await语法提高了可读性,允许你在异步代码中使用所有Dart控制流结构。
        Future<int> countActivePlayers(String teamName) async {
          try {
            var team = await downloadTeam(teamName);
            if (team == null) return 0;
        
            var players = await team.roster;
            return players.where((player) => player.isActive).length;
          } catch (e) {
            log.error(e);
            return 0;
          }
        }
    • 当异步没有任何用处时,不要使用它
      如果可以在不改变函数行为的情况下省略异步,那么就这样做。
       Future afterTwoThings(Future first, Future second) {
          return Future.wait([first, second]);
        }

      不推荐写法:

       Future afterTwoThings(Future first, Future second) async {
          return Future.wait([first, second]);
        }
  • 不要使用new

       Dart2使new 关键字可选

    • 推荐写法:

        Widget build(BuildContext context) {
          return Row(
            children: [
              RaisedButton(
                child: Text('Increment'),
              ),
              Text('Click!'),
            ],
          );
        }
    • 不推荐如下写法:

        Widget build(BuildContext context) {
          return new Row(
            children: [
              new RaisedButton(
                child: new Text('Increment'),
              ),
              new Text('Click!'),
            ],
          );
        }
  • 构造函数
    尽可能使用初始化的形式

    • 不推荐如下写法:
       class Point {
          num x, y;
          Point(num x, num y) {
            this.x = x;
            this.y = y;
          }
        }
    • 推荐如下写法:
      class Point {
        num x, y;
        Point(this.x, this.y);
      }
  • 优先使用final字段来创建只读属性
     尤其对于 StatelessWidget,在不需要的时候不要用this

    • 不推荐如下写法:

      class Box {
          var value;
          
          void clear() {
            this.update(null);
          }
          
          void update(value) {
            this.value = value;
          }
        }
    • 推荐如下写法:

      class Box {
          var value;
        
          void clear() {
            update(null);
          }
        
          void update(value) {
            this.value = value;
          }
        }
  • 类成员
      不要把不必要地将字段包装在getter和setter中
    • 不推荐如下写法:
      class Box {
          var _contents;
          get contents => _contents;
          set contents(value) {
            _contents = value;
          }
        }
  • 避免储存你能计算的东西
    在设计类时,您通常希望将多个视图公开到相同的底层状态。通常你会看到在构造函数中计算所有视图的代码,然后存储它们:
    • 应该避免的写法:
       class Circle {
          num radius;
          num area;
          num circumference;
        
          Circle(num radius)
              : radius = radius,
                area = pi * radius * radius,
                circumference = pi * 2.0 * radius;
        }

      如上代码问题:

       

      • 浪费内存

      • 缓存的问题是无效——如何知道何时缓存过期需要重新计算?

    • 推荐的写法如下:
      class Circle {
          num radius;
        
          Circle(this.radius);
        
          num get area => pi * radius * radius;
          num get circumference => pi * 2.0 * radius;
        }
  • 变量
    不要显式地将变量初始化为空
    • 在Dart中,未显式初始化的变量或字段自动被初始化为null。不要多余赋值null
      int _nextId;
        
        class LazyId {
          int _id;
        
          int get id {
            if (_nextId == null) _nextId = 0;
            if (_id == null) _id = _nextId++;
        
            return _id;
          }
        }

       

    • 不推荐如下写法:

      int _nextId = null;
        
        class LazyId {
          int _id = null;
        
          int get id {
            if (_nextId == null) _nextId = 0;
            if (_id == null) _id = _nextId++;
        
            return _id;
          }
        }
  • 参数的使用
    • 使用=将命名参数与其默认值分割开
      • 由于遗留原因,Dart均允许“:”和“=”作为指定参数的默认值分隔符。为了与可选的位置参数保持一致,使用“=”。

        void insert(Object item, {int at = 0}) { ... }
      • 不推荐如下写法:

        void insert(Object item, {int at: 0}) { ... }
    • 不要使用显式默认值null
      • 如果参数是可选的,但没有给它一个默认值,则语言隐式地使用null作为默认值,因此不需要编写它

        void error([String message]) {
           stderr.write(message ?? '\n');
        }
      • 不推荐如下写法:

        void error([String message = null]) {
          stderr.write(message ?? '\n');
        }
  • 不要使用List.from(),除非打算更改结果的类型
    • 给定一个迭代,有两种明显的方法可以生成包含相同元素的新列表
      var copy1 = iterable.toList();
      var copy2 = List.from(iterable);
    • 明显的区别是第一个比较短。重要的区别是第一个保留了原始对象的类型参数
      // Creates a List<int>:
      var iterable = [1, 2, 3];
      
      // Prints "List<int>":
      print(iterable.toList().runtimeType);
      
      // Creates a List<int>:
      var iterable = [1, 2, 3];
      
      // Prints "List<dynamic>":
      print(List.from(iterable).runtimeType);
  • 避免使用带有函数字面量的Iterable.forEach()
    • 在Dart中,如果你想遍历一个序列,惯用的方法是使用循环。

      for (var person in people) {
        ...
      }
    • 不推荐如下写法:

      people.forEach((person) {
        ...
      });
  • 考虑使用高阶方法转换序列
    如果有一个集合,并且希望从中生成一个新的修改后的集合,那么使用.map()、.where()和Iterable上的其他方便的方法通常更短,也更具有声明性
    var aquaticNames = animals
    .where((animal) => animal.isAquatic)
    .map((animal) => animal.name);
  • 尽可能使用集合字面量
    • 如果要创建一个不可增长的列表,或者其他一些自定义集合类型,那么无论如何,都要使用构造函数。
      var points = [];
      var addresses = {};
      var lines = <Lines>[];
    • 不推荐如下写法:
      var points = List();
      var addresses = Map();
  • 不要使用.length查看集合是否为空
    • 推荐写法
      if (lunchBox.isEmpty) return 'so hungry...';
      if (words.isNotEmpty) return words.join(' ');
    • 不推荐如下写法:
      if (lunchBox.length == 0) return 'so hungry...';
      if (!words.isEmpty) return words.join(' ');
  • 使用相邻字符串连接字符串文字
        如果有两个字符串字面值(不是值,而是实际引用的字面值),则不需要使用+连接它们。就像在C和c++中,简单地把它们放在一起就能做到。这是创建一个长字符串很好的方法但是不适用于单独一行。
    • 推荐写法
      raiseAlarm(
         'ERROR: Parts of the spaceship are on fire. Other '
         'parts are overrun by martians. Unclear which are which.'
      );
    • 不推荐写法:
      raiseAlarm(
      'ERROR: Parts of the spaceship are on fire. Other ' +   'parts are overrun by martians. Unclear which are which.'
      );
  • 优先使用模板字符串
    'Hello, $name! You are ${year - birth} years old.';
    • 在不需要的时候,避免使用花括号

      'Hi, $name!'
      "Wear your wildest $decade's outfit."
    • 不推荐如下写法:

      'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';
    • 不推荐如下写法:

      'Hi, ${name}!'
      "Wear your wildest ${decade}'s outfit."
  • 注释

    • 要像句子一样格式化
      除非是区分大小写的标识符,否则第一个单词要大写。以句号结尾(或“!”或“?”)。对于所有的注释都是如此:doc注释、内联内容,甚至TODOs。即使是一个句子片段。

      • 不推荐写法  

        greet(name) {
          // Assume we have a valid name.
          print('Hi, $name!');
        }
      • 不推荐如下写法:

          greet(name) {
            /* Assume we have a valid name. */
            print('Hi, $name!');
          }

        可以使用块注释(/…/)临时注释掉一段代码,但是所有其他注释都应该使用//

  • Doc注释
    • 使用///文档注释来记录成员和类型。
      使用doc注释而不是常规注释,可以让dartdoc找到并生成文档。
      /// The number of characters in this chunk when unsplit.
      int get length => ...

      由于历史原因,达特茅斯学院支持道格评论的两种语法:///(“C#风格”)和/… /(“JavaDoc风格”)。我们更喜欢/// 因为它更紧凑。
      /和/在多行文档注释中添加两个无内容的行。在某些情况下,///语法也更容易阅读,例如文档注释包含使用*标记列表项的项目符号列表。

  • 考虑为私有api编写文档注释
    Doc注释并不仅仅针对库的公共API的外部使用者。它们还有助于理解从库的其他部分调用的私有成员
    • 用一句话总结开始doc注释
      以简短的、以用户为中心的描述开始你的文档注释,以句号结尾。
      /// Deletes the file at [path] from the file system.
      void delete(String path) {
       ...
      }
      • 不推荐如下写法:
        /// Depending on the state of the file system and the user's permissions,
        /// certain operations may or may not be possible. If there is no file at
        /// [path] or it can't be accessed, this function throws either [IOError]
        /// or [PermissionError], respectively. Otherwise, this deletes the file.
        void delete(String path) {
        ...
        }
    • “doc注释”的第一句话分隔成自己的段落
      在第一个句子之后添加一个空行,把它分成自己的段落
      /// Deletes the file at [path].
      ///
      /// Throws an [IOError] if the file could not be found. Throws a
      /// [PermissionError] if the file is present but could not be deleted.
      void delete(String path) {
      ...
      }

 

posted on 2019-08-18 05:34  梁飞宇  阅读(1350)  评论(0)    收藏  举报