4-3~8 code-splitting,懶加載吠昭,預(yù)拉取喊括,預(yù)加載

1. 簡介

代碼分離是 webpack 中最引人注目的特性之一。此特性能夠把代碼分離到不同的 bundle 中矢棚,然后可以按需加載或并行加載這些文件郑什。代碼分離可以用于獲取更小的 bundle,以及控制資源加載優(yōu)先級蒲肋,如果使用合理蘑拯,會極大影響加載時間钝满。

2. 入口分離

我們看下面這種情況:

// index.js

import _ from 'lodash';
import './another-module';

console.log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);
// another-module.js

import _ from 'lodash';
import $ from 'jquery';

console.log(
    _.join(['another', 'module', 'loaded!'], ' ')
);
$('body').click(() => {
    $('body').css('background', 'green')
});

npm run dev 打包后如下:


image.png

image.png

可以看到,雖然 index 展示的時候不需要 another-module申窘,但兩者最終被打包到同一個文件輸出弯蚜,這樣的話有兩個缺點:

  1. index 和 another-module 邏輯混合到一起,增大了需要下載的包的體積剃法。如果此時 index 是首屏必須的邏輯碎捺,那么由于包體增大,延遲了首屏展示時間贷洲。
  2. 修改 index 或者 another-module 邏輯收厨,都會導致最終輸出的文件被改變,用戶需要重新下載和當前改動無關(guān)的模塊內(nèi)容优构。
    解決這兩個問題诵叁,最好的辦法,就是將無關(guān)的 index 和 another-module 分離钦椭。如下:
    entry: {
        index: "./src/index.js",
        another: "./src/another-module.js"
    },
// index.js

// index.js

import _ from 'lodash';

console.log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);

打包后如下:


image.png

![image](https://upload-images.jianshu.io/upload_images/4761597-6bbb88ad600937dc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以看到拧额,首屏加載的資源 index 明顯變小了,可是加載時間反而延長了彪腔。這是由于 another 被并行加載侥锦,而且 index 和 another 的總體大小增大了很多。仔細分析漫仆,可以發(fā)現(xiàn) lodash 模塊被分別打包到了 index 和 another。我們按照上面的思路泪幌,繼續(xù)將三方庫 lodash 和 jquery 也分離出來:

// index.js

console.log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);
// another-module.js

console.log(
    _.join(['another', 'module', 'loaded!'], ' ')
);
$('body').click(() => {
    $('body').css('background', 'green')
});
// jquery.js

import $ from 'jquery';
window.$ = $;
// lodash.js

import _ from 'lodash';
window._ = _;
image.png

image.png

可以看到盲厌,jquery 和 lodash 被分離后,index 和 another 顯著變小祸泪,而第三方模塊基本上是很少改變的吗浩,也就是當某個業(yè)務(wù)模塊改變時,我們只需要重新上傳新的業(yè)務(wù)模塊代碼没隘,用戶更新的時候也只需要更新較小的業(yè)務(wù)模塊代碼懂扼。不過可以看到,這里仍然有兩個缺點:

  1. 手動做代碼抽取非常麻煩右蒲,我們需要自己把握分離的先后順序阀湿,以及手動指定入口。
  2. 首次進入且沒有緩存的時候瑰妄,由于并行的資源較多陷嘴,并沒有減少首屏加載的時間,反而可能延長了這個時間间坐。
    下面我們來嘗試解決這兩個問題灾挨。

3. 代碼自動抽取

SplitChunksPlugin插件可以將公共的依賴模塊提取到已有的入口 chunk 中邑退,或者提取到一個新生成的 chunk。

3.1 代碼自動抽取

讓我們使用這個插件劳澄,將之前的示例中重復(fù)的 lodash 模塊 和 jquery 模塊抽取出來地技。(ps: 這里 webpack4 已經(jīng)移除了 CommonsChunkPlugin 插件,改為 SplitChunksPlugin 插件了)秒拔。

// index.js
import _ from 'lodash';

console.log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);
// another-module.js
import _ from 'lodash';
import $ from 'jquery';

console.log(
    _.join(['another', 'module', 'loaded!'], ' ')
);
$('body').click(() => {
    $('body').css('background', 'green')
});
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    }
image.png
image.png

