《Node.js 極簡(jiǎn)教程》 東海陳光劍

Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境。
Node.js 使用了一個(gè)事件驅(qū)動(dòng)捌锭、非阻塞式 I/O 的模型肯腕,使其輕量又高效。
https://nodejs.org

快速開始 Hello World

Node 是什么

  • 簡(jiǎn)史
    • 是什么
      • Node.js是一個(gè)Javascript運(yùn)行環(huán)境(runtime environment)堡僻,發(fā)布于2009年5月糠惫,由Ryan Dahl開發(fā),實(shí)質(zhì)是對(duì)Chrome V8引擎進(jìn)行了封裝钉疫。Node.js 不是一個(gè) JavaScript 框架硼讽,不同于CakePHP、Django牲阁、Rails固阁。Node.js 更不是瀏覽器端的庫(kù),不能與 jQuery城菊、ExtJS 相提并論备燃。Node.js 是一個(gè)讓 JavaScript 運(yùn)行在服務(wù)端的開發(fā)平臺(tái),它讓 JavaScript 成為與PHP凌唬、Python并齐、Perl、Ruby 等服務(wù)端語言平起平坐的腳本語言客税。
    • 發(fā)展史
      • 2009年2月况褪,Ryan Dahl在博客上宣布準(zhǔn)備基于V8創(chuàng)建一個(gè)輕量級(jí)的Web服務(wù)器并提供一套庫(kù)。
      • 2009年5月霎挟,Ryan Dahl在GitHub上發(fā)布了最初版本的部分Node.js包窝剖,隨后幾個(gè)月里,有人開始使用Node.js開發(fā)應(yīng)用酥夭。
      • 2009年11月和2010年4月赐纱,兩屆JSConf大會(huì)都安排了Node.js的講座脊奋。
      • 2010年年底,Node.js獲得云計(jì)算服務(wù)商Joyent資助疙描,創(chuàng)始人Ryan Dahl加入Joyent全職負(fù)責(zé)Node.js的發(fā)展诚隙。
      • 2011年7月,Node.js在微軟的支持下發(fā)布Windows版本起胰。
  • 特征
    • 非阻塞I/O(單線程久又、非阻塞)&事件輪詢【Single Threaded Event Loop】
      • Node通過事件驅(qū)動(dòng)的方式處理請(qǐng)求時(shí)無需為每一個(gè)請(qǐng)求創(chuàng)建額外的線程。在事件驅(qū)動(dòng)的模型當(dāng)中效五,每一個(gè)IO工作被添加到事件隊(duì)列中地消,線程循環(huán)地處理隊(duì)列上的工作任務(wù),當(dāng)執(zhí)行過程中遇到來堵塞(讀取文件畏妖、查詢數(shù)據(jù)庫(kù))時(shí)脉执,線程不會(huì)停下來等待結(jié)果,而是留下一個(gè)處理結(jié)果的回調(diào)函數(shù)戒劫,轉(zhuǎn)而繼續(xù)執(zhí)行隊(duì)列中的下一個(gè)任務(wù)半夷。這個(gè)傳遞到隊(duì)列中的回調(diào)函數(shù)在堵塞任務(wù)運(yùn)行結(jié)束后才被線程調(diào)用。
    • 優(yōu)點(diǎn)
      • 高并發(fā)(最重要的優(yōu)點(diǎn))
      • 適合I/O密集型應(yīng)用
      • RESTful API
      • npm迅细,前后端分離
    • 缺陷
      • 回調(diào)模式下的異步是有明顯缺陷的巫橄,程序的執(zhí)行順序必須依靠回調(diào)來保證,沒有層層回調(diào)茵典,就沒有可以保障的邏輯順序湘换,這也就注定了,node不能做復(fù)雜的業(yè)務(wù)邏輯敬尺。
      • 回調(diào)山真不是必須的枚尼。
      • 不適合CPU密集型應(yīng)用
      • 只支持單核CPU,不能充分利用CPU
      • 可靠性低砂吞,一旦代碼某個(gè)環(huán)節(jié)崩潰,整個(gè)系統(tǒng)都崩潰崎溃,原因:?jiǎn)芜M(jìn)程蜻直,單線程。
      • Debug不方便袁串,錯(cuò)誤沒有stack trace
  • nodejs中的庫(kù)方法是異步的概而,異步方法是約定。

