筆記七:模塊化開發(fā)與規(guī)范化標準

模塊化開發(fā)

當(dāng)下最重要的前段開發(fā)范式,“模塊化”是一種思想

模塊化演變過程

早期在沒有工具和規(guī)范的情況下脱篙,對模塊化的落地范式

  • Stage 1 - 文件劃分范式
    • 污染全局作用域
    • 命名沖突
    • 無法管理模塊依賴關(guān)系
    • 早期模塊化完全依靠約定
  • Stage 2 - 命名空間方式舌狗,每個模塊只暴露一個全局對象劳曹,所有模塊成員都掛載到這個對象中
    • 模塊成員可以被修改
  • Stage 3 - IIFE歼捐,使用立即執(zhí)行函數(shù)表達式(Immediately-Invoked Function Expression)為模塊提供私有空間

模塊化演變過程

模塊化標準 + 模塊加載器

CommonJS規(guī)范
  • 一個文件就是一個模塊
  • 每個模塊都有單獨的作用域
  • 通過module.exports到處成員
  • 通過require函數(shù)載入模塊
  • CommonJS是已同步模式加載模塊
AMD(Asynchronous Module Definition), require.js
  • define函數(shù)跃洛,定義一個模塊
  • require函數(shù),載入一個模塊
  • 目前絕大多數(shù)第三方庫都支持AMD規(guī)范
  • AMD使用起來相對復(fù)雜
  • 模塊JS文件請求頻繁
Sea.js + CMD(Common Module Definition)
  • Sea.js,淘寶團隊推出的庫蜜宪,類似CommonJS規(guī)范
  • 使用上有點類似require.js

模塊化標準規(guī)范

模塊化的最佳實踐
  • node.js環(huán)境中虫埂,遵循CommonJS規(guī)范
  • 瀏覽器環(huán)境中,遵循ES Modules規(guī)范
ES Modules基本特性
  • 自動采用嚴格模式圃验,忽略'use strict'
  • 每個ESM模塊都是單獨私有作用域
  • ESM是通過CORS去請求外部JS模塊的
  • ESM的script標簽回延遲執(zhí)行腳本
ES Modules注意事項
  • export {}這是一個固定的語法掉伏,不是es6中的對象簡寫
  • import {}這是一個固定的語法,不是es6中的對象解構(gòu)
  • 到處得到的是對值的飲用澳窑,模塊內(nèi)部修改了值斧散,外部也會跟著改變
  • 導(dǎo)入的成員是只讀成員
ES Modules導(dǎo)出和導(dǎo)入
  • export 注意有無default關(guān)鍵字
  • 不能省略文件后綴名,不能省略./
  • 執(zhí)行某個模塊摊聋,不需要提取模塊中的成員import './module.js'
  • 動態(tài)導(dǎo)入模塊鸡捐,可以用全局函數(shù)import()

ES Modules in Node.js

支持情況
  • 執(zhí)行文件時,node --experimental-modules index.mjs
  • ES Module中可以導(dǎo)入CommonJS模塊
  • CommonJS中不能導(dǎo)入ES Module模塊
  • CommonJS始終只會導(dǎo)出一個默認成員
  • 注意import不是解構(gòu)導(dǎo)出對象
與 CommonJS 模塊的差異
  • ESM 中沒有 CommonJS 中的那些模塊全局成員了(require module exports __filename __dirname)
  • 利用 import 和 url path 模塊實現(xiàn)
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
console.log(__filename);

const __dirname = dirname(__filename);
console.log(__dirname);
新版本的 node 進一步支持 ESM
  • babel是基于插件機制實現(xiàn)的麻裁,核心模塊并不會轉(zhuǎn)換代碼
  • 具體轉(zhuǎn)換代碼是通過插件來做的()
  • @babel/preset-env 是一個插件的集合箍镜,它包含了最新的JS標準中的所有新特性
  • @babel/plugin-transform-modules-commonjs 這才是一個具體的插件

Webpack 打包

打包工具解決的是前端整體的模塊化,并不單指 JavaScript 模塊化

webpack 工作模式
  • mode: 'production',
  • mode: 'development',
  • mode: 'none',
webpack 資源模塊加載
  • JS file => Default Loader
  • Other file => Other Loader
