5.1 库
库是指模块化了的可移植的代码,可以分享给多个项目使用。比如,一个文件读写的代码包含了很多程序中都会用到的通用方法,这一段代码就可以分享给多个程序使用。这时,它就被称为库。在Dart中,任何一个Dart程序都是一个库,如果要把某个标识符封装在库内部,就可以使用下画线(_)开头。
合理地使用库,可以帮助我们简化代码量、降低开发成本,从而将更多的注意力放在具体的业务逻辑上。此外,对于每个有经验的开发者而言,创建一套属于自己的库同样是很有必要的,正所谓避免造重复的轮子。下面我们就来了解一下如何使用库和创建库。
5.1.1 使用库
在前文的例子中,我们已经用到过import关键字。在Dart编程语言中,使用import关键字即可使用一个库。根据要使用的库的来源不同,通常有以下两种写法:
对于Dart内置的库,可以直接按照“import 'dart:xxx'”结构来写。比如,对于文件IO库,写法如下:
对于非Dart内置的库,需要指明具体的路径,结构是“import 'package:xxx'”。比如,在第4章中使用Person类时,代码如下:
接下来,要实例化这两个库中的Calc,实例如下:
此时,发现在new Calc()方法上出现了语法错误。因为Calc在ALib和BLib中同时存在,不知道到底要使用哪个库中的Calc对象。在这种情况下,我们需要给库加一个前缀,以区分两个发生冲突的类。修改包含主方法的类如下:
在进行如上修改后,就可以规避名称冲突的问题了。对比ALib和BLib发现,Alib中多了一个getDesc()方法,这个方法在主方法中并没有用到。在实际开发中,很可能会存在这种类似的情况,即只用到了库的某一部分,这时可以声明。比如,在上例中对于ALib而言,我们只用到了Calc,因此就可以在import时,使用show关键字做如下声明:
而对于用到一个库的大部分,只有少数类没有被用到的情况,可以使用hide关键字排除少数的部分。比如,上次仅用到了Calc,也可以排除Useless,因此,还可以写成:
在上面的例子中,引入的库很小,功能也很简单,甚至没有什么实际的意义。而在实际开发中,一个库的规模可能会很大,或者会引入很多库。在引入的库足够复杂时,可能导致的一个问题就是:初始化过慢。另外,考虑这样一种情况:假定程序需要在某些情况下弹出一个窗口,而这个窗口需要引入才能使用。如果我们从一开始就将其引入进来,而实际上并没有任何窗口弹出,那么这一步将会没有任何意义。
考虑到上述问题,Dart为我们提供了懒加载机制,即延迟载入库。其机制是不用不加载,用时再加载。其方法是在引入时使用deferred as来启用懒加载,并在合适的时候调用loadLibrary()方法来加载。
这里所说的合适的时候并不是单纯地指需要时,设想一下,如果加载一个弹出窗口的库的时间较长,如3秒,那么在需要弹出时再加载,用户就会感到卡顿3秒。这样做只是将加载时的卡顿移到了运行时,意义不大。如果我们能在弹出窗口前就加载的话,就几乎能做到用户“0”等待,这样的体验才是最好的。再回到之前的例子中,我们将BLib库作为延迟加载的库,代码片段如下:
可以看到,在开始的import处使用了deferred as让BLib库延迟加载。在后面的doMultiply()方法中,使用await关键字等待库加载。关于async和await关键字的使用,我们将在下面的小节中讲解。
5.1.2 创建库
知道了如何使用库,接下来就是如何创建库。之前我们已经可以引入自己写的库,虽然能用,但还不算正式,也不算全面。在Dart中,库是通过包的形式创建和分享的。Dart包含两种类型的包:应用包和库包,其中应用包可能包含多个本地库包。一个典型的带有库包的Dart项目目录结构如下:
lib:lib文件夹中包含了众多Dart源代码文件,其中包含一个名为src的子文件夹。在默认情况下,在做导入操作时仅需导入Lib.dart文件即可,因为src文件夹中的文件会被认为是私有的,这是一种高效的导入方式。如果要通过Lib.dart文件导入src文件夹中的所有的源码,就需要在Lib.dart文件中导出(export)src文件夹中所有的方法。具体做法如下:
这样,就可以只导入一个Lib.dart文件,却可以使用src文件夹中所有的功能。
pubspec.yaml pubspec.yaml是项目的配置文件,包含了名字、引入的资源、依赖、SDK版本、字体,甚至国际化等具体的配置信息。这些内容在学习Flutter移动开发时将会用到。