Flutter Dart學習筆記

概括

重要的概念

在學習 Dart 語言時, 應該基于以下事實和概念:

  • 任何保存在變量中的都是一個 對象,并且所有的對象都是對應一個 類 的實例扫沼。無論是數(shù)字旧找,函數(shù)和 null 都是對象逗抑。所有對象繼承自 Object 類镊绪。

  • 盡管 Dart 是強類型的,但是 Dart 可以推斷類型带欢,所以類型注釋是可選的运授。 在上面的代碼中, number 被推斷為 int 類型乔煞。 如果要明確說明不需要任何類型吁朦, 需要使用特殊類型 dynamic

  • Dart 支持泛型

  • 與 Java 不同渡贾,Dart 沒有關(guān)鍵字 public 逗宜, protectedprivate 。 如果標識符以下劃線(_)開頭空骚,則它相對于庫是私有的纺讲,class是沒有私有變量的

class Test {
    // 沒有用 還是可以在外部訪問
    Object _name;
}

函數(shù)的定義

printInteger(int aNumber) {
    print('The number is $aNumber.'); // 打印到控制臺。
}

main() {

    var number = 42; // 聲明并初始化一個變量府怯。
    printInteger(number); // 調(diào)用函數(shù)。
}

// 字符串常量防楷。
'...' (or "...")

// 字符串插值: 包括字符串文字內(nèi)部的變量或表達式的字符串牺丙。 有關(guān)更多信息,參考 Strings.
$variableName (或 ${expression})

// 程序開始執(zhí)行函數(shù),該函數(shù)是特定的冲簿、必須的粟判、頂級函數(shù)。 有關(guān)更多信息峦剔,參考 The main() function.
main()

// 定義變量档礁,通過這種方式定義變量不需要指定變量類型。
var

Final 和 const

使用過程中從來不會被修改的變量吝沫, 可以使用 final 或 const, 而不是 var 或者其他類型呻澜, final 變量的值只能被設(shè)置一次; const 變量在編譯時就已經(jīng)固定 (Const 變量 是隱式 final 的類型.) 最高級 final 變量或類變量在第一次使用時被初始化。

var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

內(nèi)建類型

  • Numbers
    Dart 語言的 numbers 有兩種類型: int 和 double

    // int
    var x = 1;
    var hex = 0xDEADBEEF;
    // double
    var y = 1.1;
    var exponents = 1.42e5;
    
  • Strings
    Dart 字符串是一組 UTF-16 單元序列恭朗。 字符串通過單引號或者雙引號創(chuàng)建。

    // 單引號
    
    var single_spear = 'Single quotes work well for string literals.';
    // 雙引號
    var double_spear = "Double quotes work just as well.";
    
    // 使用連續(xù)三個單引號或者三個雙引號實現(xiàn)多行字符串對象的創(chuàng)建:
    var s1 = '''
    You can create
    multi-line strings like this one.
    ''';
    var s2 = """This is also a
    multi-line string.""";
    
    // 使用 r 前綴,可以創(chuàng)建 “原始 raw” 字符串
    var s = r"In a raw string, even \n isn't special.";
    
  • Booleans
    Dart 使用 bool 類型表示布爾值。 Dart 只有字面量 true and false 是布爾類型, 這兩個對象都是編譯時常量。
    Dart 的類型安全意味著不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)寒屯。 而是應該像下面這樣厂置,明確的進行值檢查

// 檢查空字符串访忿。
var fullName = '';
assert(fullName.isEmpty);

// 檢查 0 值平挑。
var hitPoints = 0;
assert(hitPoints <= 0);

// 檢查 null 值唇辨。
var unicorn;
assert(unicorn == null);

// 檢查 NaN 饿幅。(檢查參數(shù)中是否是非數(shù)字值)
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);        
  • Lists
    幾乎每種編程語言中最常見的集合可能是 array 或有序的對象集合磕秤。 在 Dart 中的 array 就是 List 對象磷瘤, 通常稱之為 lists 。
    Dart 中的 list 字面量非常像 JavaScript 和 iOS 中的 array 字面量愉耙。 下面是一個 Dart list 的示例:
var list = [1, 2, 3];
提示: 分析器推斷 list 的類型為 List<int> 贮尉。 如果嘗試將非整數(shù)對象添加到此 list 中, 則分析器或運行時會引發(fā)錯誤朴沿。 有關(guān)更多信息猜谚,請閱讀 類型推斷。

Lists 的下標索引從 0 開始赌渣,第一個元素的索引是 0魏铅。 list.length - 1 是最后一個元素的索引。 訪問 list 的長度和元素與 JavaScript 中的用法一樣:

var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);
  • Maps

