Webpack

1. 簡介
  • 本質(zhì)上,webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)碰镜。當(dāng) webpack 處理應(yīng)用程序時,它會遞歸地構(gòu)建一個依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個模塊毕箍,然后將所有這些模塊打包成一個或多個 bundle.
2. 核心概念
  • 入口(entry)

1.指示 webpack 應(yīng)該使用哪個模塊,來作為構(gòu)建其內(nèi)部依賴圖的開始值骇。進入入口起點后莹菱,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。

2.可以通過在 webpack 配置中配置 entry 屬性吱瘩,來指定一個入口起點(或多個入口起點)道伟。默認值為./src

  • 輸出(output)

output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles使碾,以及如何命名這些文件蜜徽,默認值為 ./dist〔看基本上娜汁,整個應(yīng)用程序結(jié)構(gòu),都會被編譯到你指定的輸出路徑的文件夾中兄朋。你可以通過在配置中指定一個 output 字段掐禁,來配置這些處理過程

  • 加載(loader)

  • loaderwebpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉(zhuǎn)換為 webpack 能夠處理的有效模塊

  • 在更高層面颅和,在 webpack 的配置中 loader 有兩個目標(biāo):

1.test 屬性傅事,用于標(biāo)識出應(yīng)該被對應(yīng)的 loader 進行轉(zhuǎn)換的某個或某些文件。

2.use 屬性峡扩,表示進行轉(zhuǎn)換時蹭越,應(yīng)該使用哪個 loader

注意:Webpack選擇了compose方式教届,即從右到左執(zhí)行l(wèi)oader

  • 插件(plugins)

1.插件的范圍包括响鹃,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量案训。插件接口功能極其強大买置,可以用來處理各種各樣的任務(wù)。

2.plugins需要暴露出一個class, 在new WebpackPlugin()的時候通過構(gòu)造函數(shù)傳入這個插件需要的參數(shù)强霎,在webpack啟動的時候會先實例化plugin再調(diào)用plugin.apply()方法忿项,插件需要在apply函數(shù)里監(jiān)聽webpack生命周期里的事件,做相應(yīng)的處理

  • 模式(mode)

通過選擇 developmentproduction 之中的一個城舞,來設(shè)置 mode 參數(shù)轩触,你可以啟用相應(yīng)模式下的 webpack 內(nèi)置的優(yōu)化


// 多個入口