環(huán)境配置

Hello World 示例

Once you have installed Node, let's try building our first web server. Create a file named "app.js", and paste the following code:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

After that, run your web server using node app.js, visit http://localhost:3000, and you will see a message 'Hello World'

NPM 使用介紹

  • npm 簡(jiǎn)介
    • NPM是隨同NodeJS一起安裝的包管理工具囱修,能解決NodeJS代碼部署上的很多問題赎瑰,常見的使用場(chǎng)景有以下幾種:

允許用戶從NPM服務(wù)器下載別人編寫的第三方包到本地使用。
允許用戶從NPM服務(wù)器下載并安裝別人編寫的命令行程序到本地使用破镰。
允許用戶將自己編寫的包或命令行程序上傳到NPM服務(wù)器供別人使用餐曼。
由于新版的nodejs已經(jīng)集成了npm压储,所以之前npm也一并安裝好了。同樣可以通過輸入 "npm -v" 來測(cè)試是否成功安裝源譬。命令如下集惋,出現(xiàn)版本提示表示安裝成功:

$ npm -v
6.4.1

如果你安裝的是舊版本的 npm,可以很容易得通過 npm 命令來升級(jí)踩娘,命令如下:

$ sudo npm install npm -g

Node 架構(gòu)與運(yùn)行原理

Node 架構(gòu)分析

  • 架構(gòu)圖
  • nodejs組成部分
    • v8 engine
      • 虛擬機(jī)的功能刮刑,執(zhí)行js代碼
      • 提供C++函數(shù)接口,為nodejs提供v8初始化养渴,創(chuàng)建context雷绢,scope等
    • libuv
      • 基于事件驅(qū)動(dòng)的異步IO模型庫(kù),我們的js代碼發(fā)出請(qǐng)求理卑,最終由libuv完成习寸,而我們所設(shè)置的回調(diào)函數(shù)則是在libuv觸發(fā)
    • builtin modules
      • 由C++代碼寫成各類模塊,包含了crypto傻工,zlib, file stream etc 基礎(chǔ)功能霞溪。(v8提供了函數(shù)接口,libuv提供異步IO模型庫(kù)中捆,以及一些nodejs函數(shù)鸯匹,為builtin modules提供服務(wù))
    • native modules
      • 由js寫成,提供我們應(yīng)用程序調(diào)用的庫(kù)泄伪,同時(shí)這些模塊又依賴builtin modules來獲取相應(yīng)的服務(wù)支持
    • 建立http server為例
      • http server 建立過程

ES6 features

Node.js ES2015 Support

Node 模塊系統(tǒng)

簡(jiǎn)介

創(chuàng)建模塊

加載模塊

Node 全局對(duì)象

是什么

  • JavaScript 中有一個(gè)特殊的對(duì)象殴蓬,稱為全局對(duì)象(Global Object),它及其所有屬性都可以在程序的任何地方訪問蟋滴,即全局變量染厅。
  • 在瀏覽器 JavaScript 中,通常 window 是全局對(duì)象津函, 而 Node.js 中的全局對(duì)象是 global肖粮,所有全局變量(除了 global 本身以外)都是 global 對(duì)象的屬性。
  • 在 Node.js 我們可以直接訪問到 global 的屬性尔苦,而不需要在應(yīng)用中包含它涩馆。

文件操作

簡(jiǎn)介

  • Node.js 提供一組類似 UNIX(POSIX)標(biāo)準(zhǔn)的文件操作API。 Node 導(dǎo)入文件系統(tǒng)模塊(fs)語法如下所示:
  • var fs = require("fs")