通常來說坚芜, map 是用來關(guān)聯(lián) keys 和 values 的對象览芳。 keys 和 values 可以是任何類型的對象。在一個 map 對象中一個 key 只能出現(xiàn)一次鸿竖。 但是 value 可以出現(xiàn)多次沧竟。 Dart 中 map 通過 map 字面量 和 Map 類型來實現(xiàn)。

下面是使用 map 字面量的兩個簡單例子:

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

提示: 分析器會將 gifts 的類型推斷為 Map<String, String>缚忧, nobleGases 的類型推斷為 Map<int, String>悟泵。 如果嘗試在上面的 map 中添加錯誤類型,那么分析器或者運行時會引發(fā)錯誤搔谴。 有關(guān)更多信息魁袜,請閱讀類型推斷。敦第。
以上 map 對象也可以使用 Map 構(gòu)造函數(shù)創(chuàng)建:

var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
  • => expr簡寫語法 (=> 符號 有時也被稱為 箭頭 語法峰弹。)
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
// 等價于
bool isNoble(int atomicNumber) {
    return _nobleGases[atomicNumber] != null;
} 
  • 命名可選參數(shù)
// 可選參數(shù)可以是命名參數(shù)或者位置參數(shù),但一個參數(shù)只能選擇其中一種方式修飾芜果。
enableFlags(bold: true, hidden: false);

// 定義函數(shù)是鞠呈,使用 {param1, param2, …} 來指定命名參數(shù):
// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
  • 位置可選參數(shù) (只能放最后一個)
// 將參數(shù)放到 [] 中來標記參數(shù)是可選的:
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
  • 默認參數(shù)值
// 在定義方法的時候,可以使用 = 來定義可選參數(shù)的默認值右钾。 
// 默認值只能是編譯時常量蚁吝。 
// 如果沒有提供默認值旱爆,則默認值為 null。
// 下面是設(shè)置可選參數(shù)默認值示例:

void enableFlags({bool bold = false, bool hidden = false}) {...}

  • 函數(shù)也是對象

一個函數(shù)可以作為另一個函數(shù)的參數(shù)窘茁。 例如:

void printElement(int element) {
  print(element);
}
var list = [1, 2, 3];
// 將 printElement 函數(shù)作為參數(shù)傳遞怀伦。
list.forEach(printElement);

同樣可以將一個函數(shù)賦值給一個變量,例如:

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
  • 匿名函數(shù)

多數(shù)函數(shù)是有名字的山林, 比如 main() 和 printElement()房待。 也可以創(chuàng)建沒有名字的函數(shù),這種函數(shù)被稱為 匿名函數(shù)驼抹, 有時候也被稱為 lambda 或者 closure 桑孩。 匿名函數(shù)可以被復制到一個變量中, 舉個例子框冀,在一個集合中可以添加或者刪除一個匿名函數(shù)流椒。匿名函數(shù)和命名函數(shù)看起來類似— 在括號之間可以定義一些參數(shù)或可選參數(shù),參數(shù)使用逗號分割明也。

后面大括號中的代碼為函數(shù)體:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

// 一個簡單的block
doSome(VoidCallback voidCallBack) {
    voidCallBack();
}

如果函數(shù)只有一條語句宣虾, 可以使用箭頭簡寫。粘貼下面代碼到 DartPad 中 并點擊運行按鈕温数,驗證兩個函數(shù)是等價性安岂。

list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));
  • 詞法閉包

閉包 即一個函數(shù)對象,即使函數(shù)對象的調(diào)用在它原始作用域之外帆吻, 依然能夠訪問在它詞法作用域內(nèi)的變量域那。
函數(shù)可以封閉定義到它作用域內(nèi)的變量。 接下來的示例中猜煮, makeAdder() 捕獲了變量 addBy次员。
無論在什么時候執(zhí)行返回函數(shù),函數(shù)都會使用捕獲的 addBy 變量王带。

/// 返回一個函數(shù)淑蔚,返回的函數(shù)參數(shù)與 [addBy] 相加。
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  // 創(chuàng)建一個加 2 的函數(shù)愕撰。
  var add2 = makeAdder(2);

  // 創(chuàng)建一個加 4 的函數(shù)刹衫。
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
  • If 和 else

Dart 支持 if - else 語句,其中 else 是可選的搞挣, 比如下面的例子

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

注意:和 JavaScript 不同带迟, Dart 的判斷條件必須是布爾值,不能是其他類型

Dart 是一種基于類和 mixin 繼承機制的面向?qū)ο蟮恼Z言囱桨。 每個對象都是一個類的實例仓犬,所有的類都繼承于 Object. 。
基于 * Mixin 繼承* 意味著> 每個類(除 Object 外) 都只有一個超類舍肠, 一個類中的代碼可以在其他多個繼承類中重復使用搀继。

  • 使用類的成員變量

