淺析 webpack 打包流程(原理) 一 - 準(zhǔn)備工作

webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)晨缴。

自從前端模塊化出現(xiàn)译秦,我們可以把代碼拆成一個(gè)個(gè) js 文件,通過(guò) import击碗、require() 去關(guān)聯(lián)依賴文件筑悴,最后再通過(guò)打包工具把這些模塊化的 js 依賴關(guān)系打包成一個(gè)或多個(gè) js 文件在 html 頁(yè)面去引入。webpack 作為一個(gè)模塊化解決方案稍途,把項(xiàng)目中使用的每個(gè)文件都視為 模塊(Modules)雷猪。除了 js,樣式文件中 @import 的 css晰房、stylesheet url(...)、HTML <img src="...">中引入的圖片在編譯過(guò)程中都會(huì)被當(dāng)作模塊依賴來(lái)處理射沟。因?yàn)?ES2015+ 殊者、TypeScript 和一些前端框架(如 Vue、 React)的存在验夯,webpack 又擔(dān)負(fù)著將這些瀏覽器不支持的文件轉(zhuǎn)化成可識(shí)別文件的工作猖吴。
除此之外,webpack 還能進(jìn)行 tree-shaking (剔除無(wú)效代碼) 和代碼壓縮挥转,以及抽離出異步加載模塊海蔽、第三方庫(kù)來(lái)實(shí)現(xiàn)最終打包好的主文件只是進(jìn)入首頁(yè)所需要的資源。
webpack 還提供了一系列開(kāi)發(fā)輔助工具绑谣,devserver党窜,HMR 等,幫助我們高效地開(kāi)發(fā)借宵。

webpack 插件架構(gòu)

插件是 webpack 的 支柱 功能幌衣,利用一些插件可以幫助我們提取公共依賴(拆包)、壓縮資源(代碼和圖片)的體積壤玫,大大優(yōu)化我們的構(gòu)建輸出豁护。
webpack 從配置初始化到構(gòu)建完成定義了一個(gè)生命周期。整個(gè)流程是一個(gè)事件驅(qū)動(dòng)架構(gòu)欲间,利用插件系統(tǒng) Tapable楚里,通過(guò)發(fā)布-訂閱事件來(lái)實(shí)現(xiàn)所有擴(kuò)展功能。webpack 在運(yùn)行過(guò)程中會(huì)在特定節(jié)點(diǎn)調(diào)用(廣播)一些 hook猎贴,訂閱了這些 hook 的插件在監(jiān)聽(tīng)到后會(huì)執(zhí)行綁定時(shí)定義好的邏輯班缎。

webpack 核心模塊

webpack 通過(guò) Compiler (主要引擎) 控制構(gòu)建流程蝴光,用 Compilation 對(duì)象存儲(chǔ)過(guò)程中的解析編譯信息。要厘清 webpack 打包原理吝梅,理解它們至關(guān)重要虱疏。關(guān)于這部分我仔細(xì)閱讀源碼寫(xiě)了這篇:webpack 之 Compiler 、Compilation 和 Tapable苏携。還有負(fù)責(zé)生成模塊的 ModuleFactory 生成模塊做瞪,解析源碼 的 Parser ,渲染代碼 的 Template右冻。

webpack 構(gòu)建流程

當(dāng) webpack 處理應(yīng)用程序時(shí)装蓬,它會(huì)從 入口 開(kāi)始,遞歸地構(gòu)建一個(gè)依賴關(guān)系圖 (dependency graph)纱扭,其中包含應(yīng)用程序所需的每個(gè)模塊 ( loader 負(fù)責(zé)將非JavaScript文件轉(zhuǎn)換為依賴圖能直接引用的有效模塊)牍帚,最后將所有這些模塊打包成一個(gè)或多個(gè) bundle。
先放上總的構(gòu)建原理圖乳蛾,后面會(huì)詳細(xì)去闡述暗赶。