webpack 導(dǎo)入資源模塊
  • JavaScript 驅(qū)動整個前端應(yīng)用
  • 在 js 中導(dǎo)入相關(guān)資源模塊悲立,邏輯合理鹿寨,JS 確實需要這些資源文件
  • 確保上線資源不缺失,都是必要的
    學(xué)習(xí)一個新事物薪夕,不是學(xué)會它的所有用法就能提高,掌握新事物的思想才是突破點赫悄。能夠搞明白這些新事物為什么這樣設(shè)計原献,那就基本上算是出道了。
webpack 文件資源加載器
  • JS file => Default Loader => Bundle.js
  • 圖片埂淮、字體等資源文件 => File Loader => 文件路徑 => Bundle.js
webpack URL 加載器

協(xié)議 媒體類型和編碼 文件內(nèi)容
data:<mediatype>,<data>
data:text/html;charset-UTF-8,<h1>content</h1>
data:image/png;base64,iVBORw0KGg...SuQmCC
最佳實踐

  • 小文件使用 Data URLs姑隅,減少請求次數(shù)
  • 大文件單獨提取存放,提高加載速度
{
    test: /.png$/,
    use: {
        loader: 'url-loader',
        options: {
            limit: 10 * 1024 // 10 KB
        }
    }
}
  • 超出 10KB 文件單獨提取存放
  • 小于 10KB 文件轉(zhuǎn)換為 Data URLS 嵌入代碼中
webpack 常用加載器分類
  • 編譯轉(zhuǎn)換類(css-loader => 以 JS 形式工作的我 CSS 模塊)
  • 文件操作類(file-loader => 導(dǎo)出文件訪問路徑)
  • 代碼檢查類(eslint-loader => 檢查通過/不通過)
webpack 加載資源的方式
  • 遵循 ES Modules 標準的 import 聲明
  • 遵循 CommonJS 標準的 require 函數(shù)
  • 遵循 AMD 標準的 define 函數(shù)和 require 函數(shù)
  • 樣式代碼中的 @import 執(zhí)行和 url 函數(shù)
  • HTML 代碼中圖片標簽的 src 屬性
webpack 核心工作原理

Loader 機制是 Webpack 的核心

webpack 插件機制
  • Loader 專注實現(xiàn)資源模塊加載
  • Plugin 解決其他自動化工作
  • Plugin 用途:
    • 打包之前清除 dist 目錄
    • 拷貝靜態(tài)文件至輸出目錄
    • 壓縮輸出代碼
  • 常用的插件:
    • clean-webpack-plugin 打包之前清除 dist 目錄
    • html-webpack-plugin 用于生成 index.html 文件
    • copy-webpack-plugin 拷貝靜態(tài)文件至輸出目錄
      開發(fā)一個插件:插件是通過在生命周期的鉤子中掛載函數(shù)實現(xiàn)擴展
如何增強 webpack 開發(fā)體驗
  • 自動編譯
  • 自動刷新瀏覽器
  • webpack-dev-server:繼承了以上特性的工具
Source Map
  • 運行代碼與源代碼之間完全不同
  • 如果需要調(diào)試應(yīng)用倔撞,或者運行應(yīng)用過程中出現(xiàn)了錯誤讲仰,錯誤信息無法定位
  • 調(diào)試和報錯都是基于運行代碼
  • Source Map 解決了源代碼與運行代碼不一致所產(chǎn)生的問題
Source Map的方式

webpack 支持 12 種不同的 source-map 方式,每種方式的效率和效果各不相同
不同 devtool 之間的差異

  • eval - 是否使用 eval 執(zhí)行模塊代碼
  • cheap - Source Map 是否包含行信息
  • module - 是否能夠得到 Loader 處理之前的源代碼
選擇合適的 Source Map
  • 開發(fā)模式:cheap-module-eval-source-map
    • 我的代碼每行不會超過 80 個字符
    • 我的代碼經(jīng)過 Loader 轉(zhuǎn)換過后的差異較大
    • 首次打包速度慢無所謂痪蝇,重新打包速度較快
  • 生產(chǎn)環(huán)境:none / nosources-source-map
    • 安全隱患鄙陡,source-map 會暴露源代碼
    • 調(diào)試是開發(fā)階段的事情
    • 沒有絕對的選擇,理解不同模式的差異躏啰,適配不同的環(huán)境
HMR 體驗

HMR(Hot Module Replacement): 模塊熱替換

  • 應(yīng)用運行過程中實時替換某個模塊
  • 應(yīng)用運行狀態(tài)不受影響
  • 自動刷新會導(dǎo)致頁面狀態(tài)丟失
  • 熱替換只將修改的模塊實時替換至應(yīng)用中
開啟 HMR