異步和同步

  • Node.js 文件系統(tǒng)(fs 模塊)模塊中的方法均有異步和同步版本允坚,例如讀取文件內(nèi)容的函數(shù)有異步的 fs.readFile() 和同步的 fs.readFileSync()魂那。
  • 異步的方法函數(shù)最后一個(gè)參數(shù)為回調(diào)函數(shù),回調(diào)函數(shù)的第一個(gè)參數(shù)包含了錯(cuò)誤信息(error)稠项。
  • 建議大家使用異步方法涯雅,比起同步,異步方法性能更高展运,速度更快活逆,而且沒有阻塞精刷。
  • 阻塞代碼實(shí)例
    • 創(chuàng)建一個(gè)文件 input.txt ,內(nèi)容如下:
Node.js 極簡(jiǎn)教程

創(chuàng)建 main.js 文件, 代碼如下:

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("程序執(zhí)行結(jié)束!");

以上代碼執(zhí)行結(jié)果如下:

$ node main.js

Node.js 極簡(jiǎn)教程
程序執(zhí)行結(jié)束!
  • 非阻塞代碼實(shí)例
    • 創(chuàng)建一個(gè)文件 input.txt 划乖,內(nèi)容如下:
Node.js 極簡(jiǎn)教程

創(chuàng)建 main.js 文件, 代碼如下:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});

console.log("程序執(zhí)行結(jié)束!");

以上代碼執(zhí)行結(jié)果如下:

$ node main.js
程序執(zhí)行結(jié)束!
Node.js 極簡(jiǎn)教程

以上兩個(gè)實(shí)例我們了解了阻塞與非阻塞調(diào)用的不同贬养。第一個(gè)實(shí)例在文件讀取完后才執(zhí)行完程序。 第二個(gè)實(shí)例我們不需要等待文件讀取完琴庵,這樣就可以在讀取文件時(shí)同時(shí)執(zhí)行接下來的代碼误算,大大提高了程序的性能。

因此迷殿,阻塞是按順序執(zhí)行的儿礼,而非阻塞是不需要按順序的,所以如果需要處理回調(diào)函數(shù)的參數(shù)庆寺,我們就需要寫在回調(diào)函數(shù)內(nèi)蚊夫。

Web 模塊

Web 服務(wù)器簡(jiǎn)介

  • Web服務(wù)器一般指網(wǎng)站服務(wù)器,是指駐留于因特網(wǎng)上某種類型計(jì)算機(jī)的程序懦尝,Web服務(wù)器的基本功能就是提供Web信息瀏覽服務(wù)知纷。它只需支持HTTP協(xié)議、HTML文檔格式及URL陵霉,與客戶端的網(wǎng)絡(luò)瀏覽器配合琅轧。

大多數(shù) web 服務(wù)器都支持服務(wù)端的腳本語言(php、python踊挠、ruby)等乍桂,并通過腳本語言從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),將結(jié)果返回給客戶端瀏覽器效床。

目前最主流的三個(gè)Web服務(wù)器是Apache睹酌、Nginx、IIS剩檀。

使用 Node 創(chuàng)建 Web 服務(wù)器

  • Node.js 提供了 http 模塊憋沿,http 模塊主要用于搭建 HTTP 服務(wù)端和客戶端,使用 HTTP 服務(wù)器或客戶端功能必須調(diào)用 http 模塊谨朝,代碼如下:
var http = require('http');

以下是演示一個(gè)最基本的 HTTP 服務(wù)器架構(gòu)(使用 8080 端口)卤妒,創(chuàng)建 index.js 文件,代碼如下所示:

實(shí)例