module.exports = {

  mode: 'production',

  entry: {

index: ["./src/index.js"],

main: ["./src/main.js"]

  },

  output: {

path: path.resolve(__dirname, 'dist'),

filename: 'js/[name].[hash:8].js'

  },

  module: {

rules: [{

  test: /\.js$/, // 正則匹配文件名

  exclude: '/node_modules/', // 排除

  use: ['babel-loader']

}

  },

  plugins: [ // 插件

new copyWebpackPlugin([{

  from: path.resolve(__dirname, 'public/static'),

  to: path.resolve(__dirname, 'dist'),

  ignore: ['index.html']

  }])

}

3. 基本流程
  • 解析shellconfig中的配置項,用于激活webpack的加載項和插件

  • webpack初始化工作家夺,包括構(gòu)建compiler對象脱柱,初始化compiler的上下文,loaderfile的輸入輸出環(huán)境

  • 解析入口js文件拉馋,通過對應(yīng)的工廠方法創(chuàng)建模塊褐捻,使用acron生成AST樹并且遍歷AST掸茅,處理requiremodule,如果依賴中包含依賴則遍歷build module柠逞,在遍歷過程中會根據(jù)文件類型和loader配置找出合適的loader用來對文件進行轉(zhuǎn)換

  • 調(diào)用seal方法昧狮,封裝,逐次對每一個module板壮,chunk進行整理逗鸣,生成編輯后的代碼

4. 模塊打包
  • 通過fs將模塊讀取成字符串,然后用warp包裹一下绰精,使之成為一個字符串形式的的函數(shù)然后調(diào)用 vm.runInNewContext這樣類型的方法撒璧,這個字符串會變成一個函數(shù)。

  • 這些模塊的函數(shù)會被存放在數(shù)組里笨使,然后進行解析執(zhí)行卿樱。moduleexport都是傳入的對象,webpack會實現(xiàn)require函數(shù)硫椰,去加載其他模塊繁调。

  • 如果是異步模塊,則會通過jsonp的形式去加載該模塊打包好生成的chunk靶草。異步加載模塊可以使用importrequire.ensure函數(shù)蹄胰,函數(shù)將會返回一個promise

  • 上面方法都是公共的奕翔,可以抽離成模板的js文件裕寨,webpack負責(zé)做依賴分析,并將模塊讀成函數(shù)填充入數(shù)組派继。(這里說的只是js的模塊)


<!-- 同步模塊 -->

var moduleDepList = [

  {'./moduleA': 1}, // module[0] 的依賴 他依賴moduleA 且 moduleA的下標(biāo)在moduleList 中 為 1

  {}

]



function require(id, parentId) {

  var currentModlueId = parentId !== undefined ? moduleDepList[parentId][id] : id

  var module = {exports: {}}

  var moduleFunc = moduleList[currentModlueId]

  moduleFunc(id => require(id, currentModlueId), module, module.exports)

  return module.exports

}


<!-- 異步模塊 -->

var cache = {}

window.__jsonp = function(chunkId, moduleFunc) {

  var chunk = cache[chunkId]

  var resolve = chunk[0]

  var module = {exports: {}}

  moduleFunc(require, module, module.exports)

  resolve(module.exports)

}

require.ensure = function(chunkId, parentId) {

  var currentModlueId = parentId !== undefined ? moduleDepList[parentId][chunkId] : chunkId

  var currentChunk = cache[currentModlueId]

  if (currentChunk === undefined) {

var $script = document.createElement('script')

$script.src = `chunk.${chunkId}.js`

document.body.appendChild($script)

var promise = new Promise(function(resolve) {

  var chunkCache = [resolve] // 數(shù)組形式是為了保存promise

  chunkCache.status = true // 異步模塊加載中 如果有別的包 在 異步加載在模塊 那么下面的

  cache[chunkId] = chunkCache

})

cache[chunkId].push(promise)

return promise

  }

  if (currentChunk.status) {

return currentChunk[1] // 這里的promise 這里的就直接返回promise 這樣模塊只會加載一次

  }

  return currentChunk

}

5. 熱更新
  • clientserver 建立一個 websocket 通信

  • 當(dāng)有文件發(fā)生變動(如fs.watchFile)的時候宾袜,webpack編譯文件,并通過 websocketclient發(fā)送一條更新消息

  • client 根據(jù)收到的hash值驾窟,通過ajax獲取一個 manifest 描述文件

  • client 根據(jù)manifest 獲取新的JS模塊的代碼

  • 當(dāng)取到新的JS代碼之后庆猫,會更新 modules tree,(installedModules)調(diào)用之前通過 module.hot.accept注冊好的回調(diào)纫普,可能是loader提供的阅悍,也可能是你自己寫的

  • manifest: 描述資源文件對應(yīng)關(guān)系如下好渠,打包后的文件擁有了hash值昨稼,所以需要進行映射。


{

  "a.js": "a.41231243.js"

}

6. 如何開發(fā)一個plugin
  • 一個 JavaScript 命名函數(shù)拳锚。

  • 在插件函數(shù)的 prototype 上定義一個 apply 方法假栓。

  • 指定一個綁定到 webpack 自身的事件鉤子。

  • 處理 webpack 內(nèi)部實例的特定數(shù)據(jù)霍掺。

  • 功能完成后調(diào)用 webpack 提供的回調(diào)匾荆。

tapable 工具拌蜘,它提供了 webpack 插件接口的支柱


// 一個 JavaScript 命名函數(shù)。

function plugin() {};

// 在插件函數(shù)的 prototype 上定義一個 `apply` 方法牙丽。

plugin.prototype.apply = function(compiler) {

  // 指定一個掛載到 webpack 自身的事件鉤子简卧。

  compiler.plugin('webpacksEventHook', function(compilation, callback) {

callback();

  });



  // 使用taptable的寫法

  //基本寫法

  compiler.hooks.someHook.tap(...)

  //如果希望在entry配置完畢后執(zhí)行某個功能

  compiler.hooks.entryOption.tap(...)

  //如果希望在生成的資源輸出到output指定目錄之前執(zhí)行某個功能

  compiler.hooks.emit.tap(...)

};

7. Compiler和Compliation 對象和鉤子
  • 對象

1.compiler 對象代表了完整的 webpack 環(huán)境配置。這個對象在啟動 webpack 時被一次性建立烤芦,并配置好所有可操作的設(shè)置举娩,包括 options,loader 和 plugin构罗。

2.compilation 對象代表了一次資源版本構(gòu)建铜涉。當(dāng)運行 webpack 開發(fā)環(huán)境中間件時,每當(dāng)檢測到一個文件變化遂唧,就會創(chuàng)建一個新的 compilation芙代,從而生成一組新的編譯資源。一個 compilation 對象表現(xiàn)了當(dāng)前的模塊資源盖彭、編譯生成資源纹烹、變化的文件、以及被跟蹤依賴的狀態(tài)信息谬泌。compilation 對象也提供了很多關(guān)鍵時機的回調(diào)滔韵,以供插件做自定義處理時選擇使用

  • 鉤子:總體分成兩大類:Compiler和Compliation

1.Compiler暴露了和webpack整個生命周期相關(guān)的鉤子

2.Compilation暴露了與模塊和依賴有關(guān)的粒度更小的事件鉤子,官方文檔中的說法是模塊會經(jīng)歷加載(loaded),封存(sealed),優(yōu)化(optimized),分塊(chunked),哈希(hashed)和重新創(chuàng)建(restored)這幾個典型步驟掌实,從上面的示例可以看到陪蜻,compilation是Compiler生命周期中的一個步驟,使用compilation相關(guān)鉤子的通用寫法為:


compiler.hooks.compilation.tap('SomePlugin',function(compilation, callback){

compilation.hooks.someOtherHook.tap('SomeOtherPlugin',function(){

....

})

});

  • 鉤子的類型

1.同步鉤子

(1)syncHook: 不關(guān)心返回值

(2)syncBailHook: 有一個返回值不為null就跳過剩下的邏輯

(3)SyncWaterfallHook: 下一個任務(wù)要拿到上一個任務(wù)的返回值

(4)SyncLoopHook: 監(jiān)聽函數(shù)返回true表示繼續(xù)循環(huán)贱鼻,返回undefine表示結(jié)束循環(huán)

2.異步鉤子

(1)AsyncParallelHook: 異步并發(fā)執(zhí)行宴卖,仍是單線程

(2)AsyncParallelBailHook: 異步并發(fā)執(zhí)行,有一個失敗了邻悬,其他的都不用走了

(3)AsyncSeriesHook: 異步串行執(zhí)行

(4)AsyncSeriesBailHook: 異步串行執(zhí)行症昏,有一個返回值不為null就跳過剩下的邏輯

(5)AsyncSeriesWaterfallHook: 異步串行執(zhí)行,下一個任務(wù)要拿到上一個任務(wù)的返回值

8. 常見plugin
  • clean-webpack-plugin: 在構(gòu)建之前刪除上一次build的文件夾

  • copy-webpack-plugin: 復(fù)制文件或文件夾到生成后的目錄

  • extract-text-webpack | mini-css-extract-plugin: 將所有入口的chunk(entry chunks)中引用的 *.css父丰,移動到獨立分離的 CSS 文件

  • html-webpack-plugin: 將build后生成的資源以標(biāo)簽的形式嵌入到HTML模板內(nèi)

  • hot-module-replacement: 模塊熱更新

9. 常見loader
  • babel-loader: 語法肝谭,源碼轉(zhuǎn)換以便能夠運行在當(dāng)前和舊版本的瀏覽器或其他環(huán)境中

  • css-loader: 配合style-loader可以解析在js中引入的css文件,并以<style>便簽將css-loader內(nèi)部樣式注入到我們的HTML頁面

  • file-loader: 可以解析js中require的文件蛾扇,輸出到輸出目錄并返回 public URL

  • html-loader: 可以對HTML模板中指定哪個標(biāo)簽屬性組合(tag-attribute combination)元素應(yīng)該被此 loader 處理

  • less-loader: 依賴less攘烛,可以將less編譯成css

  • postcss-loader: 配合一些plugin如cssnano,autoprefixer可以對css進行壓縮,優(yōu)化镀首,自動補足前綴等

  • scss-loader: 配合node-scss坟漱,可以將scss編譯成css

  • style-loader: 配合css-loader可以解析在js中引入的css文件,并以<style>便簽將css-loader內(nèi)部樣式注入到我們的HTML頁面

  • url-loader: url-loader 功能類似于 file-loader更哄,但是在文件大杏蟪荨(單位 byte)低于指定的限制時腥寇,可以返回一個 DataURL(base64)

10. 常見打包優(yōu)化
  • 使用dll

  • 移除prefetch, preload觅捆,關(guān)閉sourceMap

  • webpack-bundle-analyzer打包分析赦役,將大的模塊可能的移至CDN。打包時間分析使用speed-measure-webpack-plugin

  • 開啟gzip栅炒,服務(wù)器需要支持

  • 使用多線程:thread-loader或HappyPack

  • webpack4內(nèi)置的terser啟動多線程壓縮

  • 對項目進行拆分

11. 性能優(yōu)化
  • webapck優(yōu)化與開啟gzip壓縮

1.babel-loaderincludeexclude 來幫我們避免不必要的轉(zhuǎn)譯扩劝,不轉(zhuǎn)譯node_moudules中的js文件,其次在緩存當(dāng)前轉(zhuǎn)譯的js文件职辅,設(shè)置loader: 'babel-loader?cacheDirectory=true'

2.文件采用按需加載等等

3.具體的做法非常簡單棒呛,只需要你在你的 request headers 中加上這么一句:

accept-encoding:gzip,該功能需要服務(wù)器支持才能正常顯示頁面域携。

4.圖片優(yōu)化簇秒,采用svg圖片或者字體圖標(biāo)

5.瀏覽器緩存機制,它又分為強緩存和協(xié)商緩存

  • 本地存儲——從 CookieWeb Storage秀鞭、IndexedDB

說明一下SessionStoragelocalStorage還有cookie的區(qū)別和優(yōu)缺點

  • 代碼優(yōu)化

1.事件代理

2.事件的節(jié)流和防抖

3.頁面的回流和重繪

4.EventLoop事件循環(huán)機制

5.代碼優(yōu)化等等

概念

1. MVVM
  • View 和 Model 之間并沒有直接的聯(lián)系趋观,而是通過ViewModel進行交互,Model 和 ViewModel 之間的交互是雙向的锋边, 因此View 數(shù)據(jù)的變化會同步到Model中皱坛,而Model 數(shù)據(jù)的變化也會立即反應(yīng)到View 上。

  • ViewModel 通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來豆巨,而View 和 Model 之間的同步工作完全是自動的剩辟,無需人為干涉,因此開發(fā)者只需關(guān)注業(yè)務(wù)邏輯往扔,不需要手動操作DOM, 不需要關(guān)注數(shù)據(jù)狀態(tài)的同步問題贩猎,復(fù)雜的數(shù)據(jù)狀態(tài)維護完全由 MVVM 來統(tǒng)一管理。

2. 組件化思想
  • 簡單的說組件就是:將一段UI樣式和其對應(yīng)的功能作為獨立的整體去看待萍膛,無論這個整體放在哪里去使用吭服,它都具有一樣的功能和樣式,從而實現(xiàn)復(fù)用蝗罗,這種整體化的思想就是組件化艇棕。

  • 組件化設(shè)計就是為了增加復(fù)用性,靈活性串塑,提高系統(tǒng)設(shè)計沼琉,從而提高開發(fā)效率。

3. 虛擬DOM
  • 使用Javascript來操縱DOM拟赊,操作效率往往很低刺桃,由于DOM被表示為樹結(jié)構(gòu)粹淋,每次DOM中的某些內(nèi)容都會發(fā)生變化吸祟,因此對DOM的更改非成龋快,但更改后的元素屋匕,并且它的子項必須經(jīng)過Reflow / Layout階段葛碧,然后瀏覽器必須重新繪制更改,這很慢的过吻。

  • 因此进泼,回流/重繪的次數(shù)越多,您的應(yīng)用程序就越卡頓纤虽。但是乳绕,Javascript運行速度很快,虛擬DOM是放在JS 和 HTML中間的一個層逼纸。它可以通過新舊DOM的對比洋措,來獲取對比之后的差異對象,然后有針對性的把差異部分真正地渲染到頁面上杰刽,從而減少實際DOM操作菠发,最終達到性能優(yōu)化的目的。

4. SPA 和 多頁面應(yīng)用
  • 單頁面應(yīng)用: 僅僅在web頁面初始化時加載相應(yīng)的HTML贺嫂、JavaScript滓鸠、CSS,一旦頁面加載完成了第喳,SPA不會因為用戶的操作而進行頁面的重新加載或跳轉(zhuǎn)糜俗,而是利用 JavaScript 動態(tài)的變換HTML的內(nèi)容,從而實現(xiàn)UI與用戶的交互曲饱。

  • 多頁面應(yīng)用: 多頁面跳轉(zhuǎn)刷新所有資源吩跋,每個公共資源(js、css等)需選擇性重新加載渔工,常用于 app 或 客戶端

5. CDN
  • CDN的全稱是Content Delivery Network锌钮,即內(nèi)容分發(fā)網(wǎng)絡(luò)∫兀基本原理是在用戶和服務(wù)器之間增加Cache層梁丘,主要是通過接管DNS實現(xiàn),將用戶的請求引導(dǎo)到Cache上獲得源服務(wù)器的數(shù)據(jù)旺韭,從而降低網(wǎng)絡(luò)的訪問時間氛谜。CDN的關(guān)鍵技術(shù)主要有負載均衡,內(nèi)容存儲和分發(fā)技術(shù)区端。

  • 負載均衡:使用整體性的網(wǎng)絡(luò)負載均衡技術(shù)布隔,通過內(nèi)容路由器中的重定向(DNS)機制,在多個遠程POP上均衡用戶的請求嗅战,以使用戶請求得到最近內(nèi)容源的響應(yīng)。

  • 內(nèi)容分發(fā):借助于建立索引酱塔、緩存、流分裂危虱、組播(Multicast)等技術(shù)羊娃,將內(nèi)容發(fā)布或投遞到距離用戶最近的遠程服務(wù)點(POP)處。

  • 內(nèi)容存儲:在功能上包括對各種內(nèi)容格式的支持埃跷,對部分緩存的支持;在性能上包括支持的容量蕊玷、多文件吞吐率、可靠性弥雹、穩(wěn)定性垃帅,都是存儲需要考慮的問題。

6. 函數(shù)式編程
  • 函數(shù)式編程是種編程方式剪勿,它將電腦運算視為函數(shù)的計算挺智。在函數(shù)編程中,函數(shù)是第一等公民窗宦,且該函數(shù)應(yīng)該是一個純函數(shù)赦颇,即相同的輸入,永遠會得到相同的輸出赴涵,而且沒有任何可觀察的副作用媒怯。

  • 列如含有:

1.log

2.http請求

3.可變數(shù)據(jù)如new Date()

4.DOM操作

  • 純函數(shù)帶來的好處就是:更好的進行單元測試和調(diào)試,一對一的數(shù)據(jù)關(guān)系可以便于緩存髓窜。函數(shù)式編程還有其他特性:

1.閉包和高階函數(shù)

2.惰性計算

3.遞歸

  • 函數(shù)式編程有兩個最基本的運算:合成compose和柯里化curry扇苞。

結(jié)束語

2020前端面試就分享到這里,前端就是一個大雜燴寄纵,亂燉鳖敷,需要會的、了解的東西太多了程拭,學(xué)無止境定踱,如果發(fā)現(xiàn)問題,歡迎評論區(qū)指正恃鞋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末崖媚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恤浪,更是在濱河造成了極大的恐慌畅哑,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件水由,死亡現(xiàn)場離奇詭異荠呐,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門泥张,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呵恢,“玉大人,你說我怎么就攤上這事圾结。” “怎么了齿诉?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵筝野,是天一觀的道長。 經(jīng)常有香客問我粤剧,道長歇竟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任抵恋,我火速辦了婚禮焕议,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弧关。我一直安慰自己盅安,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布世囊。 她就那樣靜靜地躺著别瞭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪株憾。 梳的紋絲不亂的頭發(fā)上蝙寨,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天,我揣著相機與錄音嗤瞎,去河邊找鬼墙歪。 笑死,一個胖子當(dāng)著我的面吹牛贝奇,可吹牛的內(nèi)容都是我干的虹菲。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼掉瞳,長吁一口氣:“原來是場噩夢啊……” “哼届惋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起菠赚,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤脑豹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后衡查,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘩欺,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俱饿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歌粥。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拍埠,靈堂內(nèi)的尸體忽然破棺而出失驶,到底是詐尸還是另有隱情,我是刑警寧澤枣购,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布嬉探,位于F島的核電站,受9級特大地震影響棉圈,放射性物質(zhì)發(fā)生泄漏涩堤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一分瘾、第九天 我趴在偏房一處隱蔽的房頂上張望胎围。 院中可真熱鬧,春花似錦德召、人聲如沸白魂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碧聪。三九已至,卻和暖如春液茎,著一層夾襖步出監(jiān)牢的瞬間逞姿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工捆等, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滞造,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓栋烤,卻偏偏與公主長得像谒养,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子明郭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350