webpack構(gòu)建原理

再借一張別人畫(huà)的簡(jiǎn)易版流程圖:

webpack 構(gòu)建流程簡(jiǎn)易版

幾個(gè)關(guān)鍵階段和結(jié)合資源形態(tài)流轉(zhuǎn)的角度對(duì)過(guò)程的說(shuō)明:


make后,compilation 會(huì)獲知資源模塊的內(nèi)容與依賴關(guān)系肃叶,也就知道“輸入”是什么蹂随;而經(jīng)過(guò)seal階段處理后, compilation 則獲知資源輸出的圖譜因惭,也就是知道怎么“輸出”:哪些模塊跟那些模塊“綁定”在一起輸出到哪里岳锁。

compiler.hooks.make 階段:
entry 文件以 dependence 對(duì)象形式加入 compilation 的依賴列表,dependence 對(duì)象記錄有 entry 的類型蹦魔、路徑等信息激率;
根據(jù) dependence 調(diào)用對(duì)應(yīng)的工廠函數(shù)創(chuàng)建 module 對(duì)象,之后讀入 module 對(duì)應(yīng)的文件內(nèi)容勿决,調(diào)用 loader-runner 對(duì)內(nèi)容做轉(zhuǎn)化乒躺,轉(zhuǎn)化結(jié)果若有其它依賴則繼續(xù)讀入依賴資源,重復(fù)此過(guò)程直到所有依賴均被轉(zhuǎn)化為 module低缩。
compilation.seal 階段:
遍歷 module 集合聪蘸,根據(jù) entry 配置及引入資源的方式,將 module 分配到不同的 chunk表制;
遍歷 chunk 集合健爬,調(diào)用 compilation.emitAsset 方法標(biāo)記 chunk 的輸出規(guī)則,即轉(zhuǎn)化為 assets 集合么介。
compiler.emitAssets 階段:
將 assets 寫(xiě)入文件系統(tǒng)娜遵。

再放一張簡(jiǎn)單版主體框架流程

webpack 的構(gòu)建從入口文件開(kāi)始,會(huì)找出有哪些模塊是入口起點(diǎn)依賴的壤短。需要 loader 處理的就先轉(zhuǎn)換編譯设拟,之后分析模塊自身是否有依賴慨仿,有依賴就接著處理依賴,流程和剛剛一致纳胧。像這樣遞歸獲取并處理每個(gè)模塊镰吆,同步為dependencies,異步為block跑慕,最終存儲(chǔ)到一個(gè) Map 表blockInfoMap中 (ModuleGraph)万皿。然后遍歷這些編譯完成的模塊,基于它們進(jìn)行分組 (chunkGroup) 和封包 (chunk) 核行,生成ChunkGraph并優(yōu)化牢硅。
跟著會(huì)根據(jù)插件配置對(duì) chunk 進(jìn)一步優(yōu)化處理,比如代碼分割芝雪、 treeshaking 或者 代碼壓縮减余,最后生成我們需要的 js。

webpack 的運(yùn)行流程是一個(gè)串行的過(guò)程:
webpack 就像一條生產(chǎn)線惩系,要經(jīng)過(guò)一系列處理流程后才能將源文件轉(zhuǎn)換成輸出結(jié)果位岔。 這條生產(chǎn)線上的每個(gè)處理環(huán)節(jié)的職責(zé)都是單一的,多個(gè)流程之間存在依賴關(guān)系堡牡,只有完成當(dāng)前處理后才能交給下一個(gè)流程去處理抒抬。而插件就像是一個(gè)插入到生產(chǎn)線中的一個(gè)功能,在特定的時(shí)機(jī)對(duì)生產(chǎn)線上的資源做處理悴侵。webpack 通過(guò) Compiler 來(lái)組織這條復(fù)雜的生產(chǎn)線。webpack 在運(yùn)行過(guò)程中會(huì)廣播事件拭嫁,插件只需要監(jiān)聽(tīng)它所關(guān)心的事件可免,就能加入到這條生產(chǎn)線中,去改變生產(chǎn)線的運(yùn)作做粤。webpack 的事件流機(jī)制保證了插件的有序性浇借,使得整個(gè)系統(tǒng)擴(kuò)展性很好。

