写给前端工程师的 Dart 教程

3,157 阅读6分钟

概述

Dart 是一门强类型、多范式(面向对象、函数式编程)、单线程事件驱动、跨平台(mobile、web、server)的语言。它同时支持 JIT 和 AOT 编译,这使得它具备开发时和运行时的高效率。

Dart 的语法非常简洁现代化,从中可以看到很多 ES6+ 语法的影子。下面我们从 一名前端工程师的角度看看这门语言。

语法

变量声明

dart 中支持 var const final 三种变量声明方式,var 和 Java 中一样支持类型推断。

强类型

Dart 支持编译时和运行时的强类型检查,借助 Dart 的类型推断,我们编码时也可以省略一些类型注解(typescript 和 Java 中也有类似的技术)。强类型使我们的代码更健壮、可读、可维护,并且拥有更高的运行时性能。

和 typescript 一样,Dart 也支持泛型和 any(Dart 中叫 dynamic),也可以通过 as 关键字进行类型转换。

类型系统

在 Dart 中一切皆对象,每个对象都要自己的类(包括 null、function 甚至一些我们 JS 中所谓的基本类型)。每个对象都继承于 Object 类。

和 JS 一样,Dart 也内置了 number string Boolean List Map Set Symbol 类型。不同的是, Dart 的 number 同时支持 intdouble 两种数字类型。另外 Dart 还通过 Runes 类型支持 UTF-32 编码的字符串。

main() {
  var clapping = '\u{1f44f}';
  print(clapping);
  print(clapping.codeUnits);
  print(clapping.runes.toList());

  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));
}

和 typescript 一样,dart 也支持 enum 声明一个 enum

enum Color { red, green, blue }

获取 enum 的 list

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

enum 在 switch 语句中很好用

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // Without this, you see a WARNING.
    print(aColor); // 'Color.blue'
}

函数

之前说了函数在 dart 中也是一种对象,所以函数也可以作为另一个函数的参数。和 JS 一样 Dart 也支持可选参数、默认参数、箭头函数、词法作用域和闭包。

不同的是, dart 中有一个特殊的 main 函数作为整个应用的入口。

操作符

dart 支持 js 中绝大多数的操作符,如数字运算、取余,==!=>=、类型判断(is)、赋值语句、与或非、属性访问符 [] .、可选属性访问符 ?.

另外 Dart 中还有一些语法糖:

JS中的 ||

String playerName(String name) => name ?? 'Guest';

整除,类似 Python 中的 //

assert(5 ~/ 2 == 2); // Result is an int

链式调用 ..

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

控制流

这方面 dart 和 js 差别很小。 if else for while switch等语句都支持,另外支持 assert 语句

异常处理

和 js 一样,dart 支持 throw try catch finally 语句。另外,dart 中用 on 来指定特定错误类型的处理分支

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

面向对象

dart 是一门面向对象的语言,支持单继承和 mixin,也支持 抽象类。

不同的是,dart 中构造方法支持重定向、工厂(好像没啥卵用)

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

另外,dart 中的 class 同时也是一样隐式的 interface,也就说 classAextends 时是 class,被 implements 时是 interface

泛型

和ts一样,dart 中支持泛型,泛型的意义在于兼顾代码复用和类型安全。

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

异步编程

和 js 一样,dart 是一门单线程、事件驱动的语言,非常适合前端和客户端开发。它的单线程模型和 js 很类似:

具体到 dart 中的异步队列,又分为事件队列和微任务队列。可以类比 js 中的宏任务和微任务。

关于异步语法,dart 支持 async await,使用 Future 替代 js 中的 Promise。我们也可以使用 try catch 捕获异步中的错误。

不同的是,Future 只有两种状态 UncompletedCompleted (成功或者报错)。

void printOrderMessage () async {
  try {
    var order = await fetchUserOrder();
    print('Awaiting user order...');
    print(order);
  } catch (err) {
    print('Caught error: $err');
  }
}

Future<String> fetchUserOrder() {
  // Imagine that this function is more complex.
  var str = Future.delayed(Duration(seconds: 4), () => throw 'Cannot locate user order');
  return str;
}

Future<void> main() async {
  await printOrderMessage();
}

和 node 和 Java 一样,dart 中也有 Stream 的概念,比如一个简单的 http server

Future main() async {
  var server = await HttpServer.bind(
    InternetAddress.loopbackIPv4,
    4040,
  );
  print('Listening on localhost:${server.port}');

  await for (HttpRequest request in server) {
    request.response.write('Hello, world!');
    await request.response.close();
  }
}

并发编程

dart 通过 Isolates 来支持并发编程,和浏览器中的 web Worker 线程不同,每个 isolates 拥有独立的内存、事件循环。

装饰器

dart 支持装饰器。

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
}

模块系统

在 dart 中,我们通过 import 来引入模块,

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();

也可以异步加载一个模块,和 js 中的 import() 类似:

import 'package:greetings/hello.dart' deferred as hello;
// 需要调用时
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

loadLibrary 可以多次调用,但模块本身只会加载一次。

标准库

Dart 有非常优秀的标准库,比如

跨平台的标准库:

dart:collection 覆盖了日常开发中的数据结构

dart:convert 编解码、序列化、反序列化

dart:developer 用于 debug 和 检查

dart:math 同 js 中的 math

native 标准库

dart:io 网络请求和io

dart:isolate 并发编程

dart:mirrors 反射

web 中使用的标准库

包含了 html index_db web_gl 等功能

开发工具

dart 提供了很多开发工具来辅助我们日常开发。

模块管理

类似 npm maven ,dart 中提供 pub 来管理依赖,pubspec.yaml 来描述项目的依赖模块。

Command-line

可以用来编译和运行 dart 文件,类似 nodejs。 通过官网安装dart SDK: dart.dev/get-dart

debug

devtools

代码静态分析

dartanalyzer

代码 format

dartfmt

文档生成

dartdoc

自动化测试

package:test/test.dart

总结

Dart 是一门简洁实用、现代化的语言。它吸收了 JavaScript 中的事件驱动思想并提供强大灵活的类型系统,它拥有简洁的并发模型、优秀的标准库和开发工具支持。

Dart 强调实用,并不追求所谓的编程哲学,它吸收了众多语言的优秀思想,并且没有 JavaScript 的历史包袱。我个人比较喜欢它的类型系统、标准库和测试、代码静态分析工具。

它的缺点也很明显,它是一门 ”普通“ 的现代化语言,没什么让 JavaScript 开发者动心的亮点。AOT 编译很好,性能很强大,但 web 应用的性能瓶颈并不在于 JavaScript。在 flutter 爸爸出现之前,它定位是 JavaScript 的替代品,但没有干过 typescript + JS 强大的生态。总的来说,它不是JavaScript