Dart | 浅析dart中库的导入与拆分

前言

最近十分热门的跨平台框架使用了一门比较生僻的编程语言dart。dart语言本身深受早期一些编程语言的影响。特别是Smalltalk,Java和JavaScript。我是从Java语言向dart过度的,一开始感觉很不错,快速就对这门语言有了一个初步的认识,并能够写小段代码了。

但在flutter的不断学习过程中,我遇到了不少因为dart的一些语法而产生的困惑。回过头来看dart的语法之后产生了一些顿悟,在此记录下来希望能帮助到更多的学习者。

你将学到什么

  • 外部库的完全导入与不完全导入
  • 导入新的库对变量命名空间的影响
  • 库的拆分——part

导入

完全导入

我们假定需要在A库里使用B库的某些方法,我们需要将B库import进A库。这是我们十分熟悉的。

import 'B.dart';
复制代码

不仅如此,dart的导入语句还适用于任意url。我们可以使用网络上的某个资源(dart文件)作为外部库导入。

import 'http://hello/hello.dart';
复制代码

但是我们不推荐这样做。因为网络资源随时可能会发生变化。一旦改变,我们的程序将会被破坏。

真正严谨的做法是:

import'package:hello/hello.dart';
复制代码

使用 package:导入方式会执行一个常驻的封装了代码位置信息的包管理器。

一个库可以使用的全部对象包括这个库本身声明的,以及通过导入语句从其他库导入的。在dart:core中定义的对象是隐式导入的。而一个库对外可使用的对象称为库导出的命名空间。

理论上一个库的命名空间中不应有名称相同的两个对象,否则你需要使用别名。

import 'test2.dart'
class Test{
    static final hello = new Test();
}
复制代码
class Test2{
    static final hello = new Test2();
    Test2 _test = new Test2();
}
复制代码

在这段代码中,Test2被import进了Test库,我们在test库中是无法看到Test2的私有变量_test的,所以这个变量将不会被导入到命名空间。

在Dart中,当前库所声明的对象优先级高于任何对象,因此导入的库中有顶层对象并不会有想象中那样具有破坏性。但是如果你访问了一个导入的对象,另外一个导入后续又添加了一个同名对象,那么新导入的对象会覆盖原有对象。

不完全导入

Dart提供了额外的机制来控制导入到库内的对象:命名组合器show和hide。

show

当我们只需要一个庞大库中某一个或某几个(少数)的对象的时候,我们可以选择使用show组合器进行导入。这样可以使你的库更加健壮。

import 'package:math' show Random;
复制代码

在这行代码中,我们只导入了math库中的Random对象。

show组合器接收一个命名空间和一个标识符列表,并将标识符列表中出现的对象保留在命名空间。

hide

当我们在一个库种希望不导入某一个或某几个对象的时候,我们可以使用hide组合器进行导入。

import 'package:math' hide Random;
复制代码

这段代码将导入math库但不导入math库种的Random对象。实现方式与show类似。同样也是接收一个命名空间和标识符列表,并将标识符列表中出现的对象从命名空间中丢弃,然后产生一个新的命名空间。

解决变量名冲突的办法

解决此问题最好的办法是将引入的库加上别名。

import 'package:math' as mymath;
复制代码

通过这种方式我们可以完美避开不同库之间因为导入而使得变量名冲突的问题。

库的拆分

有的时候一个库可能太大,不能方便的保存在一个文件当中。Dart允许我们把一个库拆分成一个或者多个较小的part组件。或者我们想让某一些库共享它们的私有对象的时候,我们需要使用part。

import 'package:json_annotation/json_annotation.dart';

part 'data.g.dart';

@JsonSerializable()
class Data extends Object with _$DataSerializerMixin{
  final String by;
  final int descendants;
  final int id;
  final List<int> kids;
  final int score;
  final int time;
  final String title;
  final String type;
  final String url;

  Data({this.by, this.descendants, this.id, this.kids, this.score, this.time,
    this.title, this.type, this.url});

  factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
}
复制代码
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'data.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Data _$DataFromJson(Map<String, dynamic> json) => new Data(
    by: json['by'] as String,
    descendants: json['descendants'] as int,
    id: json['id'] as int,
    kids: (json['kids'] as List)?.map((e) => e as int)?.toList(),
    score: json['score'] as int,
    time: json['time'] as int,
    title: json['title'] as String,
    type: json['type'] as String,
    url: json['url'] as String);

abstract class _$DataSerializerMixin {
  String get by;
  int get descendants;
  int get id;
  List<int> get kids;
  int get score;
  int get time;
  String get title;
  String get type;
  String get url;
  Map<String, dynamic> toJson() => <String, dynamic>{
        'by': by,
        'descendants': descendants,
        'id': id,
        'kids': kids,
        'score': score,
        'time': time,
        'title': title,
        'type': type,
        'url': url
      };
}
复制代码

这里的data.dart与data.g.dart通过part的头部互相指定它们所在的库。不是所有的库都有名称,但如果使用part来构建库,那么库必须要命名。

Part与import有什么区别

可见性: 如果说在A库中import了B库,A库对B库是不可见的,也就是说B库是无法知道A库的存在的。而part的作用是将一个库拆分成较小的组件。两个或多个part共同构成了一个库,它们彼此之间是知道互相的存在的。

在上述例子中,我们可以看到,part data.dart中Data类中调用了part data.g.dart的DataFromJson(json)方法,而第二个part中构建DataFromJson(json)方法返回Data对象并没有在part data.g.dart定义。它们彼此之间是共用的命名空间。并且私有对象也是可以互相访问的。

part也可以通过url指明它所在的库,但是我们同样也是不推荐这样做的。

写在最后

以上就是Dart中库的导入与拆分的全部内容,若有描述不准确或错误之处欢迎各位大牛指正!

之后我会更新一系列flutter干货,如果你喜欢的话可以关注我或者给我一个好评哦!我会更新更有动力的。

猜你喜欢

转载自juejin.im/post/5b601f40e51d4519575a5036