案例 demo

本系列的項(xiàng)目 demo怕品,后面會(huì)以此為例分析過(guò)程和結(jié)果:【淺析 webpack 打包流程(原理) - 案例 demo】

一妇垢、初始化工作

webpack-cli 傳的參數(shù)和項(xiàng)目配置做一個(gè)合并( cli 參數(shù)優(yōu)先級(jí)更高),并處理部分參數(shù) (驗(yàn)證:validateOptions(options) 處理:processOptions(options)) 肉康,得到最終的配置 options闯估,接著對(duì)配置中的統(tǒng)計(jì)信息(options.stats)進(jìn)行處理。
創(chuàng)建 Compiler 實(shí)例compiler = new Compiler(options.context)(options.context 為項(xiàng)目絕對(duì)路徑)吼和,把最終配置 options 掛載到 compiler 對(duì)象下涨薪。

二、編譯前準(zhǔn)備

此階段概述:在 compiler 的各種 hook 上注冊(cè)項(xiàng)目配置的 plugins炫乓、注冊(cè) webpack 默認(rèn)插件 ?? 注冊(cè)resolverFactory.hooks為 Factory.createResolver 方法提供參數(shù)對(duì)象刚夺。
webpack 的事件機(jī)制是基于 tapable 庫(kù)做的事件流控制献丑,在整個(gè)編譯過(guò)程中暴露出各種hook,而 plugin 注冊(cè)監(jiān)聽(tīng)了某個(gè)/某些 hook侠姑,在這個(gè) hook 觸發(fā)時(shí)创橄,會(huì)執(zhí)行 plugin 里綁定的方法。

// /lib/Webpack.js
new NodeEnvironmentPlugin({
  infrastructureLogging: options.infrastructureLogging
}).apply(compiler);

NodeEnvironmentPlugin 類主要對(duì)文件系統(tǒng)做一些封裝莽红,包括輸入妥畏,輸出,緩存船老,監(jiān)聽(tīng)等等咖熟,這些擴(kuò)展后的方法全部掛載在 compiler 對(duì)象下。

plugin.apply(compiler); 通過(guò)調(diào)用每個(gè)插件實(shí)例的 apply 方法柳畔,并把 complier 實(shí)例作為參數(shù)傳進(jìn)去馍管,在 compiler 生命周期的各種鉤子事件上注冊(cè)配置中的所有 plugins。即插件 apply 方法中訂閱了 compiler 的一些 hook薪韩,后續(xù) compiler 會(huì)根據(jù)運(yùn)行時(shí)各種事件鉤子的觸發(fā)确沸,去執(zhí)行插件注冊(cè)/綁定的函數(shù)。
關(guān)于 Compiler 和 插件機(jī)制我這篇有比較詳細(xì)的說(shuō)明 ?? webpack 之 Compiler 俘陷、Compilation 和 Tapable

// /lib/Webpack.js
compiler.options = new WebpackOptionsApply().process(options, compiler);

WebpackOptionsApply 類的 process 方法把配置里的一些屬性添加到 compiler 上罗捎,更主要的是注冊(cè)激活一些默認(rèn)自帶的插件和 resolverFactory.hooks。大部分插件的作用是往 compiler 的兩個(gè) hook: compilation, thisCompilation 里注冊(cè)一些事件(此時(shí)這兩個(gè)鉤子已經(jīng)獲取到 normalModuleFactory 等參數(shù))拉盾,舉例:

// /lib/WebpackOptionsApply.js
new JavascriptModulesPlugin().apply(compiler); // 給 normalModuleFactory 的 js 模塊提供 Parser桨菜、JavascriptGenerator 對(duì)象 ,并給 seal 階段的 template 提供 renderManifest 數(shù)組(包含 render 方法)
new EntryOptionPlugin().apply(compiler); // 將插件注冊(cè)在compiler.hooks.entryOption 上
compiler.hooks.entryOption.call(options.context, options.entry); // 激活 entryOption 鉤子事件捉偏,EntryOptionPlugin 實(shí)例里綁定的方法隨即被觸發(fā)

EntryOptionPlugin 插件會(huì)根據(jù)入口配置是單入口或多入口實(shí)例化SingleEntryPlugin / MultiEntryPlugin 插件倒得,兩者均會(huì)在 apply 方法里注冊(cè) compiler.hooks: compilation, make

插件處理完畢夭禽,觸發(fā)compiler.hooks.afterPlugins鉤子霞掺。

// /lib/WebpackOptionsApply.js
compiler.resolverFactory.hooks.resolveOptions
  .for("context")
  .tap("WebpackOptionsApply", resolveOptions => {
    return Object.assign(
      {
        fileSystem: compiler.inputFileSystem,
        esolveToContext: true
      },
      cachedCleverMerge(options.resolve, resolveOptions)
    );
  });

然后依次注冊(cè) compiler.resolverFactory.hooks: resolveOptions.for (normal/context/loader),目的是為 Factory.createResolver 提供默認(rèn)參數(shù)對(duì)象 (包含相關(guān)的項(xiàng)目 resolve 配置項(xiàng))讹躯。觸發(fā) compiler.hooks.afterResolvers 鉤子菩彬,至此 compiler 初始化完畢。

三潮梯、開(kāi)始編譯

此階段概述:compiler.run ?? compiler.compile 開(kāi)啟編譯 ?? 實(shí)例化 NormalModuleFactory 類ContextModuleFactory 類 ?? 創(chuàng)建Compilation實(shí)例 ?? 觸發(fā)compiler.hooks.make鉤子執(zhí)行 compilation.addEntry (處理入口)骗灶,執(zhí)行 moduleFactory.create 開(kāi)始構(gòu)建 module
compile 是真正進(jìn)行編譯的過(guò)程秉馏,最終會(huì)把所有原始資源編譯為目標(biāo)資源矿卑。

繼續(xù)回到/lib/Webpack.js,判斷 options 里是否有 watch沃饶,有走 compiler.watch母廷,無(wú)則 compiler.run轻黑,我們執(zhí)行 compiler 的 run 方法,正式啟動(dòng)編譯琴昆。

首先調(diào)用compiler.hooks: beforeRun鉤子氓鄙,做一些判斷 inputFileSystem 是否配置、讀取之前的 records 等處理业舍,再在回調(diào)里執(zhí)行 Compiler 類的compile原型方法抖拦。

// /lib/Compiler.js
compile(callback) {
  const params = {
    normalModuleFactory: this.createNormalModuleFactory(),
    contextModuleFactory: this.createContextModuleFactory(),
    compilationDependencies: new Set()
  };
}

先分別實(shí)例化 NormalModuleFactory 類和 ContextModuleFactory 類 (均擴(kuò)展于 tapable),和觸發(fā) compiler.hooks: normalModuleFactory 舷暮,contextModuleFactory鉤子态罪。

// /lib/NormalModuleFactory.js
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
  let resolver = this.hooks.resolver.call(null);
  resolver(result, (err, data) => {
    // ...
  });
});
this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
  // ...
});