可以看到莫矗,兩個公共模塊各自被自動抽取到了新生成的 chunk 中。

3.2 SplitChunksPlugin 配置參數(shù)詳解

SplitChunksPlugin 默認配置如下:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      automaticNameMaxLength: 30,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

各項缺省時會自動取默認值溯警,也就是如果傳入:

module.exports = {
  //...
  optimization: {
    splitChunks: {}
  }
};

等同于全部取默認值趣苏。下面我們來看一下每一項的含義。首先修改一下源文件梯轻,抽取 log-util 模塊:

// log-util.js
export const log = (info) => {
    console.log(info);
};

export const err = (info) => {
    console.log(info);
};
// index.js
import _ from 'lodash';
import { log } from './log-util';

log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);
// another-module.js
import _ from 'lodash';
import $ from 'jquery';
import { log } from './log-util';

log(
    _.join(['another', 'module', 'loaded!'], ' ')
);
$('body').click(() => {
    $('body').css('background', 'green')
});

3.2.1 splitChunks.chunks

chunks 有三個值食磕,分別是:
async: 異步模塊(即按需加載模塊,默認值)
initial: 初始模塊(即初始存在的模塊)
all: 全部模塊(異步模塊 + 初始模塊)
因為更改初始塊會影響 HTML 文件應(yīng)該包含的用于運行項目的腳本標簽喳挑。我們可以修改該配置項如下(這里對 cacheGroups 做了簡單的修改彬伦,是為了方便后續(xù)的比較,大家簡單理解為伊诵,node_modules 的模塊单绑,會放在 verdors 下,其他的會放在 default 下即可曹宴,后面會有更詳細的解釋):

    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
image.png

3.2.2 splitChunks.minSize

生成塊的最小大小(以字節(jié)為單位)搂橙。

    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 800000,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
image.png

可以看到 lodash 并沒有從 index 中拆出,lodash 和 jquery 從another 拆出后一起被打包在一個公共的 vendors~another 中笛坦。這是由于如果 lodash 和 jquery 單獨拆出后 jquery 是不到 800k 的区转,無法拆成單獨的兩個 chunk。

    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
     
image.png

可以看到每個模塊都被分離了出來版扩。

3.2.3 splitChunks.minRemainingSize

在 webpack 5 中引入了該選項废离,通過確保分割后剩余塊的最小大小超過指定限制,從而避免了零大小的模塊礁芦。在“開發(fā)”模式下默認為0蜻韭。對于其他情況,該選項默認為 minSize 的值柿扣。所以它不需要手動指定肖方,除非在需要采取特定的深度控制的情況下。

3.2.4 splitChunks.maxSize

使用 maxSize 告訴 webpack 嘗試將大于 maxSize 字節(jié)的塊分割成更小的部分未状。每塊至少是 minSize 大小窥妇。該算法是確定性的,對模塊的更改只會產(chǎn)生局部影響娩践。因此活翩,它在使用長期緩存時是可用的烹骨,并且不需要記錄。maxSize只是一個提示材泄,當模塊大于 maxSize 時可能不會分割也可能分割后大小小于 minSize沮焕。
當塊已經(jīng)有一個名稱時,每個部分將從該名稱派生出一個新名稱拉宗。取決于值optimization.splitChunks.hidePathInfo峦树,它將從第一個模塊名或其散列派生一個
key。
需要注意:

  1. maxSize比maxInitialRequest/ maxasyncrequest具有更高的優(yōu)先級旦事。實際的優(yōu)先級是maxInitialRequest/maxAsyncRequests < maxSize < minSize魁巩。
  2. 設(shè)置maxSize的值將同時設(shè)置maxAsyncSize和maxInitialSize的值。
    maxSize選項用于HTTP/2和長期緩存姐浮。它增加了請求數(shù)谷遂,以便更好地進行緩存。它還可以用來減小文件大小卖鲤,以便更快地重建肾扰。
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            maxSize: 30000,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }

image.png