集成在 webpack-dev-server 中

  • webpack-dev-server --hot
  • 也可以通過配置文件開啟
HMR 的疑問
  • webpack 中的 HMR 并不可以開箱即用
  • webpack 中的 HMR 需要手動處理模塊熱替換邏輯
  • 為什么樣式文件的熱更新開箱即用趁矾?因為樣式經(jīng)過了 loader 處理,然后只需要替換掉某段 <style></style> 就可以實現(xiàn)
  • 我的項目沒有手動處理给僵,JS 照樣可以熱替換毫捣?因為使用了框架,框架下的開發(fā),每種文件都是有規(guī)律的
  • 通過腳手架創(chuàng)建的項目內(nèi)部都集成了 HMR 方案
    總結(jié):我們需要手動處理 JS 模塊更新后的熱替換
Webpack 生產(chǎn)環(huán)境優(yōu)化
  • 生產(chǎn)環(huán)境跟開發(fā)環(huán)境有很大差異
  • 生產(chǎn)環(huán)境注重運行效率蔓同,開發(fā)環(huán)境注重開發(fā)效率
  • 模式(mode)饶辙,為不同的工作環(huán)境創(chuàng)建不同的配置
Webpack Tree Shaking
  • 盡可能的將所有模塊合并輸出到一個函數(shù)中
  • 既提升了運行效率,又減少了代碼體積
  • Tree Shaking 又被稱為 Scope Hoisting 作用域提升
Webpack Tree Shaking 與 Babel
  • Tree Shaking 前提是 ES Modules
  • 由 Webpack 打包的代碼必須使用 ESM
  • 為了轉(zhuǎn)換代碼中的 ECMAScript 新特性而使用 babel-loader 斑粱,就有可能導(dǎo)致 ESM => CommonJS畸悬,這取決我們有沒有使用轉(zhuǎn)換 ESM 的插件
Webpack 代碼分割

代碼分包

  • 所有代碼最終都被打包到一起,bundle 體積過大
  • 并不是每個模塊在啟動時都是必要的
  • 模塊打包是必要的珊佣,但是應(yīng)用越來越大之后蹋宦,需要進行分包,按需加載
  • 有兩種方式:多入口打包咒锻;ESM 動態(tài)導(dǎo)入
多入口打包
  • 常用于多頁應(yīng)用程序
  • 一個頁面對應(yīng)一個打包入口
  • 公共部分單獨提取
動態(tài)導(dǎo)入
  • 按需加載冷冗,需要用到某個模塊時,再加載這個模塊
  • 可以極大地節(jié)省帶寬和流量
  • 無需配置任何地方惑艇,只需要按照 ESM 動態(tài)導(dǎo)入的方式去導(dǎo)入模塊蒿辙,webpack 內(nèi)部會自動處理分包和按需加載
  • 使用單頁應(yīng)用開發(fā)框架(React/Vue),在項目中的路由映射組件就可以通過動態(tài)導(dǎo)入實現(xiàn)按需加載
Webpack 魔法注釋
  • 使用魔法注釋可以為動態(tài)導(dǎo)入最終打包出來的文件命名
  • 命名相同的模塊最終會被打包到一起
Webpack 輸出文件名 Hash
  • 一般我們部署前端資源文件時滨巴,都會采用服務(wù)器的靜態(tài)資源緩存
  • 開啟緩存的問題:緩存時間過短-效果不明顯思灌,緩存過期時間較長-應(yīng)用發(fā)生了更新重新部署后客戶端因為緩存得不到更新
  • 解決上面問題,建議生產(chǎn)模式下恭取,文件名使用 Hash泰偿,文件名不同也就是新的請求,解決了緩存的問題蜈垮,服務(wù)器可以將緩存過期時間設(shè)置足夠長
  • 三種 Hash 方式
    • hash: 整個項目級別的耗跛,項目中任意一個地方改動,重新打包之后的 hash 值都會改變
    • chunkhash: chunk 級別的攒发,同一路的打包 chunkhash 都是相同的
    • contenthash: 文件級別的hash调塌,根據(jù)文件內(nèi)容生成的hash值,不同的文件就有不同的值
      解決緩存問題的最佳 hash 方式 [contenthash:8]

Rollup

Rollup 概述
  • Rollup 與 Webpack作用類似
  • Rollup 更為小巧
  • 僅僅是一款 ESM 打包器
  • Rollup 中并不支持類似 HMR 這種高級特性
  • Rollup 的初衷是提供一個充分利用 ESM 各項特性的高效打包器