NormalModuleFactory 負(fù)責(zé)生成各類模塊:從入口點(diǎn)開(kāi)始,分解每個(gè)請(qǐng)求下面,解析文件內(nèi)容以查找進(jìn)一步的請(qǐng)求复颈,然后通過(guò)分解所有請(qǐng)求以及解析新的文件來(lái)爬取全部文件。在最后階段沥割,每個(gè)依賴項(xiàng)都會(huì)成為一個(gè)模塊實(shí)例耗啦。
在實(shí)例化 NormalModuleFactory 執(zhí)行 constructor 的過(guò)程中,注冊(cè)了 normalModuleFactory.hooks: factory机杜,觸發(fā) factory 鉤子時(shí)會(huì)先觸發(fā) normalModuleFactory.hooks: resolver帜讲,再執(zhí)行注冊(cè)的回調(diào)函數(shù)。

ContextModuleFactory 從 webpack 獨(dú)特的 require.context API 生成依賴關(guān)系椒拗。它會(huì)解析請(qǐng)求的目錄似将,為每個(gè)文件生成請(qǐng)求,并依據(jù)傳遞來(lái)的 regExp 進(jìn)行過(guò)濾蚀苛。最后匹配成功的依賴關(guān)系將被傳入 NormalModuleFactory在验。

之后觸發(fā)compiler.hooks: beforeCompile、compile枉阵,然后執(zhí)行:const compilation = this.newCompilation(params)來(lái)實(shí)例化一個(gè) Compilation 類译红。
newCompilation 方法里還觸發(fā)了 compiler.hooks: thisCompilation预茄、compilation兴溜,在編譯前注冊(cè)plugins階段WebpackOptionsApply.js里注冊(cè)了大量這倆 hooks 的事件,此時(shí)拿到 compilation 對(duì)象耻陕,開(kāi)始執(zhí)行這一系列事件拙徽。

  • compiler.hooks.thisCompilation 會(huì)在 compilation 對(duì)象的 hooks 上注冊(cè)一些新事件;
  • compiler.hooks.compilation 會(huì)在 compilation诗宣、normalModuleFactory 對(duì)象的 hooks 上注冊(cè)一些新事件膘怕,同時(shí)還會(huì)往 compilation.dependencyFactories (工廠類)、compilation.dependencyTemplates (模板類) 增加依賴模塊召庞。

為什么這里需要 thisCompilation岛心、compilation 兩個(gè)鉤子来破?
Compiler 的 createChildCompiler 方法可以創(chuàng)建子編譯器,過(guò)程中會(huì)復(fù)制 compilation 鉤子(上注入的插件方法)忘古,但不會(huì)復(fù)制thisCompilation徘禁、makecompile等髓堪。子編譯器擁有完整的 module 和 chunk 生成送朱,通過(guò)它可以獨(dú)立于父編譯器執(zhí)行一個(gè)核心構(gòu)建流程,額外生成一些需要的 module 和 chunk干旁。

觸發(fā)compiler.hooks : make驶沼,執(zhí)行之前在SingleEntryPlugin | MultiEntryPlugin注冊(cè)的訂閱事件,執(zhí)行:

// /lib/SingleEntryPlugin.js 或 /lib/MultiEntryPlugin.js
compiler.hooks.make.tapAsync(
  "SingleEntryPlugin",
  (compilation, callback) => {
    const { entry, name, context } = this;
    const dep = SingleEntryPlugin.createDependency(entry, name);
    compilation.addEntry(context, dep, name, callback);
  }
);

再看 compilation 的 addEntry 方法:

// /lib/Compilation.js
_addModuleChain(context, dependency, onModule, callback) {
  // ...
  const Dep = /** @type {DepConstructor} */ (dependency.constructor);
  const moduleFactory = this.dependencyFactories.get(Dep); // moduleFactory 為 normalModuleFactory
  this.semaphore.acquire(() => { // 編譯隊(duì)列控制
    // 默認(rèn)并發(fā)數(shù)為 100争群,超過(guò)后存入 semaphore.waiters回怜,
    // 根據(jù)情況再調(diào)用 semaphore.release 去執(zhí)行存入的事件 semaphore.waiters。
    moduleFactory.create({...}, (err, module) => {
      //...
    });
  });
} 