var http = require('http');
var fs = require('fs');
var url = require('url');
 
 
// 創(chuàng)建服務(wù)器
http.createServer( function (request, response) {  
   // 解析請(qǐng)求字币,包括文件名
   var pathname = url.parse(request.url).pathname;
   
   // 輸出請(qǐng)求的文件名
   console.log("Request for " + pathname + " received.");
   
   // 從文件系統(tǒng)中讀取請(qǐng)求的文件內(nèi)容
   fs.readFile(pathname.substr(1), function (err, data) {
      if (err) {
         console.log(err);
         // HTTP 狀態(tài)碼: 404 : NOT FOUND
         // Content Type: text/plain
         response.writeHead(404, {'Content-Type': 'text/html'});
      }else{             
         // HTTP 狀態(tài)碼: 200 : OK
         // Content Type: text/plain
         response.writeHead(200, {'Content-Type': 'text/html'});    
         
         // 響應(yīng)文件內(nèi)容
         response.write(data.toString());        
      }
      //  發(fā)送響應(yīng)數(shù)據(jù)
      response.end();
   });   
}).listen(8080);
 
// 控制臺(tái)會(huì)輸出以下信息
console.log('Server running at http://127.0.0.1:8080/');

接下來我們?cè)谠撃夸浵聞?chuàng)建一個(gè) index.html 文件,代碼如下:

index.html 文件

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Node.js 極簡(jiǎn)教程</title>
</head>
<body>
    <h1>我的第一個(gè)標(biāo)題</h1>
    <p>我的第一個(gè)段落共缕。</p>
</body>
</html>

執(zhí)行 index.js 文件:

$ node index.js
Server running at http://127.0.0.1:8080/
Request for / received.
{ [Error: ENOENT: no such file or directory, open ''] errno: -2, code: 'ENOENT', syscall: 'open', path: '' }
Request for /index.html received.
Request for /favicon.ico received.
{ [Error: ENOENT: no such file or directory, open 'favicon.ico']
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'favicon.ico' }
Request for /index.html received.
Request for /favicon.ico received.
{ [Error: ENOENT: no such file or directory, open 'favicon.ico']
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'favicon.ico' }

直接訪問 http://127.0.0.1:8080/洗出, 提示 HTTP ERROR 404 Not found:


因?yàn)椋覀兇a里沒有對(duì)根路徑進(jìn)行映射處理图谷。

接著我們?cè)跒g覽器中打開地址:http://127.0.0.1:8080/index.html翩活,顯示如下圖所示:

MySQL數(shù)據(jù)庫(kù)操作

安裝驅(qū)動(dòng)

$ cnpm install mysql

連接數(shù)據(jù)庫(kù)

  • 在以下實(shí)例中根據(jù)你的實(shí)際配置修改數(shù)據(jù)庫(kù)用戶名阱洪、及密碼及數(shù)據(jù)庫(kù)名:

test.js 文件代碼:

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : '123456',
  database : 'test'
});
 
connection.connect();
 
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});

執(zhí)行以下命令輸出結(jié)果為:

$ node test.js
The solution is: 2

數(shù)據(jù)庫(kù) CRUD 操作

  • 查詢數(shù)據(jù)
    • 查詢數(shù)據(jù)
var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : '123456',       
  port: '3306',                   
  database: 'test', 
}); 
 
connection.connect();
 
var  sql = 'SELECT * FROM websites';
//查
connection.query(sql,function (err, result) {
        if(err){
          console.log('[SELECT ERROR] - ',err.message);
          return;
        }
 
       console.log('--------------------------SELECT----------------------------');
       console.log(result);
       console.log('------------------------------------------------------------\n\n');  
});
 
connection.end();
  • 插入數(shù)據(jù)
    • 插入數(shù)據(jù)
var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : '123456',       
  port: '3306',                   
  database: 'test', 
}); 
 
connection.connect();
 
var  addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)';
var  addSqlParams = ['菜鳥工具', 'https://c.runoob.com','23453', 'CN'];
//增
connection.query(addSql,addSqlParams,function (err, result) {
        if(err){
         console.log('[INSERT ERROR] - ',err.message);
         return;
        }        
 
       console.log('--------------------------INSERT----------------------------');
       //console.log('INSERT ID:',result.insertId);        
       console.log('INSERT ID:',result);        
       console.log('-----------------------------------------------------------------\n\n');  
});
 
connection.end();

執(zhí)行以下命令輸出就結(jié)果為:

$ node test.js
--------------------------INSERT----------------------------
INSERT ID: OkPacket {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 6,
  serverStatus: 2,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0 }
-----------------------------------------------------------------
  • 更新數(shù)據(jù)
    • 我們也可以對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行修改:

更新數(shù)據(jù)

var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : '123456',       
  port: '3306',                   
  database: 'test', 
}); 
 
connection.connect();
 
var modSql = 'UPDATE websites SET name = ?,url = ? WHERE Id = ?';
var modSqlParams = ['菜鳥移動(dòng)站', 'https://m.runoob.com',6];
//改
connection.query(modSql,modSqlParams,function (err, result) {
   if(err){
         console.log('[UPDATE ERROR] - ',err.message);
         return;
   }        
  console.log('--------------------------UPDATE----------------------------');
  console.log('UPDATE affectedRows',result.affectedRows);
  console.log('-----------------------------------------------------------------\n\n');
});
 
connection.end();

執(zhí)行以下命令輸出就結(jié)果為:

--------------------------UPDATE----------------------------
UPDATE affectedRows 1
-----------------------------------------------------------------
  • 刪除數(shù)據(jù)
    • 我們可以使用以下代碼來刪除 id 為 6 的數(shù)據(jù):

刪除數(shù)據(jù)

var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : '123456',       
  port: '3306',                   
  database: 'test', 
}); 
 
connection.connect();
 
var delSql = 'DELETE FROM websites where id=6';
//刪
connection.query(delSql,function (err, result) {
        if(err){
          console.log('[DELETE ERROR] - ',err.message);
          return;
        }        
 
       console.log('--------------------------DELETE----------------------------');
       console.log('DELETE affectedRows',result.affectedRows);
       console.log('-----------------------------------------------------------------\n\n');  
});
 
connection.end();

執(zhí)行以下命令輸出就結(jié)果為:

--------------------------DELETE----------------------------
DELETE affectedRows 1
-----------------------------------------------------------------

Kotlin 開發(fā)者社區(qū)

國(guó)內(nèi)第一Kotlin 開發(fā)者社區(qū)公眾號(hào),主要分享菠镇、交流 Kotlin 編程語言冗荸、Spring Boot、Android利耍、React.js/Node.js蚌本、函數(shù)式編程、編程思想等相關(guān)主題隘梨。

開發(fā)者社區(qū) QRCode.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末程癌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子轴猎,更是在濱河造成了極大的恐慌嵌莉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捻脖,死亡現(xiàn)場(chǎng)離奇詭異锐峭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)可婶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門沿癞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扰肌,你說我怎么就攤上這事抛寝。” “怎么了曙旭?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵盗舰,是天一觀的道長(zhǎng)蹋笼。 經(jīng)常有香客問我晤锹,道長(zhǎng),這世上最難降的妖魔是什么毁靶? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任剂习,我火速辦了婚禮蛮位,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鳞绕。我一直安慰自己失仁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布们何。 她就那樣靜靜地躺著萄焦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拂封,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天茬射,我揣著相機(jī)與錄音,去河邊找鬼冒签。 笑死在抛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萧恕。 我是一名探鬼主播刚梭,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼廊鸥!你這毒婦竟也來了望浩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤惰说,失蹤者是張志新(化名)和其女友劉穎磨德,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吆视,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡典挑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啦吧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片您觉。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖授滓,靈堂內(nèi)的尸體忽然破棺而出琳水,到底是詐尸還是另有隱情,我是刑警寧澤般堆,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布在孝,位于F島的核電站,受9級(jí)特大地震影響淮摔,放射性物質(zhì)發(fā)生泄漏私沮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一和橙、第九天 我趴在偏房一處隱蔽的房頂上張望仔燕。 院中可真熱鬧,春花似錦魔招、人聲如沸晰搀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厕隧。三九已至,卻和暖如春俄周,著一層夾襖步出監(jiān)牢的瞬間吁讨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工峦朗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留建丧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓波势,卻偏偏與公主長(zhǎng)得像翎朱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尺铣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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