Rollup 快速上手
# 安裝依賴
yarn add rollup --dev

# 指定打包的入口文件惠猿、打包輸出格式羔砾、輸出結(jié)果路徑,執(zhí)行打包
yarn rollup ./src/index.js --format iife --file dist/bundle.js
Rollup 配置文件
  • 在項目根目錄創(chuàng)建 rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  }
}
  • 執(zhí)行命令 yarn rollup --config 完成打包偶妖,也可以在命令最后跟上文件名
Rollup 使用插件
  • 想加載其他類型的資源模塊
  • 想導(dǎo)入 CommonJS 模塊姜凄、編譯 ECMAScript 新特性
  • Rollup 支持使用插件的方式擴展,插件是 Rollup 唯一擴展途徑
  • rollup-plugin-json 加載 json 文件的插件
  • rollup-plugin-node-resolve 加載 npm 模塊的插件
  • rollup-plugin-commonjs 加載 CommonJS 模塊
Rollup 代碼拆分

使用 Dynamic Imports 動態(tài)導(dǎo)入實現(xiàn)模塊按需加載餐屎,實現(xiàn)代碼拆分/分包
rollup.config.js 修改為:

export default {
  input: 'src/index.js',
  output: {
    // file: 'dist/bundle.js',
    // format: 'iife'
    dir: 'dist',
    format: 'amd'
  } 
}
Rollup 多入口打包
  • 將 rollup.config.js 文件中的 input 改為一個數(shù)組 或者 對象
  • 對于以 amd 格式輸出的文件檀葛,不能直接引入到頁面上,需要配合 Require.js 這樣的庫使用
Rollup VS Webpack 選用原則
  • 優(yōu)點:
    • 輸出結(jié)果更加扁平
    • 自動移除未引用的代碼
    • 打包結(jié)果依然完全可讀
  • 缺點:
    • 加載非 ESM 的第三方模塊比較復(fù)雜
    • 模塊最終都被打包到一個函數(shù)中腹缩,無法實現(xiàn) HMR
    • 瀏覽器環(huán)境中屿聋,代碼拆分功能依賴 AMD
  • 選用原則:
    • 如果我們正在開發(fā)應(yīng)用程序 => webpack
    • 如果我們正在開發(fā)框架或者類庫 => rollup
    • 大多數(shù)知名框架 / 庫都在使用 rollup
    • 社區(qū)中希望二者共存空扎,webpack 大而全,rollup 小而美

規(guī)范化標準

規(guī)范化標準介紹

規(guī)范化是我們踐行前端工程化中重要的一部分

  • 為什么要有規(guī)范會標準润讥?
    • 軟件開發(fā)需要多人協(xié)同
    • 不同開發(fā)者具有不同的編碼習(xí)慣和喜好
    • 不同的喜好會增加項目的維護成本
    • 每個項目或者團隊需要明確統(tǒng)一的標準
  • 哪里需要規(guī)范化標準转锈?
    • 代碼、文檔楚殿、甚至是提交日志
    • 開發(fā)過程中人為編寫的成果物
    • 代碼標準化規(guī)范最為重要
  • 實施規(guī)范化的方法
    • 編碼前人為的標準約定
    • 通過工具實現(xiàn) Lint
  • 常見的規(guī)范化實現(xiàn)方式
    • ESLint 工具使用
    • 定制 ESLint 校驗規(guī)則
    • ESLint 對 TypeScript 的支持
    • ESLint 結(jié)合自動化工具或者 Webpack
    • 基于 ESLint 的衍生工具
    • StyleLint 工具的使用
ESLint 介紹
  • 最為主流的 JavaScript Lint 工具撮慨,檢測 JS 代碼質(zhì)量
  • ESLint 很容易統(tǒng)一開發(fā)者的編碼風(fēng)格
  • ESLint 可以幫助開發(fā)者提升編碼能力
ESLint 快速上手
  • 初始化項目,安裝 ESLint 模塊為開發(fā)依賴 npm install eslint -D
  • 編寫“問題”代碼脆粥,使用 eslint 執(zhí)行檢測 npx eslint ./01-prepare.js 加上參數(shù) --fix 可以自動修復(fù)格式問題
  • 當(dāng)代碼中存在語法錯誤時砌溺,eslint 沒法檢查問題代碼
  • 完成 eslint 使用配置
結(jié)合自動化工具
  • 集成之后,ESLint 一定會工作
  • 與項目統(tǒng)一变隔,管理更加方便
  • 結(jié)合 gulp 使用规伐,通過 .pipe(plugins.eslint()) 讓其工作