可以看到,defaultVendorsanotherindex~ 又分離出了 defaultVendorsanotherindex._node_modules_lodash_lodash.js2ef0e502.js 和 defaultVendorsanotherindex~._node_modules_webpack_buildin_g.js蛋逾。

3.2.5 splitChunks.minChunks

代碼分割前共享一個模塊的最小 chunk 數(shù)集晚,我們來看一下:

optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 10,
            minChunks: 2,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
image.png

可以看到, jquery 由于引用次數(shù)小于 2区匣,沒有被單獨分離出來偷拔。如果改為 3,

    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 10,
            minChunks: 3,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }
image.png

可以看到亏钩, jquery 和 lodash 由于引用次數(shù)小于 3莲绰,都沒有被單獨分離出來。

3.2.6 splitChunks.maxAsyncRequests

按需加載時的最大并行請求數(shù)铸屉。

3.2.7 splitChunks.maxInitialRequests

一個入口點的最大并行請求數(shù)钉蒲。

3.2.8 splitChunks.automaticNameDelimiter

默認情況下切端,webpack將使用塊的來源和名稱來生成名稱(例如: vendors~main.js)彻坛。此選項允許您指定用于生成的名稱的分隔符。踏枣。

3.2.9 splitChunks.automaticNameMaxLength

插件生成的 chunk 名稱所允許的最大字符數(shù)昌屉。防止名稱過長,增大代碼和傳輸包體茵瀑,保持默認即可间驮。

3.2.10 splitChunks.cacheGroups

緩存組可以繼承和/或覆蓋splitChunks中的任何選項。但是test马昨、priority和reuseExistingChunk只能在緩存組級配置竞帽。若要禁用任何缺省緩存組扛施,請將它們設(shè)置為false。

3.2.10.1 splitChunks.cacheGroups.{cacheGroup}.test

控制此緩存組選擇哪些模塊屹篓。省略它將選擇所有模塊疙渣。它可以匹配絕對模塊資源路徑或塊名稱。當一個 chunk 名匹配時堆巧,chunk 中的所有模塊都被選中妄荔。

optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            minChunks: 1,
            cacheGroups: {
                log: {
                    test(module, chunks) {
                        // `module.resource` contains the absolute path of the file on disk.
                        // Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
                        return module.resource &&
                            module.resource.indexOf('log') > -1;
                    }
                },
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }

image.png

可以看到,log-util 模塊被匹配到了 loganotherindex chunk谍肤。

3.2.10.2 splitChunks.cacheGroups.{cacheGroup}.priority

一個模塊可以屬于多個緩存組啦租。該優(yōu)化將優(yōu)先選擇具有較高優(yōu)先級的緩存組舵稠。默認組具有負優(yōu)先級铅协,以允許自定義組具有更高的優(yōu)先級(默認值為0的自定義組)。

optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            minChunks: 1,
            cacheGroups: {
                log: {
                    test(module, chunks) {
                        // `module.resource` contains the absolute path of the file on disk.
                        // Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
                        return module.resource &&
                            module.resource.indexOf('log') > -1;
                    },
                    priority: -20,
                },
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -15,
                    reuseExistingChunk: true
                }
            }
        }
    }
image.png

可以看到 log 緩存組下不會輸出了冶伞,事實上乳附,比 default 的 prioity 低的緩存組都是不會輸出的内地。

3.2.10.3 splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk

如果當前 chunk 包含已經(jīng)從主包中分離出來的模塊,那么它將被重用赋除,而不是生成一個新的 chunk阱缓。這可能會影響 chunk 的結(jié)果文件名。

3.3 小結(jié)

可以看到举农,提取公共代碼單獨輸出后荆针,我們加載資源的時間并沒有變短,因為帶寬是一定的颁糟,并行資源過多航背,反而會增加 http 耗時。我們獲得的主要好處是棱貌,充分利用了緩存玖媚,這對于用戶資源更新時有很大的好處,不過也需要衡量公共代碼提取的條件婚脱,防止負優(yōu)化今魔。這里一般使用默認的四個條件即可(至于作用的模塊我們可以改為 all):

  1. 新的 chunk 可以被共享,或者是來自 node_modules 文件夾
  2. 新的 chunk 大于30kb(在 min + gz 壓縮之前)
  3. 當按需加載 chunk 時障贸,并行請求的最大數(shù)量小于或等于 6
  4. 初始頁面加載時并行請求的最大數(shù)量將小于或等于 4

