目录

Dart学习笔记

参考资料

Dart最大特点是核心模块内置了html/js解析器,无论是html5还是canvas都能应付自如,并且对js有很好的性能支持。

import 'dart:html';

核心模块内置了html和js解析器

变量和常量

可变变量var

尽管Dart是强类型的,但是类型注释是可选的,因为Dart可以推断类型。用var关键词搞定,当你不想显示地声明一个变量的类型,那么您可以使用特殊类型dynamic

不过在 Dart2中,类型不再是可选的。
当你使用 var定义 name变量时,这个类型便会约束在 String类型,如果赋值其他类型便会出错。
如果你不想约束一个变量的类型,那么可以使用 Object类型定义或者dynamic关键字定义。

不可变量final

final修饰的变量,必须在定义时将其初始化,其值在初始化后不可改变,但是用final标记的变量可以在运行时确定。

final 其实就是在运行时声明一个不可变的引用,这个引用一旦确定就不可再变。

不可变量const

const也可以定义常量。

它们的区别在于,const比final更加严格。final只是要求变量在初始化后值不变,但通过final,我们无法在编译时(运行之前)知道这个变量的值;而const所修饰的是编译时常量,我们在编译时就已经知道了它的值,显然,它的值也是不可改变的。

未初始化的变量的默认值为:null。甚至具有数字类型的变量最初也是null,因为数字就像dart中的其他东西一样也是对象。

在 Dart中这一点很叫人放心。

数据类型

Number 类型

Number 类型分为 int 和 double

int 的范围是 -2^53 ~ 2^53
double 是符合 IEEE 754标准的 64位双精度的浮点数。
int和 double都是 Number 的子类型。
除了常见的运算符外,你还能使用内置的 abs()、ceil()、floor()等方法。
如果还有其他运算方法的需求,你可以引入 dart:math库来尝试用一下。

一个例子

void main() {    
  // 字符串转 int
  var one = int.parse('1');
 
  // 字符串转浮点
  var onePointOne = double.parse('1.1');
 
  // int 转字符串
  String oneAsString = 1.toString();
 
  // double 转字符串,保留几位小数
  String piAsString = 3.14159.toStringAsFixed(2); 
}

String 类型

int 是基本类型,它的对象类型是Number ,String 是对象类型,所有用大写

可以使用单引号或双引号创建字符串,也可以用缓存区创建

var Res = StringBuffer();
Res..write();

字符串连接

字符串连接,使用 + 操作符

// 字符串连接可以使用 + 号
var str4 = str1 +'-'+ str2;

多行字符串

// 多行字符串,如""" 或 '''
var str5 = '''
多行字符串的定义
简单吧
''';

raw 字符串

使用 r 来定义

// 使用 r 修饰,表明是 raw 类型的字符串
var str6 = r"In a raw string, even \n s";

列表List

var list = [1, 2, 3];

和其他语言一样,数组索引也从 0开始。同样地,你还可以像 JavaScript那样迭代数组:

list.forEach((item) {
  print('index: ${list.indexOf(item)}, item:${item}');
});

字典Map

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};
 
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

自定义下标字典

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};
 
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

流程控制

if..else if..else

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

for loops

var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
  message.write('!');
}
var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
// for - in
var collection = [0, 1, 2];
for (var x in collection) {
  print(x);
}

while & do-while loops

// while 循环在执行循环之前先判断条件是否满足
while (!isDone()) {
  doSomething();
}
// do-while 循环是先执行循环代码再判断条件
do {
  printLine();
} while (!atEndOfPage());

switch case

Dart 中的 Switch 语句使用 == 比较 integer、string、或者编译时常量。

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
  case 'APPROVED':
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

break & continue

// 使用break来终止当前循环
while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}
使用 continue 来跳过当前,开始下一次循环:
for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}

assert

如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。适用于单元测试。

// Make sure the variable has a non-null value.
assert(text != null);
 
// Make sure the value is less than 100.
assert(number < 100);
 
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
断言只在检查模式下运行有效,如果在生产模式 运行,则断言不会执行。

函数(过程)

可选命名参数

把一组函数的参数放在{}之内可以把它们标记为可选位置参数:

在调用函数的时候,你可以通过paraName: Value的形式使用有命名的参数。比如:

enableFlags(bold: true, hidden: false);
int add2(x,{y=2,z=3}){
        print("x=$x y=$y z=$z");
        return(x+y+z);
}
 
void main(){
        print(add2(1,z:3,y:4));
}

可选位置参数

把一组函数的参数放在[]之内可以把它们标记为可选位置参数:

int add(x,[y=2,z=3]){
        print("x=$x y=$y z=$z");
        return(x+y+z);
}
 
void main(){
        print(add(1));
        print(add(1,3,4));
}
void main() {
  String say(String from, String msg, [String device]) {
    var result = '$from says $msg';
    if (device != null) {
      result = '$result with a $device';
    }
    return result;
  }
 
  print(say("geminis say: ", "hello world"));
  print(say("geminis say: ", "hello world", "iphone6"));
}

作为对象的函数

你可以把一个函数当做参数传给另一个函数(函数代参,或称高阶递归)。比如:

printElement(element) {
     print(element);
}
 
var list = [1, 2, 3];
 
// 将 printElement 作为参数传递给其他函数
list.forEach(printElement);

语法作用域

Dart 是语法作用域型的语言,这意味着变量的范围是静态确定的。你可以“层层跟踪大括号”来看一下变量是否存在于这个区域中。

下面是一份关于嵌套的带参函数在不同的作用域等级上的示例:

var topLevel = true;
 
main() {
     var indideMain = true;
 
     myFunction() {
         var insideFunction = true;
 
         nestedFunction() {
             var insideNestedFunction = true;
 
             assert(topLevel);
             assert(insideMain);
             assert(insideFunction);
             assert(insideNestedFunction);
        }
    }
}

请留意nestedFunction()是怎样使用不同作用域上的变量的,最好从最内层开始一直分析到最外层。

闭包函数

闭包指的是一个函数可以访问其语法作用域内的变量,即使这个函数是在变量本身的作用域之外被调用的。

函数内部会包含在临近作用域内所定义的变量。在下一个示例中,makeAdder()捕获了变量addBy。不管返回的函数在哪里被调用,它都可以使用addBy。

/// 返回一个把 addBy 作为参数的函数
Function makeAdder(num addBy) {
     return (num i) => addBy + 1;
}
 
main() {
     // 创建一个加2的函数
     var add2 = makeAdder(2);
     // 创建一个加4的函数
     var add4 = makeAdder(4);
 
     assert(add2(3) == 5);
     assert(add4(3) == 7);
}

异常处理

你的Dart代码可以抛出异常和捕获异常。异常就是出现预期之外的结果的错误。如果没有捕获异常, isolate 将会使异常挂起,往往会导致 isolate 和程序终止。

Dart中所有异常都是不需检测的异常。方法并不会声明它将抛出哪些异常,而且你不需要去捕捉任何异常。

Dart 除了提供 异常、错误 类型以外还提供了众多预定义的子类型。当然,你可以定义你自己的异常类型。毕竟,Dart程序可以将任何非空对象作为异常抛出,不只局限与异常和错误对象。

throw抛出异常

catch捕获异常

finally安全运行

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一个具体异常
  buyMoreLlamas();
} on Exception catch (e) {
  // 任意一个异常
  print('Unknown exception: $e');
} catch (e) {
  // 非具体类型
  print('Something really unknown: $e');
} finally {
  cleanLlamaStalls();  // 然后清理
}

类和泛型

简化构造

class Point {
  num x;
  num y;
  Point(this.x, this.y);
}
 
var p = new Point(1, 2);
print([p.x, p.y]);

调用系统命令

多线程和线程池

库管理

创建自己的库

library loglib;
 
debug(msg) => print("DEBUG: $msg");
warn(msg) => print("WARN: $msg");
info(msg) => print("INFO: $msg");
 