ESLint 結(jié)合 Webpack
  • Webpack 可以通過 loader 機制實現(xiàn) eslint 的檢測工作
  • 安裝 eslint eslint-loader
  • 在 webpack.config.js 文件配置 eslint-loader 應(yīng)用在 .js 文件中
  • 安裝相關(guān)插件,如:eslint-plugin-react
  • 修改 .eslintrc.js 的配置
ESLint 檢查 TypeScript
  • 初始化項目
  • 安裝 eslint typescript
  • 初始化 .eslintrc.js 配置文件匣缘,注意當(dāng)詢問 use TypeScript ? 是要選擇 yes
  • 執(zhí)行 npx eslint .\index.ts
Stylelint 認識
  • 提供默認的代碼檢查規(guī)則
  • 提供 CLI 工具猖闪,快速調(diào)用
  • 通過插件支持 Sass Less PostCSS
  • 支持 Gulp 或 Webpack 集成
  • 快速上手
    • 安裝 stylelint npm i stylelint -D
    • 安裝 standard 插件 npm i stylelint-config-standard -D
    • 創(chuàng)建 .stylelintrc.js 配置文件,并修改 extends 字段
module.exports = {
    extends: 'stylelint-config-standard'
}
  • 執(zhí)行 npx stylelint ./index.css肌厨,加上參數(shù) --fix 可以自動修復(fù)部分格式問題
  • 檢查 sass 文件培慌,執(zhí)行 npm i stylelint-config-sass-guidelines -D,修改 .stylelintrc.js 文件中的 extends 為數(shù)組柑爸,添加 sass 插件
Prettier 的使用
  • Prettier 幾乎可以完成所有類型文件的格式化工作
  • 安裝吵护, npm i prettier -D
  • 檢查某個文件并輸出檢查結(jié)果,npx prettier style.css
  • 檢查并格式化某個文件竖配,npx prettier style.css --write
  • 檢查并格式化項目所有文件何址,npx prettier . --write
ESLint 結(jié)合 Git Hooks
  • Git Hooks
    • 代碼提交至倉庫之前未執(zhí)行 lint 工作
    • 使用 lint 的目的就是保證提交到倉庫的代碼是沒有問題的
    • 通過 Git Hooks 在代碼提交前強制 lint
    • Git Hooks 也稱為 git 鉤子,每個鉤子都對應(yīng)一個任務(wù)
    • 通過 shell 腳本可以編寫鉤子任務(wù)觸發(fā)時要具體執(zhí)行的操作
  • 快速上手
    • 很多前端開發(fā)者并不擅長使用 shell
    • Husky 可以實現(xiàn) Git Hooks 的使用需求 npm i husky -D进胯,然后在 package.json 中添加如下配置
    "husky": {
        "hooks": {
            "pre-commit": "npm run lint"
        }
    }
  • 配合 lint-stage 使用,npm i lint-staged -D
  "lint-staged": {
        "*.js*": [
            "eslint",
            "git add"
        ]
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末原押,一起剝皮案震驚了整個濱河市胁镐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诸衔,老刑警劉巖盯漂,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異笨农,居然都是意外死亡就缆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門谒亦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竭宰,“玉大人空郊,你說我怎么就攤上這事∏薪遥” “怎么了狞甚?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長廓旬。 經(jīng)常有香客問我哼审,道長,這世上最難降的妖魔是什么孕豹? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任涩盾,我火速辦了婚禮,結(jié)果婚禮上励背,老公的妹妹穿的比我還像新娘春霍。我一直安慰自己,他們只是感情好椅野,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布终畅。 她就那樣靜靜地躺著,像睡著了一般竟闪。 火紅的嫁衣襯著肌膚如雪离福。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天炼蛤,我揣著相機與錄音妖爷,去河邊找鬼。 笑死理朋,一個胖子當(dāng)著我的面吹牛絮识,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗽上,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼次舌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兽愤?” 一聲冷哼從身側(cè)響起彼念,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浅萧,沒想到半個月后逐沙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡洼畅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年吩案,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帝簇。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡徘郭,死狀恐怖靠益,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崎岂,我是刑警寧澤捆毫,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站冲甘,受9級特大地震影響绩卤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜江醇,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一濒憋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧陶夜,春花似錦凛驮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至羽嫡,卻和暖如春本姥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杭棵。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工婚惫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人魂爪。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓先舷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親滓侍。 傳聞我的和親對象是個殘疾皇子蒋川,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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