4. 動態(tài)引入和懶加載

我們進一步考慮错森,初始的時候并行了這么多資源,導致加載時間變慢篮洁,那么其中是否所有的資源都是需要的呢涩维。顯然不是的。這里我們其實是想先加載首屏邏輯袁波,然后點擊 body 時才去加載 another-module 的邏輯瓦阐。
首先蜗侈,webpack 資源是支持動態(tài)引入的。當涉及到動態(tài)代碼拆分時睡蟋,webpack 提供了兩個類似的技術(shù)宛篇。對于動態(tài)導入,第一種薄湿,也是優(yōu)先選擇的方式是叫倍,使用符合 ECMAScript 提案import() 語法。第二種豺瘤,則是使用 webpack 特定的 require.ensure吆倦。更推薦使用第一種,適應(yīng)范圍更大坐求。
而在用戶真正需要的時候才去動態(tài)引入資源蚕泽,也就是所謂的懶加載了。
我們作如下修改:

// index.js
import _ from 'lodash';
import { log } from './log-util';

log(
    _.join(['index', 'module', 'loaded!'], ' ')
);
const div = document.createElement('div');
div.innerText = 'first screen content';
document.getElementById('root').appendChild(div);
document.body.addEventListener('click', () => {
    import ('./another-module').then(anotherModule => {
        anotherModule.default.run();
    });
});
// another-module.js
import _ from 'lodash';
import $ from 'jquery';
import { log } from './log-util';
const anotherModule = {
    run() {
        log(
            _.join(['another', 'module', 'loaded!'], ' ')
        );
        $('body').css('background', 'green');
    }
};

export default anotherModule;
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            minChunks: 1,
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 1,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    }

打包后如下:


image.png

image.png

可以看到桥嗤,another 的輔助加載和 log须妻,lodash 邏輯被提前加載,但是模塊內(nèi)部邏輯和 jquery 模塊都被單獨拎出來了泛领,且并沒有加載荒吏。


async.gif

點擊body后,該部分內(nèi)容才被加載并執(zhí)行渊鞋。這樣就能有效提升首屏加載速度绰更。
如果我們想改變異步加載包的名稱,可以使用 magic-comment锡宋,如下:
document.body.addEventListener('click', () => {
    import (/* webpackChunkName: "anotherModule" */ './another-module').then(anotherModule => {
        anotherModule.default.run();
    });
});

打包發(fā)現(xiàn):


image.png

image.png

但是尷尬地是儡湾,由于新增了 another-module,和 another 相同的部分被打包并且提前加載了执俩,導致我們的懶加載策略失效了徐钠,這個坑大家要注意。

5. 預(yù)拉取和預(yù)加載

我們考慮一下這個問題役首,懶加載雖然減少了首屏加載時間尝丐,但是在交互操作或者其他異步渲染的響應(yīng)。我們該如何解決這個問題呢宋税?
webpack 4.6.0+增加了對預(yù)拉取和預(yù)加載的支持摊崭。
預(yù)拉取: 將來某些導航可能需要一些資源
預(yù)加載: 在當前導航可能需要一些資源
假設(shè)有一個主頁組件讼油,它呈現(xiàn)一個LoginButton組件杰赛,然后在單擊后按需加載一個LoginModal組件。

// LoginButton.js
//...
import(/* webpackPrefetch: true */ 'LoginModal');

這將導致 <link rel="prefetch" href="login-modal-chunk.js"> 被附加在頁面的頭部矮台,指示瀏覽器在空閑時間預(yù)拉取login-modal-chunk.js文件乏屯。
ps:webpack將在加載父模塊后立即添加預(yù)拉取提示根时。
Preload 不同于 prefetch:

  • 一個預(yù)加載的塊開始與父塊并行加載。預(yù)拉取的塊在父塊完成加載后啟動辰晕。
  • 預(yù)加載塊具有中等優(yōu)先級蛤迎,可以立即下載。在瀏覽器空閑時下載預(yù)拉取的塊含友。
  • 一個預(yù)加載的塊應(yīng)該被父塊立即請求替裆。預(yù)拉取的塊可以在將來的任何時候使用。
  • 瀏覽器支持是不同的窘问。
    讓我們想象一個組件 ChartComponent辆童,它需要一個巨大的圖表庫。它在渲染時顯示一個 LoadingIndicator惠赫,并立即按需導入圖表庫:
// ChartComponent.js
//...
import(/* webpackPreload: true */ 'ChartingLibrary');

當使用 ChartComponent 的頁面被請求時把鉴,還會通過請求圖表庫塊。假設(shè)頁面塊更小儿咱,完成速度更快庭砍,那么頁面將使用 LoadingIndicator 顯示,直到已經(jīng)請求的圖表庫塊完成混埠。這將對加載時間有一定優(yōu)化怠缸,因為它只需要一次往返而不是兩次。特別是在高延遲環(huán)境中钳宪。

ps: 不正確地使用 webpackPreload 實際上會損害性能凯旭,所以在使用它時要小心。
對于本文所列的例子使套,顯然更符合預(yù)拉取的情況罐呼,如下:

document.body.addEventListener('click', () => {
    import (/* webpackPrefetch: true */ './another-module').then(anotherModule => {
        anotherModule.default.run();
    });
});
image.png

圖示資源,提前被下載好侦高,在點擊的時候再去下載資源時就可以直接使用緩存嫉柴。

document.body.addEventListener('click', () => {
    import (/* webpackLoad: true */ './another-module').then(anotherModule => {
        anotherModule.default.run();
    });
});

6. 小結(jié)

本文內(nèi)容比較多,統(tǒng)合了多個章節(jié)奉呛,而且內(nèi)容上有很大的不一致计螺。如果大家有同步看視屏,應(yīng)該也會發(fā)現(xiàn)之前也有很多不一致的地方瞧壮。學習記錄切忌照本宣科登馒,多查資料,多實踐咆槽,才能有更多收獲陈轿。

參考

https://webpack.js.org/guides/code-splitting/#root
https://www.webpackjs.com/guides/code-splitting/
Webpack 的 Bundle Split 和 Code Split 區(qū)別和應(yīng)用
https://webpack.js.org/plugins/split-chunks-plugin/
手摸手,帶你用合理的姿勢使用webpack4
webpack4 splitChunks的reuseExistingChunk選項有什么作用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市麦射,隨后出現(xiàn)的幾起案子蛾娶,更是在濱河造成了極大的恐慌,老刑警劉巖潜秋,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛔琅,死亡現(xiàn)場離奇詭異,居然都是意外死亡峻呛,警方通過查閱死者的電腦和手機罗售,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钩述,“玉大人莽囤,你說我怎么就攤上這事∏芯啵” “怎么了朽缎?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谜悟。 經(jīng)常有香客問我话肖,道長,這世上最難降的妖魔是什么葡幸? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任最筒,我火速辦了婚禮,結(jié)果婚禮上蔚叨,老公的妹妹穿的比我還像新娘床蜘。我一直安慰自己,他們只是感情好蔑水,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布邢锯。 她就那樣靜靜地躺著,像睡著了一般搀别。 火紅的嫁衣襯著肌膚如雪丹擎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天歇父,我揣著相機與錄音蒂培,去河邊找鬼。 笑死榜苫,一個胖子當著我的面吹牛护戳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垂睬,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼媳荒,長吁一口氣:“原來是場噩夢啊……” “哼抗悍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肺樟,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逻淌,沒想到半個月后么伯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡卡儒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年田柔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骨望。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡硬爆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出擎鸠,到底是詐尸還是另有隱情缀磕,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布劣光,位于F島的核電站袜蚕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绢涡。R本人自食惡果不足惜牲剃,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雄可。 院中可真熱鬧凿傅,春花似錦、人聲如沸数苫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虐急。三九已至过椎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戏仓,已是汗流浹背疚宇。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赏殃,地道東北人敷待。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像仁热,于是被迫代替她去往敵國和親榜揖。 傳聞我的和親對象是個殘疾皇子勾哩,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359