addEntry(context, entry, name, callback) {
  this.hooks.addEntry.call(entry, name); // 觸發(fā) addEntry 鉤子
  // ...
  this._addModuleChain( // 調(diào)用上面的_addModuleChain
    context,
    entry,
    module => this.entries.push(module), // 把 module 添加 compilation.entries
    (err, module) => {} // _addModuleChain 執(zhí)行完的回調(diào)
  ) 
}

進(jìn)一步分析祭阀,dependency = SingleEntryPlugin.createDependency(entry, name)鹉戚,即new SingleEntryDependency(entry),則 Dep 為 SingleEntryDependency 類专控,而之前compiler.hooks: compilation的注冊(cè)事件中添加了依賴:

// /lib/SingleEntryPlugin.js 或 /lib/MultiEntryPlugin.js
compilation.dependencyFactories.set(
  SingleEntryDependency,
  normalModuleFactory
);

所以 moduleFactory 即為 normalModuleFactory抹凳。

this.semaphore是一個(gè)編譯隊(duì)列控制,對(duì)執(zhí)行進(jìn)行了并發(fā)控制伦腐。moduleFactory.create開(kāi)始構(gòu)建 module赢底, 遞歸解析依賴的重復(fù)從此處開(kāi)始

下文:淺析 webpack 打包流程(原理) 二 - 遞歸構(gòu)建 module

webpack 打包流程系列(未完):
淺析 webpack 打包流程(原理) - 案例 demo
淺析 webpack 打包流程(原理) 一 - 準(zhǔn)備工作
淺析 webpack 打包流程(原理) 二 - 遞歸構(gòu)建 module
淺析 webpack 打包流程(原理) 三 - 生成 chunk
淺析 webpack 打包流程(原理) 四 - chunk 優(yōu)化
淺析 webpack 打包流程(原理) 五 - 構(gòu)建資源
淺析 webpack 打包流程(原理) 六 - 生成文件

參考鳴謝:
webpack打包原理 ? 看完這篇你就懂了 !
webpack 透視——提高工程化(原理篇)
webpack 透視——提高工程化(實(shí)踐篇)
webpack 4 源碼主流程分析
[萬(wàn)字總結(jié)] 一文吃透 Webpack 核心原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柏蘑,一起剝皮案震驚了整個(gè)濱河市幸冻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咳焚,老刑警劉巖洽损,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異革半,居然都是意外死亡碑定,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門又官,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)延刘,“玉大人,你說(shuō)我怎么就攤上這事六敬〉饫担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)普泡。 經(jīng)常有香客問(wèn)我播掷,道長(zhǎng),這世上最難降的妖魔是什么撼班? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任叮趴,我火速辦了婚禮,結(jié)果婚禮上权烧,老公的妹妹穿的比我還像新娘眯亦。我一直安慰自己,他們只是感情好般码,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布妻率。 她就那樣靜靜地躺著,像睡著了一般板祝。 火紅的嫁衣襯著肌膚如雪宫静。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天券时,我揣著相機(jī)與錄音孤里,去河邊找鬼。 笑死橘洞,一個(gè)胖子當(dāng)著我的面吹牛捌袜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炸枣,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼虏等,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了适肠?” 一聲冷哼從身側(cè)響起霍衫,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侯养,沒(méi)想到半個(gè)月后敦跌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逛揩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年柠傍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片息尺。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡携兵,死狀恐怖疾掰,靈堂內(nèi)的尸體忽然破棺而出搂誉,到底是詐尸還是另有隱情,我是刑警寧澤静檬,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布炭懊,位于F島的核電站并级,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侮腹。R本人自食惡果不足惜嘲碧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望父阻。 院中可真熱鬧愈涩,春花似錦、人聲如沸履婉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苛茂。三九已至胯究,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涨颜,已是汗流浹背庭瑰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工弹灭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穷吮,地道東北人八回。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像士败,于是被迫代替她去往敵國(guó)和親褥伴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子重慢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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