class Logger {
  bool _isEnabled;
  bool get isEnabled => _isEnabled;
  void set isEnabled(value) => _isEnabled = value;
  log(msg) => print("LOG: $msg");

引入自定义库

import 'dart:html';
import './mylog.dart'; 
//import './mylog.dart' as logger;
 
// 类似C语言的风格的主函数和注释,
/* 
  是不是
  很亲切
  的感觉
*/
main() {
  querySelector('#status').text = 'Hello world, this is dart!';
  var title = new Element.html("<h1>清单列表</h1>"); // 可以直接是 html原生语法
  document.body.children.add(title);
 
  InputElement itemInput = new Element.tag(
      "input"); // 也可以引用dart的tag对象,如InputElement,ButtonElement,DivElement
  itemInput.id = "txt-item";
  itemInput.placeholder = "Enter an item"; // 默认灰色提示
  document.body.children.add(itemInput);
 
  ButtonElement addButton = new ButtonElement(); // 这也是一种初始方式
  addButton.text = "提交";
  document.body.children.add(addButton);
 
  DivElement itemContainer = new DivElement();
  document.body.children.add(itemContainer);
/*
 * 监听事件
 */
  var number = 0; // 初始化物品数量
  add_items() {
    var itemValue = itemInput.value; // 获取input内容
    info(itemValue + itemValue.toString().trim().length.toString()); // 打开浏览器开发者控制台查看
 
    if (itemValue.toString().trim().length > 0) {
      var listElement = new Element.html("<div class='item'>${itemValue}<div>");
      itemContainer.children.add(listElement); // 向itemContainer里填充数据
      number++;
      querySelector('#status').text = number.toString();
    }
    itemInput.value = ""; // 清空input输入框
  }
 
  var evt_additems = (event) {
    add_items();
  };
 
  addButton.onClick.listen(evt_additems); // 添加监听一
  itemInput.onKeyPress.listen((event) {
    // 添加监听二
    if (event.keyCode == 13) {
      // 按Enter回车
      add_items();
    }
  });
}

打包分享库

通过pub这个内置工具来打包发布有用的库函数,先定义pubspec.yaml文件

name: loglib
version: 1.2.3
description: >
  Provides a simple logging framework
  to log to the console
author: You <you@your.email.com>
homepage: http://your.website.com/loglib
dependencies:
  args: '^0.13.2'
environment:
  sdk: '>=1.19.0 <3.0.0'
  flutter: ^0.1.2
hosted_library:
versioned_library: '1.2.3'
unittest:
  any
my_security_library:
  hosted:
    url: http://your.internal.server.com
  version: '>=1.0.0 <2.0.0'
  git:
    url: git://github.com/my/open_source_library.git
pub publish --dry-run

先来个直观的例子

创建只包含一个元素的html页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Dart:HTML == Future</title>
</head>
<body>
<div id="status"></div> <!-- 只包含一个div元素 -->
<script async type="text/javascript" src="packages/browser/dart.js"></script>
<script async type="application/dart" src="demo.dart"></script> <!-- 这是一段类似js的dart程序 -->
</body>
</html>

新建一个demo.dart脚本

import 'dart:html';
 
// 类似C语言的风格的主函数和注释,
/* 
  是不是
  很亲切
  的感觉
*/
main(){
  querySelector('#status').text = 'Hello world, this is dart!';
}

在dart中新增html元素很简单

import 'dart:html';
 
// 类似C语言的风格的主函数和注释,
/* 
  是不是
  很亲切
  的感觉
*/
main(){
  querySelector('#status').text = 'Hello world, this is dart!';
  var title = new Element.html("<h1>清单列表</h1>"); // 可以直接是 html原生语法
  document.body.children.add(title);
 
  InputElement itemInput = new Element.tag("input"); // 也可以引用dart的tag对象,如InputElement,ButtonElement,DivElement
  itemInput.id = "txt-item";
  itemInput.placeholder = "Enter an item"; // 默认灰色提示
  document.body.children.add(itemInput);
 
  ButtonElement addButton = new Element.tag("button"); 
  addButton.text = "提交";
  document.body.children.add(addButton);
 
  DivElement itemContainer = new Element.tag("div");
  document.body.children.add(itemContainer);
}

增加事件监听的复杂例子

import 'dart:html';
 
// 类似C语言的风格的主函数和注释,
/* 
  是不是
  很亲切
  的感觉
*/
main() {
  querySelector('#status').text = 'Hello world, this is dart!';
  var title = new Element.html("<h1>清单列表</h1>"); // 可以直接是 html原生语法
  document.body.children.add(title);
 
  InputElement itemInput = new Element.tag("input"); // 也可以引用dart的tag对象,如InputElement,ButtonElement,DivElement
  itemInput.id = "txt-item";
  itemInput.placeholder = "Enter an item"; // 默认灰色提示
  document.body.children.add(itemInput);
 
  ButtonElement addButton = new ButtonElement(); // 这也是一种初始方式
  addButton.text = "提交";
  document.body.children.add(addButton);
 
  DivElement itemContainer = new DivElement();
  document.body.children.add(itemContainer);
 
  var evt_additems = (event) {
    var itemValue = itemInput.value; // 获取input内容
    var listElement = new Element.html("<div class='item'>${itemValue}<div>");
    itemContainer.children.add(listElement); // 向itemContainer里填充数据
    itemInput.value = ""; // 清空input输入框
  };
  addButton.onClick.listen(evt_additems); // 事件监听是重点:onClick,onDrag,onMouseUp/Over/Down/Out...,onKeyUp/Down/Press..
}

dart处理后修改html元素内容

import 'dart:html';
 
// 类似C语言的风格的主函数和注释,
/* 
  是不是
  很亲切
  的感觉
*/
main() {
  querySelector('#status').text = 'Hello world, this is dart!';
  var title = new Element.html("<h1>清单列表</h1>"); // 可以直接是 html原生语法
  document.body.children.add(title);
 
  InputElement itemInput = new Element.tag(
      "input"); // 也可以引用dart的tag对象,如InputElement,ButtonElement,DivElement
  itemInput.id = "txt-item";
  itemInput.placeholder = "Enter an item"; // 默认灰色提示
  document.body.children.add(itemInput);
 
  ButtonElement addButton = new ButtonElement(); // 这也是一种初始方式
  addButton.text = "提交";
  document.body.children.add(addButton);
 
  DivElement itemContainer = new DivElement();
  document.body.children.add(itemContainer);
/*
 * 监听事件
 */
  var number = 0; // 初始化物品数量
  add_items() {
    var itemValue = itemInput.value; // 获取input内容
    var listElement = new Element.html("<div class='item'>${itemValue}<div>");
    itemContainer.children.add(listElement); // 向itemContainer里填充数据
    itemInput.value = ""; // 清空input输入框
    number++;
    querySelector('#status').text = number.toString();
  }
 
  var evt_additems = (event) {
    add_items();
  };
 
  addButton.onClick.listen(evt_additems); // 添加监听一
  itemInput.onKeyPress.listen((event) {
    // 添加监听二
    if (event.keyCode == 13) {
      // 按Enter回车
      add_items();
    }
  });
}

一个简单的黄金爬虫脚本

这个脚本是用于获取黄金价格和涨幅比例

创建目录

mkdir getGold && cd getGold

使用 stagehand 来生成终端应用

pub global activate stagehand # If it's not installed
stagehand console-full

添加组件到 pubspec.yaml

dependencies:
  html: ^0.13.3+3
  http: ^0.12.0

运行安装相关依赖

pub get

编写代码 /lib/getGold.dart

import 'package:http/http.dart';
import 'package:html/parser.dart';
import 'package:html/dom.dart';
 
Future initiate(BaseClient client) async {
  // Make API call to Hackernews homepage
  Response response = await client.get('http://fund.eastmoney.com/000217.html');
 
  if (response.statusCode != 200) return response.body;
 
  // Use html parser
  var document = parse(response.body);
 
  var time = document.querySelectorAll('#gz_gztime');
  var jinzhi = document.querySelectorAll('#gz_gsz');
  var zhangfu = document.querySelectorAll('#gz_gszzl');
 
  var res = StringBuffer();
  jinzhi.forEach((v) => res..write(v.text + " "));
  zhangfu.forEach((v) => res..write(v.text + " "));
  time.forEach((v) => res..write(v.text + " "));
 
  return res;
}

运行启动脚本 /bin/main.dart

mport 'package:getGold/getGold.dart' as getGold;
import 'package:http/http.dart';
 
Future main() async {
  print(await getGold.initiate(Client()));
}