★Dart-1-Hello Dart

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了★Dart-1-Hello Dart相关的知识,希望对你有一定的参考价值。

1.什么是Dart?

Dart是一种开源的结构化编程语言,用于创建复杂的基于浏览器的web应用程序。您可以使用直接支持Dart代码的浏览器或将Dart代码编译为javascript来运行在Dart中创建的应用程序。Dart有一个熟悉的语法,它是基于类的、可选类型的和单线程的。它有一个称为隔离(isolates)的并发模型,允许并行执行,我们将在第15章中讨论。除了在web浏览器中运行Dart代码并将其转换为JavaScript之外,您还可以在托管的命令行上运行Dart代码。除了在web浏览器和将其转换为JavaScript,您还可以在Dart虚拟机中托管的命令行上运行Dart代码,允许应用程序的客户端和服务器部分使用相同的语言进行编码。

这是一个很小的Dart脚本,包含一个名为main的函数:

main() {
	var d = "Dart";
	String w = "World";
	print("Hello ${d} ${w});
}

此脚本可以嵌入html页面的<script type=“application/dart”>,并在Dartium浏览器(谷歌Chrome web浏览器的Dart开发者版)中运行。您可以使用dart2js工具将其转换为JavaScript,以便在所有现代浏览器中运行,或者使用Dart虚拟机可执行文件直接从服务器端命令行运行脚本。

图1.1显示了工具生态系统,其中包括多个运行时环境、语言和编辑器工具以及综合库,所有这些都是为了在构建复杂web应用程序时改进开发人员的工作流程而设计的。

Dart开发人员的一个关键工具是Dartium,它允许您编写或编辑Dart代码,并通过加载文件和刷新浏览器来查看代码的运行情况。当Dartium与Dart编辑器结合使用时,您将获得往返调试的额外好处。

为了帮助开发人员实现Java/C#/JavaScript语言的多样性,Dart有一个可选的类型特性,它允许开发人员完全不指定类型(如在JavaScript中使用var关键字),或者到处使用类型注释(如String、int、Object),或者混合使用这两种方法。
表1.1提供了Dart、Java和JavaScript之间的一些比较。

Dart是一种通用语言,与JavaScript或Java一样,您可以使用它构建许多不同类型的应用程序。不过,当您构建复杂的web应用程序时,Dart确实非常出色。

2.单页应用程序体系结构

单页应用程序GoogleMail、GoogleInstant Search和GoogleMaps是Dart设计用于构建的典型web应用程序类型。整个应用程序(或至少应用程序主要部分的所有用例)的源代码由单个网页加载。该源代码在浏览器中运行,负责构建UI并从服务器请求数据以填充该UI,如图1.2所示。

单页应用程序使用快速客户端虚拟机将处理从服务器移动到客户端。这允许您的服务器处理更多请求,因为构建布局所涉及的处理被移动到客户机上。通过使用Dart的HTML库结合现代HTML5浏览器存储和缓存技术,应用程序还可以在浏览器中缓存数据,进一步提高应用程序性能,甚至允许用户脱机工作。

每个Dart脚本都有一个名为main()的入口点函数,它是Dart VM执行的第一个函数。因此,当调用主函数main()时,您可以依赖于定义应用程序的所有代码;您不能像使用JavaScript一样在运行的代码中定义和执行函数,因为执行代码中没有eval()或其他修补程序。此单一功能可帮助您编写适合单页应用程序体系结构的Dart应用程序,因为您可以确保您的代码将作为单个已知的代码单元执行。Dart虚拟机使用此功能提高应用程序启动时间,使用堆快照加载应用程序的速度比同等的JavaScript应用程序快得多。

记住:
■ Dart是一种用于web开发的语言,具有熟悉的语法。
■ Dart的工具生态系统比同等的动态语言提供更高的生产率。
■ Dart的可选类型系统在JavaScript的动态类型和Java的静态类型之间架起了桥梁。
■ 通过允许工具验证源代码,类型注释可以极大地帮助开发团队之间的开发过程。
■ Dart是开发单页web应用程序的理想选择。

3.字符串插值(String interpolation)

字符串在web应用程序中的许多地方都使用。Dart为您提供了许多将表达式转换为字符串的方法,可以通过内置于基本Object类中的toString()函数,也可以使用字符串插值。

字符串插值在单引号或双引号内使用$字符或${ }表达式。当要将表达式转换为字符串时,可以使用带有 前 缀 的 变 量 名 , 例 如 前缀的变量名,例如 name。如果要使用需要计算的表达式(例如计算或方法调用),请使用大括号:"The name is ${5 + 10}"

可以使用三个双引号创建多行字符串;您可以通过在字符串前面加上r字符以使不解析类似 的 运 算 , 从 而 直 接 输 出 引 号 内 文 本 。 一 起 情 况 必 须 使 用 字 符 串 插 值 , 例 如 的运算,从而直接输出引号内文本。 一起情况必须使用字符串插值,例如 使forename $surname,或者,如果它们是已知的字符串值,则将它们放在彼此相邻的位置,如:var title = "Dart " "in " "Action";,它产生单条字符串"Dart in Action"。

下面的列表显示了使用Dart的内置打印(print)功能可以对字符串执行的操作,该功能在浏览器中运行时输出到标准输出、服务器端或浏览器调试控制台。

import 'package:flutter/material.dart';

void main() {
	var h = "Hello";
	final w = "World";
	print('$h $w');// 计算简单的变量并字符串输出
	print(r'$h $w');// 不计算直接原样输出
	
	var helloworld = "Hello " "World";// 相邻的字符串常量是串联的
	print(helloWorld);

	print("${helloWorld.toUpperCase()}");// 计算需要在${ }里面进行
	print("The answer is ${5 + 10}");

	var multiline = """abc// 三个双引号相当于HTML中的标签<pre>,即按未加工的raw格式输出(包括单引号、双引号)
<div id='greeting'>
		"Hello World"
	</div>""";
	print(multiline);

	var o = new Object();
	print(o.toString());
	print("$o");// 字符串插值自动调用toString()方法
}

4.可选的类型

JavaScript和Dart之间的一个关键区别是,Dart在语言中加入了类型的概念。幸运的是,通过使用Dart的可选类型(option typing),您可以通过使用类型注释的地方获得强类型的好处。
可选类型注释用于变量声明、函数参数定义和返回类型以及类定义中。下面的代码段显示了声明字符串变量消息的四种方法。前两个没有类型注释,后两个提供字符串类型注释,向开发人员和工具表明您希望在变量中使用字符串值:

var messageA;// 未注释类型
var messageB = "Hello Dart";// 同上

String messageC;// 提供了类型注释
String messageD = "Hello Dart";// 同上

在前面的代码段中,两个变量声明在声明消息时初始化消息的值。如果该值在声明后不会更改,则应使用final关键字,如下所示:

final messageE = "Hello Dart";// 不可再更改
final String messageF = "Hello Dart";// 同上

作为一个例子,您可以从使用可选的类型中获益,考虑下面的代码块,它具有trueIfNull(a,b)函数,它使用两个参数,如果两者都为NULL,则返回true。此代码目前没有类型注释,但我们将解释如何使用类型注释来显示意图:

trueIfNull(a, b) {// a、b和返回类型没有提供类型注释
	return a == null && b == null;
}

main() {
	final nums = trueIfNull(1, 2);// nums没有提供类型注释
	final strings = trueIfNull("hello", null);// strings没有提供类型注释
	print("$nums");// 打印:false
	print("$strings");// 打印:false
}

代码段中没有提供类型注释,这意味着在阅读此代码时,您不知道开发人员的意图。trueIfNull(a,b)函数可能意味着trueIfNull(a,b)应采用两种int类型并返回bool,但开发人员可能还打算使用其他方法,例如,返回字符串"true"而不是bool。Dart的可选类型允许开发人员以类型信息的形式提供有关参数和返回类型的文档。

下面是都加上类型注释后的代码:

bool trueIfNull(int a, int b) {
	return a == null && b == null;
}

main() {
	final bool nums = trueIfNull(1, 2);
	final bool strings = trueIfNull("hello", null);
	print("$nums");
	print("$strings");
}

上面示例包含bool类型。与JavaScript不同,Dart中只有一个假值:关键字false本身的值。零和null的计算结果不为false。

5.传统的基于类的结构

Dart以传统的、毫不奇怪的面向对象方式使用类和接口。它支持单继承和多接口。在这一点上,只要指出Dart的OO模型类似于Java/C#,而不类似于JavaScript就足够了。我们将在第6章和第7章更深入地了解类及其特性。

默认情况下,所有Dart类都继承自Object。它们可以有公共成员和私有成员,并且一个有用的getter和setter语法允许您将字段与属性互换使用而不影响类用户。下面清单显示了一个类的快速示例。

class Greeter {
	var greeting;// public property
	var _name;// private property

	sayHello() => "$greeting ${this.name}";// public method

	get name => _name;// getter method for _name
	set name(value) => _name = value;// setter method for _name
}
main() {
	var greeter = Greeter();
	greeter.greeting = "Hello";
	greeter.name = "World";
	print(greeter.sayHello());// Hello World
}

私有成员通过在名称前面加上下划线字符来表示。Dart中没有类似public private protected这样的访问修饰符,Dart中在属性或者方法名前加下划线“_”就表示私有的。
必须使用关键字“const”、“final”、“var”或类型名声明变量。
私有属性/方法必须要抽离出单独文件,否则不会生效。

class Rect {
  num height;
  final num _width;// 私有属性

  Rect(this.height, this._width);

  num _getW() {// 私有方法
    return _width;
  }

  get area {
    return height * _width;
  }

  set areaHeight(value) {
    height = value;
  }
}

void main() {
  Rect r = Rect(10, 4);

  print(r._width);// 同一文件中,私有属性不生效,非同一文件中不可访问私有成员
  print(r._getW());// 同一文件中,私有方法不生效,非同一文件中不可访问私有成员

  print(r.area);// 调用get area
  r.areaHeight = 6;// 调用set areaHeight
  print(r.area);// 调用get area
}

4
4
40
24

getter和setter语法也很有用,因为可以像使用getter和setter一样使用类的字段。因此,类设计器可以公开属性(如上面清单中的greeting)并在以后将其更改为使用getter和setter(如示例中的name),而无需更改调用代码。

this关键字在JavaScript世界中引起了很多误解,它也以传统的OO方式使用。它指的是类本身的特定实例,而不是在任何给定时间点的类所有者(如JavaScript中)。

与Java和C#不同,Dart中的类是可选的。您可以编写存在于顶层作用域中的函数,而不必成为类的一部分。换句话说,您不需要为了声明函数而声明类。如果您发现正在编写包含实用程序方法的类,则可能不需要类。相反,您可以使用Dart的顶层作用域函数。

6.隐含的接口定义

Dart具有与Java和C#类似的接口,但在Dart中,使用类结构定义接口。这是基于所有类都在其公共成员上定义了一个隐式接口。下面清单定义了一个名为Welcomer的类和一个需要Welcomer实例的顶层sayHello()函数。除了使用extends关键字实现Java和C#中的继承体系外,还可以使用implements关键字在每个类上定义的接口。Greeter类实现Welcomer的公共方法,这允许使用它代替Welcomer实例。这使您能够集中精力针对类的接口而不是特定的实现进行编程。

class Welcomer {
	printGreeting() => print("Hello ${name}");
	var name;
}
class Greeter implements Welcomer {
	printGreeting() => print("Greeting ${name}");// 根据接口规范,这是必须实现的方法
	var name;
}
void sayHello(Welcomer welcomer) {
	welcomer.printGreeting();
}
main() {
	var welcomer = Welcomer();
	welcomer.name = "Tom";
	sayHello(welcomer);

	var greeter = Greeter();
	greeter.name = "Jerry";
	sayHello(greeter);
}
Hello Tom
Greeting Jerry
7.工厂构造函数提供默认实现

关键字factory表明是一个工厂构造函数

abstract class IGreetable {// 定义接口
  String sayHello(String name);// 提供必须实现的方法
  factory IGreetable() => Greeter();// 返回实现了接口的实例的工厂构造函数
}
class Greeter implements IGreetable {// 接口实现者
  @override
  sayHello(name) =>"Hello $name";// 实现接口方法
}
void main() {
  IGreetable myGreetable = IGreetable();// 创建实现了接口的实例
  var message = myGreetable.sayHello("Dart");// 使用接口实现
  print(message);// Hello Dart
}

由于这种能力,需要注意的是,许多核心类都是接口,例如Stringint,它们具有使用工厂构造函数提供的特定实现类。我在本书的第2部分详细介绍了类、接口及其与可选类型系统的交互。

8.库和范围

Dart能够将源代码文件分解为逻辑结构。可以在一个.dart文件中编写整个Dart应用程序,但这样做并不能实现出色的代码导航或组织。为了解决这个问题,Dart将库烘焙到该语言中。Dart中的库是源代码文件的集合,这些源代码文件可能是单个文件,但已被拆分以帮助人类与代码交互。

我前面提到过,在Dart中类是可选的。这是因为函数可以位于库的顶层范围内。在Dart中,库是以逻辑方式组合在一起的一个或多个.dart文件;每个文件可以包含零个或多个类和零个或多个顶层函数。Dart库还可以导入其自身代码使用的其他Dart库。

库使用library关键字定义,使用import导入其他库,并使用part引用其他源文件,如下清单所示。

library my_library;// 定义文件是一个库,库ID为my_library

import "../lib/my_other_library.dart";// 导入其他目录中的库

part "greeter.dart"// 包含其他源文件
part "leaver.dart";

greetFunc() {// 在顶层(top-level)库作用域中定义函数
    var g = new Greeter();// 使用greeter.dart文件中的类
    sayHello(g);// 调用my_other_library的顶层作用域中的函数
}


为了避免命名冲突,你可以使用关键字as。例如当您正在编写的库和正在导入的库都包含名为Greeter的类时,可以应用库前缀,例如

import "../lib/my_other_library.dart" as other;// other是my_other_library库的别名

然后使用下面的形式引用该库中的类:

other.otherLibFunction("blah");// 调用other库中方法otherLibFunction,参数为"blah"

这样就可以确保合理地命名类和方法,而不必担心弄脏全局名称空间。

库可以构成应用程序的入口点,但如果是,则必须具有main()函数。您可以有多个库,每个库都有自己的main()函数,但它是执行的

库中的任何函数或类都可供库的任何导入者使用;也就是说,它们是公开的。要阻止库的导入程序使用特定函数或类,请将它们标记为私有的。

Dart提供了一种简单的私有化方法:在方法、类或属性的名称前加下划线(_)。这样做会使项目库成为私有的,或在库的范围内成为私有的。

class _Rect {// 私有[类]
    num _width;// 私有[属性]
    num _getW() => _width;// 私有[方法]
}

在我们正在进行的示例中,如果某个东西用下划线标记为private,这意味着如果类Greeter和类Leaver在同一个库中,它们可以访问彼此的私有元素(类似于Java中的package private)。这还意味着可以从同一库中的任何其他类访问属性或函数_greeerpivate()。但当通过另一个库导入Greeter时,它对另一个库不可见,如图1.4所示。
私有元素可以包括顶层(库)函数、类、类字段、属性和方法(称为成员)以及类构造函数。在库中,隐私被忽略,因此,如果库将其他零件文件中的私有元素带入同一个库中,则任何零件文件都可以访问这些私有元素。库的用户无法访问该库的任何私有元素(或该库中类的私有元素)。

9.作为first-class对象的函数

您可以在Dart中将函数作为对象传递,传递方式与JavaScript中类似:您可以将函数作为参数传递,将函数存储在变量中,并将匿名函数用作回调。

String sayHello(name) => "Hello $name";
main() {
    var myFunc = sayHello;// 将函数名赋值给变量
    
    print(myFunc("World"));
    
    var anonymousSumFunc = (a,b) => a+b;// 定义一个匿名函数

    var c = anonymousSumFunc(1, 2);// 调用匿名函数
    print(c);
}

下面两个函数是完全相同的:

String sayHello(name) {
    return "Hello $name";
}
||
String sayHello(name) => "Hello $name";
10.isolate并发

Dart是一种单线程语言。尽管这种设计可能与当前的硬件技术不一致,因为应用程序可以使用越来越多的处理器,但这意味着Dart有一个简单的模型来进行编码。

在Dart中,isolate(隔离)(而不是线程或进程)是工作单元。它有自己的内存分配(不允许在隔离之间共享内存),这有助于提供隔离的安全模型。每个隔离可以将消息传递给另一个隔离。当隔离接收到一条消息(可能是一些要处理的数据)时,事件处理程序可以以类似于处理用户单击按钮时的事件的方式处理该消息。在隔离中,当您传递消息时,接收隔离将获得从发送隔离发送的消息的副本。对接收数据的更改不会反映在发送端;你需要再发另一条信息回去。

在网页中,每个单独的脚本(包含一个main()函数)都在其自己的隔离区中运行。您可能有用于应用程序不同部分的脚本,例如一个用于新闻提要,一个用于脱机同步,等等。Dart代码可以从运行的代码中产生一个新的隔离,其方式类似于Java或C#代码启动新线程的方式。隔离与线程的不同之处在于隔离有自己的内存。无法在隔离之间共享变量,隔离之间通信的唯一方式是通过消息传递。

隔离还用于动态加载外部代码。您可以提供应用程序核心代码之外的代码,这些代码可以加载到自己的内存保护空间中,并独立于应用程序运行,通过消息传递进行通信。此行为非常适合创建插件体系结构。

如果需要,Dart VM实现可以使用多个内核来运行隔离。当隔离代码转换成JavaScript时,他们就变成了HTML5 Web工作者。

记住:
■ Dart具有可选(或由文件组成的)类型。
■ 库帮助您分解源文件并组织代码。
■ 隐私是语言的一部分。
■ 函数是顶层的,可以不带类而存在。
■ Dart使用消息传递隔离来理解并发性。

11.Web编程

Dart的目标之一是改善开发人员的生活。因为Dart最终是一种web编程语言,所以我们花了大量的精力将浏览器DOM操作API转化为一种使用起来很有趣的东西。在JavaScript中,在出现jQuery之前,访问浏览器DOM是一件烦琐的事情,这让使用浏览器感觉很自然。类似地,编写dart:html库是为了简化在dart中编写浏览器代码。

在撰写本文时,没有可用于Dart的UI小部件库。尽管Dart团队已经公开表示,他们希望Dart是一种“包含电池”的语言,但Dart的早期公开发布意味着,他们需要花一些时间使该语言在焦点转移到更高级别的抽象之前能够完美地工作。但是他们已经以dart:html的形式构建了可以被认为是jQuery核心的等价物。

如果您使用过像jQuery这样的框架,那么您将熟悉使用CSS选择器访问DOM元素,例如id="myDiv"的div或所有

元素。dart:html库使这项工作变得简单。与使用本机DOM API时一样,dart:html没有包含许多不同的获取元素的调用,例如getElementsById()和getElementsByName(),而是只有两种选择元素的方法querySelector()返回单个元素,querySelectorAll()返回元素列表。由于dart:html库使用dart列表,因此可以使用所有标准列表函数,如contains()isEmpty(),以及数组语法,如element.children[0]

下面的列表显示了通过dart:html库与DOM的一些交互:

import 'dart:html';// 导入dart:html库
void main() {
  var button = ButtonElement();// 创建一个新button元素
  button.text = "Click me";
  button.onClick.listen(// 添加匿名函数
          (e) {
        List buttonList = querySelectorAll("button");
        window.alert("There is ${buttonList.length} button");
      }
  );
  document.body!.nodes.add(button);// 将button添加到HTML body
}

编写浏览器代码时,请记住,print(“Hello World”)中使用的print函数将输出发送到浏览器控制台,而不是页面。

正如您可以直接与浏览器元素交互一样,dart:html库还公开HTML5元素,如画布canvas、WebGL、设备运动事件和地理位置信息。
图1.6所示的输出由下面清单中的代码生成,该代码使用HTML5画布API。
将此输出绘制到画布的Dart代码添加了一个HTML5
标记到浏览器DOM,然后使用它获取二维图形上下文。然后,Dart代码将文本和形状写入图形上下文。

import 'dart:html';
import 'dart:math';
void main() {
    CanvasElement canvas = CanvasElement();
    canvas.height = 300;
    canvas.width = 300;
    document.body!.nodes.add(canvas);
    CanvasRenderingContext2D _2d = canvas.context2D;
    _2d.fillText("hello canvas", 10, 10);
    _2d.beginPath();
    _2d.arc(50, 50, 20, 0, pi * 2, true);// arc(x, y, radius, startAngle, endAngle, anticlockwise)
    _2d.closePath();
    _2d.fill();
 }


有了dart:html库,您就可以随时访问所有标准浏览器元素,这些元素是您希望编码的对象。而且,由于DOM库(dart:html库的一部分)是从WebKit IDL(Interface Definition Language)生成的,因此您可以确保访问dart中可用的最新浏览器功能。

记住:
■ dart:html提供浏览器DOM的dart视图。
■ HTML5的支持是Dart语言的核心部分。

12.Dart虚拟机

Dart虚拟机是Dart语言的核心。一种用途是作为命令行VM上的可执行文件(允许您在控制台上运行Dart代码),例如启动HTTP服务器或运行简单脚本(相当于批处理文件或shell脚本),或任何其他基于控制台的Dart使用。另一个用途是将其嵌入到另一个应用程序中,如Dartium。

13.dart2js、包管理Pub

您可以使用dart2js工具从Dart编辑器或命令行上的单机版将Dart编译为JavaScript。dart2js工具将组成Dart应用程序的所有各种库和源代码文件编译为单个JavaScript文件。它输出的代码是相当可读的,尽管当您使用Dartium在Dart中进行本机开发时,您很少需要阅读它。

dart2js是第三个Dart-to-JavaScript转换器。第一个是dartc,第二个是一种叫做frog的工具。您可能会在各种旧文档和博客文章中看到这些名称;它们都是将Dart转换为JavaScript的工具。

包管理是任何语言的关键特性,Maven for Java,NuGet for .NET,npm for node.js是常见的示例。Dart有自己的包管理器,名为pub。Pub允许库开发人员在公共规范文件中定义包元数据(package metadata),并在代码仓库(如GitHub)中发布他们的库。

使用库时,可以使用pub工具下载应用程序所需的所有库,包括版本依赖项。我们将在第5章中详细讨论这一点,并在讨论Dart的库结构时展示一个使用pub的示例。

记住:
■ Dart工具生态系统是Dart项目的核心部分。
■ Dart编辑器为开发人员提供了丰富的工具。
■ Dartium使在Dart中开发像刷新浏览器一样简单。
■ Dart旨在转换为JavaScript。

总结

乍一看,Dart可能只是另一种语言。但是,当你考虑到整个Dart生态系统时,Dart代表了web开发领域令人兴奋的前景。随着应用程序变得越来越复杂,需要更大的开发团队,Dart及其相关工具和环境有望在以前过于灵活的JavaScript世界中提供某种结构。

在浏览器中托管的单页应用程序(如Google Plus)可以通过Dart这样的语言实现,因为维护大型客户端代码库变得不那么脆弱。Dart具有本机运行或转换为JavaScript与HTML5结合的能力,是构建不需要外部插件来提供功能的web应用程序的理想解决方案。

在接下来的章节中,您将使用Dart生态系统,探索核心语言,并使用Dart开发针对支持HTML5的现代web浏览器的单页web应用程序。在本书的结尾,您将开发Dart应用程序,这些应用程序在客户端脱机运行,由Dart文件服务器提供服务,并连接到Dart服务器以在数据库中持久化数据。

以上是关于★Dart-1-Hello Dart的主要内容,如果未能解决你的问题,请参考以下文章

Dart - 一个 dart 项目如何在不使用 pub 的情况下从另一个 dart 项目导入代码?

Dart 语言在 VS 代码中不能执行

为啥我的 Contact.dart 没有运行? (颤振、飞镖、VS 代码)

Dart 包的条件导入/代码

如何在不创建新的 dart 项目的情况下运行与我的颤振项目隔离的 dart 代码?

SpaceVim 语言模块 dart