缘起
-
Dart 可以同时在 Mobile/Web/Server 三端跑,很像 nodejs,不过没有 nodejs 那么好命
-
需要一个可以打通 Mobile、Web 的 tk,又不想用 RN的,可以跟着了解一下
目的
-
记录使用过程
- 安装/配置
$ brew tap dart-lang/dart $ brew install dart # 刚发布的复活版 Dart 2.1 $ brew info dart $ dart --version Dart VM version: 2.1.0 (Tue Nov 13 18:22:02 2018 +0100) on "macos_x64" # Dart 的工具包管理工具叫 pub (Flutter 有自己的一套工具管理包): $ pub --version Pub 2.1.0 # 编辑器选用 VS Code,所以把官方的 [Dart 插件](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code)装一下 复制代码
- 安装/配置
-
个人比较喜欢 terminal 风格,所以先从 Server 端开始,首先当然是 hello world,除了第三行 print 的参数之外,简直跟 C 一模一样:
void main() { for (int i = 0; i < 5; i++) { print('hello ${i + 1}'); } } 复制代码
可以直接在 vscode(也可以是 DartPad,如果你能忍的话) 点击 run 运行,当然可以可以直接字符界面下来:
$ dart --enable-asserts test.dart hello 1 hello 2 hello 3 hello 4 hello 5 复制代码
-
如果只是简单看下代码,估计了解一下 Sample 这一页就够了:
- 变量定义:
// 把 var 去掉直接就是 Python ... var name = 'Voyager I'; var year = 1977; var antennaDiameter = 3.7; var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune']; var image = { 'tags': ['saturn'], 'url': '//path/to/saturn.jpg' }; 复制代码
更多变量定义细节在这里
- 控制流:
if (year >= 2001) { print('21st century'); } else if (year >= 1901) { print('20th century'); } for (var object in flybyObjects) { print(object); } for (int month = 1; month <= 12; month++) { print(month); } while (year < 2016) { year += 1; } 复制代码
更多控制流细节在这里
- 函数定义:
int fibonacci(int n) { if (n == 0 || n == 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } var result = fibonacci(20); 复制代码
官方建议每个函数的参数和返回值都显示定义,更适合大型应用协作。
关于函数的一个语法糖是,可以用 => 定义匿名函数(确实有点儿像 PHP),比如下面这个定义了一个简单匿名函数用作
where()
函数的参数,过滤 name 中包含turn
的元素,遍历并针对每个符合条件的元素调用print
flybyObjects.where((name) => name.contains('turn')).forEach(print); 复制代码
关于函数定义的更多细节在这里
-
注释跟 C 语言一样,行注释用
//
,段注释用/* 我是注释 */
, 更高级的用法,比如 doc-testing 之类的细节在这里 -
包管理也是选了
import
关键字,比如调用内置的 libs:
// Importing core libraries import 'dart:math'; // Importing libraries from external packages import 'package:test/test.dart'; // Importing files import 'path/to/my_other_file.dart'; 复制代码
-
类
- 这里有一个类,包含 1 个方法;2 个构造函数;3 个属性;其中的
launchYear
属性展示了如何在 Dart 中配合 getter 函数定义一个只读属性:
class Spacecraft { String name; DateTime launchDate; // Constructor, with syntactic sugar for assignment to members. Spacecraft(this.name, this.launchDate) { // Initialization code goes here. } // Named constructor that forwards to the default one. Spacecraft.unlaunched(String name) : this(name, null); int get launchYear => launchDate?.year; // read-only non-final property // Method. void describe() { print('Spacecraft: $name'); if (launchDate != null) { int years = DateTime.now().difference(launchDate).inDays ~/ 365; print('Launched: $launchYear ($years years ago)'); } else { print('Unlaunched'); } } } 复制代码
Spacecraft
类的用法如下:var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5)); voyager.describe(); var voyager3 = Spacecraft.unlaunched('Voyager III'); voyager3.describe(); 复制代码
关于类的高级内容在这里
- 这里有一个类,包含 1 个方法;2 个构造函数;3 个属性;其中的
-
继承
- Dart 走的是
要聚合不要多继承
的单继承设计思路 - 下面
mixin
部分有更详细介绍,这里先看一眼一个简单的例子:
class Orbiter extends Spacecraft { num altitude; Orbiter(String name, DateTime launchDate, this.altitude) : super(name, launchDate); } 复制代码
- 老规矩,关于 Dart 的继承详情在这里
- Dart 走的是
-
Mixin
- 上面提到
要聚合不要多继承
,基本已经被大多数现代变成语言接受,至于mixin
这个概念基本不用介绍了吧,好像也没有看到那种语言里面有个比较贴切的翻译,还不如直接就用mixin
. - 上例子:
class Piloted { int astronauts = 1; void describeCrew() { print('Number of astronauts: $astronauts'); } } 复制代码
Piloted
这个类就可以作为一个mixin
来用,比如这样,用extends ... with
关键字实现:class PilotedCraft extends Spacecraft with Piloted { // ··· } 复制代码
这样
PilotedCraft
类就不仅有继承自Spacecraft
的方法和属性,也有了来自Piloted
的astronauts
属性和describeCrew
方法。mixin
本身就我的理解来讲,更倾向于把不包含数据的单纯算法进行包装,脱离了数据的纯算法,方便代码复用。 - 上面提到
-
接口和抽象类
- Dart 没有接口的概念,取而代之的是每个类默认的就可以用作一个接口,所以可以直接
实现
任何一个类:
class MockSpaceship implements Spacecraft { // ··· } 复制代码
- 关于显示的使用接口,在这里有详细介绍
- 抽象类是被支持的,抽象类里面当然包含抽象方法(函数体都是空的那种):
abstract class Describable { void describe(); void describeWithEmphasis() { print('========='); describe(); print('========='); } } 复制代码
所有
extends
了Describale
的类都默认拥有describeWithEmphasis
方法,该方法自动调用最终实例化对象自己定义的describe
方法(逻辑如上)。 - Dart 没有接口的概念,取而代之的是每个类默认的就可以用作一个接口,所以可以直接
-
Async 异步
- async/await 这两年好像在语言设计界比较吃香,比如 Python 3.x 里面也用这俩词代表的异步思路解决
callback
嵌套调用带来的麻烦和多任务场景的任务。其他语言,比如 Obj-C 就不,人家用的是block
,嗯嗯高大上的样子... 上例子:
const oneSecond = Duration(seconds: 1); // ··· Future<void> printWithDelay(String message) async { await Future.delayed(oneSecond); print(message); } 复制代码
上面这段翻译成非
async/await
的写法就是这样(不明白_
和Future
之类的 是什么意思没关系,猜一下,基本也是你猜的那个意思:)):Future<void> printWithDelay(String message) { return Future.delayed(oneSecond).then((_) { print(message); }); } 复制代码
再来一个例子说明
async/await
是如何简化异步逻辑,带来更好的代码可读性的:Future<void> createDescriptions(Iterable<String> objects) async { for (var object in objects) { try { var file = File('$object.txt'); if (await file.exists()) { var modified = await file.lastModified(); print( 'File for $object already exists. It was modified on $modified.'); continue; } await file.create(); await file.writeAsString('Start describing $object in this file.'); } on IOException catch (e) { print('Cannot create description for $object: $e'); } } } 复制代码
针对
streams
类的任务场景,Dart 提供了async*
来进一步提高代码的可阅读性:Stream<String> report(Spacecraft craft, Iterable<String> objects) async* { for (var object in objects) { await Future.delayed(oneSecond); yield '${craft.name} flies by $object'; } 复制代码
关于
Future
/Stream
等等 Dart 里面的关键字意思,以及 Dart 是如何解决异步任务场景的思路,详情在这里 - async/await 这两年好像在语言设计界比较吃香,比如 Python 3.x 里面也用这俩词代表的异步思路解决
-
异常处理
- 基本没什么好说的,用
throw
抛异常:
if (astronauts == 0) { throw StateError('No astronauts.'); } 复制代码
- 异常捕捉是通过 try... on/catch... finally 这种,上例子:
try { for (var object in flybyObjects) { var description = await File('$object.txt').readAsString(); print(description); } } on IOException catch (e) { print('Could not describe object: $e'); } finally { flybyObjects.clear(); } 复制代码
- 有一个需要注意的点是,Dart 的异常捕捉是无论异步、同步环境下都生效的,所以,基本上你怎么想的,程序就会怎么跑,不用人去适应机器,大赞。
- Dart 在异常处理上还有些小心思、小设计的,比如
rethrow
和 stack traces 细节等,详情在这里
- 基本没什么好说的,用
-
本文基本是官方 Sample 的翻译,看不懂基本就是翻译的太水,直接去看原文就好 :)