對象的由函數(shù)和數(shù)據(jù)(即方法和實例變量)組成窘面。 方法的調(diào)用要通過對象來完成: 調(diào)用的方法可以訪問其對象的其他函數(shù)和數(shù)據(jù)。
使用 (.) 來引用實例對象的變量和方法:

var p = Point(2, 2);

// 為實例的變量 y 設(shè)置值叽躯。
p.y = 3;

// 獲取變量 y 的值财边。
assert(p.y == 3);

// 調(diào)用 p 的 distanceTo() 方法。
num distance = p.distanceTo(Point(4, 4));

// 使用 ?. 來代替 . 点骑, 可以避免因為左邊對象可能為 null 制圈, 導致的異常:
// 如果 p 為 non-null,設(shè)置它變量 y 的值為 4畔况。
p?.y = 4;
  • 使用構(gòu)造函數(shù)

通過 構(gòu)造函數(shù) 創(chuàng)建對象。 構(gòu)造函數(shù)的名字可以是 ClassName 或者 ClassName.identifier慧库。
例如跷跪, 以下代碼使用 Point 和 Point.fromJson() 構(gòu)造函數(shù)創(chuàng)建 Point 對象:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
  • 獲取對象的類型
    使用對象的 runtimeType 屬性, 可以在運行時獲取對象的類型齐板, runtimeType 屬性回返回一個 Type 對象吵瞻。
print('The type of a is ${a.runtimeType}');
  • 重寫類成員

子類可以重寫實例方法,getter 和 setter甘磨。 可以使用 @override 注解指出想要重寫的成員:

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}
  • 重寫運算符

下面示例演示一個類重寫 + 和 - 操作符:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // 運算符 == 和 hashCode 部分沒有列出橡羞。 有關(guān)詳情,請參考下面的注釋济舆。
  // ···
}
void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
  • noSuchMethod()

當代碼嘗試使用不存在的方法或?qū)嵗兞繒r卿泽, 通過重寫 noSuchMethod() 方法,來實現(xiàn)檢測和應對處理:

class A {
  // 如果不重寫 noSuchMethod滋觉,訪問
  // 不存在的實例變量時會導致 NoSuchMethodError 錯誤签夭。
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}
  • 枚舉類型

枚舉類型也稱為 enumerations 或 enums , 是一種特殊的類椎侠,用于表示數(shù)量固定的常量值第租。

enum Color { 
    red, 
    green, 
    blue 
}
  • Mixin繼承是什么意思呢?

mixins是一個很強大的概念我纪,可以讓您跨多個類層次結(jié)構(gòu)重用代碼慎宾。

Mixins通過普通的類聲明隱式定義:
class Walker {
  void walk() {
    print("I'm walking");
  }
}
要使用mixin的話,你需要使用with關(guān)鍵字浅悉,后跟一個或多個mixin的名稱:
class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}
理解后就覺得很簡單趟据,但是這個設(shè)計思想很值得學習
class A {
  String getMessage() => 'A';
}

class B {
  String getMessage() => 'B';
}

class P {
  String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
  String result = '';

  AB ab = AB();
  result += ab.getMessage();

  BA ba = BA();
  result += ba.getMessage();

  print(result);
}

AB和BA類都使用A和B mixins繼承至P類,但順序不同术健。所有的A(3個)之宿,B和P類都有一個名為getMessage的方法。

首先苛坚,我們調(diào)用AB類的getMessage方法比被,然后調(diào)用BA類的getMessage方法色难。

  • A. BA
  • B. AB
  • C. BAAB
  • D. ABBA

線性化

實際上,這段代碼等同于

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}
  • 指定庫前綴

如果導入兩個存在沖突標識符的庫等缀, 則可以為這兩個庫枷莉,或者其中一個指定前綴。
例如尺迂,如果 library1 和 library2 都有一個 Element 類笤妙, 那么可以通過下面的方式處理:

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

// 使用 lib1 中的 Element。
Element element1 = Element();

// 使用 lib2 中的 Element噪裕。
lib2.Element element2 = lib2.Element();

導入庫的一部分
如果你只使用庫的一部分功能蹲盘,則可以選擇需要導入的 內(nèi)容。例如:

// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

延遲加載庫
lazy loading 可以讓應用在需要的時候再加載庫膳音。 下面是一些使用延遲加載庫的場景:

  1. 減少 APP 的啟動時間召衔。
  2. 執(zhí)行 A/B 測試,例如 嘗試各種算法的 不同實現(xiàn)祭陷。
  3. 加載很少使用的功能苍凛,例如可選的屏幕和對話框。
    要延遲加載一個庫兵志,需要先使用 deferred as 來導入:
import 'package:greetings/hello.dart' deferred as hello;

當需要使用的時候醇蝴,使用庫標識符調(diào)用 loadLibrary() 函數(shù)來加載庫:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

在前面的代碼,使用 await 關(guān)鍵字暫停代碼執(zhí)行一直到庫加載完成想罕。 關(guān)于 async 和 await 的更多信息請參考 異步支持悠栓。
在一個庫上你可以多次調(diào)用 loadLibrary() 函數(shù)。但是該庫只是載入一次按价。
使用延遲加載庫的時候闸迷,請注意一下問題:

  1. 延遲加載庫的常量在導入的時候是不可用的。 只有當庫加載完畢的時候俘枫,庫中常量才可以使用.
  2. 在導入文件的時候無法使用延遲庫中的類型腥沽。 如果你需要使用類型,則考慮把接口類型移動到另外一個庫中鸠蚪, 讓兩個庫都分別導入這個接口庫今阳。
  3. Dart 隱含的把 loadLibrary() 函數(shù)導入到使用 deferred as 的命名空間 中。 loadLibrary() 方法返回一個 Future茅信。
  • 處理 Stream

當需要從 Stream 中獲取數(shù)據(jù)值時盾舌, 可以通過以下方式:
異步for循環(huán)的使用形式

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

上面 表達式 返回的值必須是 Stream 類型。 執(zhí)行流程如下:

  1. 等待蘸鲸,直到流發(fā)出一個值妖谴。
  2. 執(zhí)行 for 循環(huán)體,將變量設(shè)置為該發(fā)出的值
  3. 重復1和2,直到關(guān)閉流膝舅。
  • 可調(diào)用類

通過實現(xiàn)類的 call() 方法嗡载, 能夠讓類像函數(shù)一樣被調(diào)用。
在下面的示例中仍稀,WannabeFunction 類定義了一個 call() 函數(shù)洼滚, 函數(shù)接受三個字符串參數(shù),函數(shù)體將三個字符串拼接技潘,
字符串間用空格分割遥巴,并在結(jié)尾附加了一個感嘆號。

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out');
}
  • 元數(shù)據(jù)

使用元數(shù)據(jù)可以提供有關(guān)代碼的其他信息享幽。 元數(shù)據(jù)注釋以字符 @ 開頭铲掐, 后跟對編譯時常量 (如 deprecated) 的引用或?qū)ΤA繕?gòu)造函數(shù)的調(diào)用。
對于所有 Dart 代碼有兩種可用注解:@deprecated 和 @override值桩。 關(guān)于 @override 的使用摆霉, 參考 擴展類(繼承)。 下面是使用

@deprecated(被棄用的) 注解的示例:

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

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

可以自定義元數(shù)據(jù)注解颠毙。 下面的示例定義了一個帶有兩個參數(shù)的 @todo 注解:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}

使用 @todo 注解的示例:

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

所有代碼中可用的注解

  1. @deprecated 被棄用的
  2. @override 重載
  3. @proxy 代理
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砂碉,隨后出現(xiàn)的幾起案子蛀蜜,更是在濱河造成了極大的恐慌,老刑警劉巖增蹭,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滴某,死亡現(xiàn)場離奇詭異,居然都是意外死亡滋迈,警方通過查閱死者的電腦和手機霎奢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饼灿,“玉大人幕侠,你說我怎么就攤上這事“恚” “怎么了晤硕?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庇忌。 經(jīng)常有香客問我舞箍,道長,這世上最難降的妖魔是什么皆疹? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上私痹,老公的妹妹穿的比我還像新娘。我一直安慰自己晃酒,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布立砸。 她就那樣靜靜地躺著掖疮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颗祝。 梳的紋絲不亂的頭發(fā)上浊闪,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音螺戳,去河邊找鬼搁宾。 笑死,一個胖子當著我的面吹牛倔幼,可吹牛的內(nèi)容都是我干的盖腿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼损同,長吁一口氣:“原來是場噩夢啊……” “哼翩腐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膏燃,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤茂卦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后组哩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體等龙,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年伶贰,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛛砰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡黍衙,死狀恐怖泥畅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琅翻,我是刑警寧澤涯捻,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站望迎,受9級特大地震影響障癌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辩尊,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一涛浙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦轿亮、人聲如沸疮薇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽按咒。三九已至,卻和暖如春但骨,著一層夾襖步出監(jiān)牢的瞬間励七,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工奔缠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掠抬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓校哎,卻偏偏與公主長得像两波,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闷哆,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容