前端性能優(yōu)化 -- 從 10 多秒到 1.05 秒

Optimize.png

關于 性能優(yōu)化 是個大的面是趴,這篇文章主要涉及到 前端 的幾個點,如 前端性能優(yōu)化 的流程术陶、常見技術手段、工具等腥椒。

提及 前端性能優(yōu)化 ,大家應該都會想到 雅虎軍規(guī)候衍,本文會結(jié)合 雅虎軍規(guī) 融入自己的了解知識笼蛛,進行的總結(jié)和梳理 ??

詳情,可以查閱我的 博客 lishaoy.net

首先蛉鹿,我們先來看看 ?? 雅虎軍規(guī)35 條滨砍。

  1. 盡量減少 HTTP 請求個數(shù)——須權衡
  2. 使用 CDN(內(nèi)容分發(fā)網(wǎng)絡)
  3. 為文件頭指定 Expires 或 Cache-Control ,使內(nèi)容具有緩存性妖异。
  4. 避免空的 src 和 href
  5. 使用 gzip 壓縮內(nèi)容
  6. 把 CSS 放到頂部
  7. 把 JS 放到底部
  8. 避免使用 CSS 表達式
  9. 將 CSS 和 JS 放到外部文件中
  10. 減少 DNS 查找次數(shù)
  11. 精簡 CSS 和 JS
  12. 避免跳轉(zhuǎn)
  13. 剔除重復的 JS 和 CSS
  14. 配置 ETags
  15. 使 AJAX 可緩存
  16. 盡早刷新輸出緩沖
  17. 使用 GET 來完成 AJAX 請求
  18. 延遲加載
  19. 預加載
  20. 減少 DOM 元素個數(shù)
  21. 根據(jù)域名劃分頁面內(nèi)容
  22. 盡量減少 iframe 的個數(shù)
  23. 避免 404
  24. 減少 Cookie 的大小
  25. 使用無 cookie 的域
  26. 減少 DOM 訪問
  27. 開發(fā)智能事件處理程序
  28. 用 <link> 代替 @import
  29. 避免使用濾鏡
  30. 優(yōu)化圖像
  31. 優(yōu)化 CSS Spirite
  32. 不要在 HTML 中縮放圖像——須權衡
  33. favicon.ico要小而且可緩存
  34. 保持單個內(nèi)容小于25K
  35. 打包組件成復合文本

如對 雅虎軍規(guī) 的具體細則內(nèi)容不是很了解惋戏,可自行去各搜索 ?? 引擎 ,搜索 雅虎軍規(guī) 了解詳情他膳。

壓縮 合并

對于 前端性能優(yōu)化 自然要關注 首屏 打開速度响逢,而這個速度,很大因素是花費在網(wǎng)絡請求上棕孙,那么怎么減少網(wǎng)絡請求的時間呢舔亭?

  • 減少網(wǎng)絡請求次數(shù)
  • 減小文件體積
  • 使用 CDN 加速

所以 壓縮些膨、合并 就是一個解決方案,當然可以用 gulp 钦铺、 webpack 傀蓉、 grunt 等構建工具 壓縮、合并

JS职抡、CSS 壓縮 合并

例如:gulp js、css 壓縮误甚、合并代碼如下 ??

//壓縮缚甩、合并js
gulp.task('scripts', function () {
    return gulp.src([
        './public/lib/fastclick/lib/fastclick.min.js',
        './public/lib/jquery_lazyload/jquery.lazyload.js',
        './public/lib/velocity/velocity.min.js',
        './public/lib/velocity/velocity.ui.min.js',
        './public/lib/fancybox/source/jquery.fancybox.pack.js',
        './public/js/src/utils.js',
        './public/js/src/motion.js',
        './public/js/src/scrollspy.js',
        './public/js/src/post-details.js',
        './public/js/src/bootstrap.js',
        './public/js/src/push.js',
        './public/live2dw/js/perTips.js',
        './public/live2dw/lib/L2Dwidget.min.js',
        './public/js/src/love.js',
        './public/js/src/busuanzi.pure.mini.js',
        './public/js/src/activate-power-mode.js'
    ]).pipe(concat('all.js')).pipe(minify()).pipe(gulp.dest('./public/dist/'));
});

// 壓縮、合并 CSS 
gulp.task('css', function () {
    return gulp.src([
        './public/lib/font-awesome/css/font-awesome.min.css',
        './public/lib/fancybox/source/jquery.fancybox.css',
        './public/css/main.css',
        './public/css/lib.css',
        './public/live2dw/css/perTips.css'
    ]).pipe(concat('all.css')).pipe(minify()).pipe(gulp.dest('./public/dist/'));
});

然后窑邦,再把 壓縮擅威、合并JS、CSS 放入 CDN , ?? 看看效果如何

如圖:* 壓縮冈钦、合并 且放入 CND 之后的效果 *

首頁請求速度(js)
首頁請求速度(css)

以上是 lishaoy.net 清除緩存后的 首頁 請求速度郊丛。

可見,請求時間是 4.59 s 瞧筛,總請求個數(shù) 51 厉熟, 而 js 的請求個數(shù)是 8css 的請求個數(shù)是 3 (其實就 all.css 一個较幌,其它 2 個是 Google瀏覽器加載的)揍瑟, 而沒使用 壓縮、合并 時候乍炉,請求時間是 10 多秒绢片,總請求個數(shù)有 70 多個,js 的請求個數(shù)是 20 多個 岛琼,對比請求時間 性能 提升 1倍

如圖:有緩存下的首頁效果

首頁請求速度(緩存)

基本都是秒開 ??

Tips:在 壓縮底循、合并 后,單個文件控制在 25 ~ 30 KB左右槐瑞,同一個域下熙涤,最好不要多于5個資源

圖片壓縮、合并

例如:gulp 圖片壓縮代碼如下 ??

//壓縮image
gulp.task('imagemin', function () {
    gulp.src('./public/**/*.{png,jpg,gif,ico,jpeg}')
        .pipe(imagemin())
        .pipe(gulp.dest('./public'));
});

圖片的合并可以采用 CSS Spirite困檩,方法就是把一些小圖用 PS 合成一張圖灭袁,用 css 定位顯示每張圖片的位置

.top_right .phone {
    background: url(../images/top_right.png) no-repeat 7px -17px;
    padding: 0 38px;
}

.top_right .help {
    background: url(../images/top_right.png) no-repeat 0 -47px;
    padding: 0 38px;
}

然后,把 壓縮 的圖片放入 CDN , ?? 看看窗看,效果如何

首頁請求速度(images)

可見茸歧,請求時間是 1.70 s ,總請求個數(shù) 50 , 而 img 的請求個數(shù)是 15 (這里因為首頁都是大圖显沈,就沒有合并软瞎,只是壓縮了) 逢唤,但是,效果很好 ?? 涤浇,從 4.59 s 縮短到 1.70 s, 性能又提升一倍鳖藕。

再看看有緩存情況如何 ??

首頁請求速度(images 緩存)

請求時間是 1.05 s ,有緩存和無緩存基本差不多

Tips:大的圖片在不同終端只锭,應該使用不同分辨率著恩,而不應該使用縮放(百分比)

整個 壓縮、合并 (js蜻展、css喉誊、img) 再放入 CDN ,請求時間從 10 多秒 纵顾,到最后的 1.70 s 伍茄,性能提升 5 倍多,可見施逾,這個操作必要性敷矫。

緩存

緩存會根據(jù)請求保存輸出內(nèi)容的副本,例如 頁面汉额、圖片曹仗、文件,當下一個請求來到的時候:如果是相同的URL蠕搜,緩存直接使 用本地的副本響應訪問請求整葡,而不是向源服務器再次發(fā)送請求。因此讥脐,可以從以下 2 個方面提升性能遭居。

  • 減少相應延遲,提升響應時間
  • 減少網(wǎng)絡帶寬消耗旬渠,節(jié)省流量

我們用兩幅圖來了解下瀏覽器的 緩存機制

瀏覽器第一次請求

第一次請求

瀏覽器再次請求

再次請求

從以上兩幅圖中俱萍,可以清楚的了解瀏覽器 緩存 的過程。
首次訪問一個 URL 告丢,沒有 緩存 枪蘑,但是,服務器會響應一些 header 信息岖免,如:expires岳颇、cache-control、last-modified颅湘、etag 等话侧,來記錄下次請求是否緩存、如何緩存闯参。
再次訪問這個 URL 時候瞻鹏,瀏覽器會根據(jù)首次訪問返回的 header 信息悲立,來決策是否緩存、如何緩存新博。
我們重點來分析下第二幅圖薪夕,其實是分兩條線路,如下 ??

  • 第一條線路: 當瀏覽器再次訪問某個 URL 時赫悄,會先獲取資源的 header 信息原献,判斷是否命中強緩存 (cache-control和expires) ,如命中埂淮,直接從緩存獲取資源姑隅,包括響應的 header 信息 (請求不會和服務器通信) ,也就是 強緩存 同诫,如圖
強緩存
  • 第二條線路: 如沒有命中 強緩存 ,瀏覽器會發(fā)送請求到服務器樟澜,請求會攜帶第一次請求返回的有關緩存的 header 信息 (Last-Modified/If-Modified-Since和Etag/If-None-Match) 误窖,由服務器根據(jù)請求中的相關 header 信息來比對結(jié)果是否協(xié)商緩存命中;若命中秩贰,則服務器返回新的響應 header 信息更新緩存中的對應 header 信息霹俺,但是并不返回資源內(nèi)容,它會告知瀏覽器可以直接從緩存獲榷痉选丙唧;否則返回最新的資源內(nèi)容,也就是 協(xié)商緩存觅玻。

現(xiàn)在想际,我們了解到瀏覽器緩存機制分為 強緩存、協(xié)商緩存溪厘,再來看看他們的區(qū)別 ??

緩存策略 獲取資源形式 狀態(tài)碼 發(fā)送請求到服務器
強緩存 從緩存取 200(from memory cache) 否胡本,直接從緩存取
協(xié)商緩存 從緩存取 304(not modified) 是,通過服務器來告知緩存是否可用

強緩存

與強緩存相關的 header 字段有兩個:

expires

expires: 這是 http1.0 時的規(guī)范畸悬,它的值為一個絕對時間的 GMT 格式的時間字符串侧甫,如 Mon, 10 Jun 2015 21:31:12 GMT ,如果發(fā)送請求的時間在 expires 之前蹋宦,那么本地緩存始終有效披粟,否則就會發(fā)送請求到服務器來獲取資源

cache-control

cache-control: max-age=number ,這是 http1.1 時出現(xiàn)的 header 信息冷冗,主要是利用該字段的 max-age 值來進行判斷守屉,它是一個相對值;資源第一次的請求時間和 Cache-Control 設定的有效期蒿辙,計算出一個資源過期時間胸梆,再拿這個過期時間跟當前的請求時間比較敦捧,如果請求時間在過期時間之前,就能命中緩存碰镜,否則未命中兢卵, cache-control 除了該字段外,還有下面幾個比較常用的設置值:

  • no-cache: 不使用本地緩存绪颖。需要使用緩存協(xié)商秽荤,先與服務器確認返回的響應是否被更改,如果之前的響應中存在 ETag 柠横,那么請求的時候會與服務端驗證窃款,如果資源未被更改,則可以避免重新下載牍氛。
  • no-store: 直接禁止游覽器緩存數(shù)據(jù)晨继,每次用戶請求該資源,都會向服務器發(fā)送一個請求搬俊,每次都會下載完整的資源紊扬。
  • public: 可以被所有的用戶緩存,包括終端用戶和 CDN 等中間代理服務器唉擂。
  • private: 只能被終端用戶的瀏覽器緩存餐屎,不允許 CDN 等中繼緩存服務器對其緩存。

Tips:如果 cache-control 與 expires 同時存在的話玩祟,cache-control 的優(yōu)先級高于 expires

協(xié)商緩存

協(xié)商緩存都是由瀏覽器和服務器協(xié)商腹缩,來確定是否緩存,協(xié)商主要通過下面兩組 header 字段空扎,這兩組字段都是成對出現(xiàn)的藏鹊,即第一次請求的響應頭帶上某個字段 Last-Modified 或者 Etag ,則后續(xù)請求會帶上對應的請求字段 If-Modified-Since 或者 If-None-Match 转锈,若響應頭沒有 Last-Modified 或者 Etag 字段伙判,則請求頭也不會有對應的字段。

Last-Modified/If-Modified-Since

二者的值都是 GMT 格式的時間字符串黑忱,具體過程:

  • 瀏覽器第一次跟服務器請求一個資源宴抚,服務器在返回這個資源的同時,在 responeheader 加上 Last-Modified 字段甫煞,這個 header 字段表示這個資源在服務器上的最后修改時間

  • 瀏覽器再次跟服務器請求這個資源時菇曲,在 requestheader 上加上 If-Modified-Since 字段,這個 header 字段的值就是上一次請求時返回的 Last-Modified 的值

  • 服務器再次收到資源請求時抚吠,根據(jù)瀏覽器傳過來 If-Modified-Since 和資源在服務器上的最后修改時間判斷資源是否有變化常潮,如果沒有變化則返回 304 Not Modified ,但是不會返回資源內(nèi)容楷力;如果有變化喊式,就正常返回資源內(nèi)容孵户。當服務器返回 304 Not Modified 的響應時,response header 中不會再添加 Last-Modified的header 岔留,因為既然資源沒有變化夏哭,那么 Last-Modified 也就不會改變,這是服務器返回 304 時的 response header

  • 瀏覽器收到 304 的響應后献联,就會從緩存中加載資源

  • 如果協(xié)商緩存沒有命中竖配,瀏覽器直接從服務器加載資源時,Last-ModifiedHeader 在重新加載的時候會被更新里逆,下次請求時进胯,If-Modified-Since 會啟用上次返回的Last-Modified

Etag/If-None-Match

這兩個值是由服務器生成的每個資源的唯一標識字符串,只要資源有變化就這個值就會改變原押;其判斷過程與 Last-Modified胁镐、If-Modified-Since 類似,與 Last-Modified 不一樣的是诸衔,當服務器返回 304 Not Modified 的響應時盯漂,由于 ETag 重新生成過,response header 中還會把這個 ETag 返回署隘,即使這個 ETag 跟之前的沒有變化宠能。

Tips:Last-Modified與ETag是可以一起使用的亚隙,服務器會優(yōu)先驗證ETag磁餐,一致的情況下,才會繼續(xù)比對Last-Modified阿弃,最后才決定是否返回304诊霹。

Service Worker

什么是 Service Worker

Service Worker 本質(zhì)上充當Web應用程序與瀏覽器之間的代理服務器,也可以在網(wǎng)絡可用時作為瀏覽器和網(wǎng)絡間的代理渣淳。它們旨在(除其他之外)使得能夠創(chuàng)建有效的離線體驗脾还,攔截網(wǎng)絡請求并基于網(wǎng)絡是否可用以及更新的資源是否駐留在服務器上來采取適當?shù)膭幼鳌K麄冞€允許訪問推送通知和后臺同步API入愧。

Service worker 可以解決目前離線應用的問題鄙漏,同時也可以做更多的事。 Service Worker 可以使你的應用先訪問本地緩存資源棺蛛,所以在離線狀態(tài)時怔蚌,在沒有通過網(wǎng)絡接收到更多的數(shù)據(jù)前,仍可以提供基本的功能(一般稱之為 Offline First)旁赊。這是原生APP 本來就支持的功能桦踊,這也是相比于 web app ,原生 app 更受青睞的主要原因终畅。

再來看看 ?? service worker 能做些什么:

  • 后臺消息傳遞
  • 網(wǎng)絡代理籍胯,轉(zhuǎn)發(fā)請求竟闪,偽造響應
  • 離線緩存
  • 消息推送
  • … …

本文主要以(lishaoy.net)資源緩存為例,闡述下 service worker如何工作

生命周期

service worker 初次安裝的生命周期,如圖 ??

no-shadow

從上 ?? 圖可知杖狼,service worker 工作的流程:

  1. 安裝: service worker URL 通過 serviceWorkerContainer.register() 來獲取和注冊炼蛤。
  2. 激活:service worker 安裝完成后,會接收到一個激活事件(activate event)本刽。 onactivate 主要用途是清理先前版本的 service worker 腳本中使用的資源鲸湃。
  3. 監(jiān)聽: 兩種狀態(tài)
    • 終止以節(jié)省內(nèi)存;
    • 監(jiān)聽獲取 fetch 和消息 message 事件子寓。
  4. 銷毀: 是否銷毀由瀏覽器決定暗挑,如果一個 service worker 長期不使用或者機器內(nèi)存有限,則可能會銷毀這個 worker 斜友。

Tips:激活成功之后炸裆,在 Chrome 瀏覽器里,可以訪問 chrome://inspect/#service-workerschrome://serviceworker-internals/ 可以查看到當前運行的service worker 鲜屏,如圖 ??烹看。

service worker

現(xiàn)在,我們來寫個簡單的例子 ??

注冊 service worker

要安裝 service worker 洛史,你需要在你的頁面上注冊它惯殊。這個步驟告訴瀏覽器你的 service worker 腳本在哪里。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    // Registration was successful
    console.log('ServiceWorker registration successful with scope: ',    registration.scope);
  }).catch(function(err) {
    // registration failed :(
    console.log('ServiceWorker registration failed: ', err);
  });
}

上面的代碼檢查 service worker API 是否可用也殖,如果可用土思,service worker /sw.js 被注冊。如果這個 service worker 已經(jīng)被注冊過忆嗜,瀏覽器會自動忽略上面的代碼己儒。

激活 service worker

在你的 service worker 注冊之后,瀏覽器會嘗試為你的頁面或站點安裝并激活它捆毫。
install 事件會在安裝完成之后觸發(fā)闪湾。install 事件一般是被用來填充你的瀏覽器的離線緩存能力。你需要為 install 事件定義一個 callback 绩卤,并決定哪些文件你想要緩存.

// The files we want to cache
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  '/',
  '/css/main.css',
  '/js/main.js'
];

self.addEventListener('install', function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

在我們的 install callback 中途样,我們需要執(zhí)行以下步驟:

  • 開啟一個緩存
  • 緩存我們的文件
  • 決定是否所有的資源是否要被緩存

上面的代碼中,我們通過 caches.open 打開我們指定的 cache 文件名濒憋,然后我們調(diào)用 cache.addAll 并傳入我們的文件數(shù)組何暇。這是通過一連串 promise (caches.open 和 cache.addAll) 完成的。event.waitUntil 拿到一個 promise 并使用它來獲得安裝耗費的時間以及是否安裝成功跋炕。

監(jiān)聽 service worker

現(xiàn)在我們已經(jīng)將你的站點資源緩存了赖晶,你需要告訴 service worker 讓它用這些緩存內(nèi)容來做點什么。有了 fetch 事件,這是很容易做到的遏插。

每次任何被 service worker 控制的資源被請求到時捂贿,都會觸發(fā) fetch 事件,我們可以給 service worker 添加一個 fetch 的事件監(jiān)聽器胳嘲,接著調(diào)用 event 上的 respondWith() 方法來劫持我們的 HTTP 響應厂僧,然后你用可以用自己的方法來更新他們。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request);
  );
});

caches.match(event.request) 允許我們對網(wǎng)絡請求的資源和 cache 里可獲取的資源進行匹配了牛,查看是否緩存中有相應的資源颜屠。這個匹配通過 urlvary header 進行,就像正常的 HTTP 請求一樣鹰祸。

那么甫窟,我們?nèi)绾畏祷?request 呢,下面 ?? 就是一個例子 ??

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        return fetch(event.request);
      }
    )
  );
});

上面的代碼里我們定義了 fetch 事件蛙婴,在 event.respondWith 里粗井,我們傳入了一個由 caches.match 產(chǎn)生的 promise.caches.match 查找 request 中被 service worker 緩存命中的 response
如果我們有一個命中的 response 街图,我們返回被緩存的值浇衬,否則我們返回一個實時從網(wǎng)絡請求 fetch 的結(jié)果。

sw-toolbox

當然餐济,我也可以使用第三方庫耘擂,例如:lishaoy.net 使用了 sw-toolbox

sw-toolbox 使用非常簡單絮姆,下面 ?? 就是 lishaoy.net 的一個例子 ??

  "serviceWorker" in navigator ? navigator.serviceWorker.register('/sw.js').then(function () {
    navigator.serviceWorker.controller ? console.log("Assets cached by the controlling service worker.") : console.log("Please reload this page to allow the service worker to handle network operations.")
  }).catch(function (e) {
    console.log("ERROR: " + e)
  }) : console.log("Service workers are not supported in the current browser.")

以上是 注冊 一個 service woker

"use strict";
(function () {
    var cacheVersion = "20180527";
    var staticImageCacheName = "image" + cacheVersion;
    var staticAssetsCacheName = "assets" + cacheVersion;
    var contentCacheName = "content" + cacheVersion;
    var vendorCacheName = "vendor" + cacheVersion;
    var maxEntries = 100;
    self.importScripts("/lib/sw-toolbox/sw-toolbox.js");
    self.toolbox.options.debug = false;
    self.toolbox.options.networkTimeoutSeconds = 3;

    self.toolbox.router.get("/images/(.*)", self.toolbox.cacheFirst, {
        cache: {
            name: staticImageCacheName,
            maxEntries: maxEntries
        }
    });

    self.toolbox.router.get('/js/(.*)', self.toolbox.cacheFirst, {
        cache: {
            name: staticAssetsCacheName,
            maxEntries: maxEntries
        }
    });
    self.toolbox.router.get('/css/(.*)', self.toolbox.cacheFirst, {
        cache: {
            name: staticAssetsCacheName,
            maxEntries: maxEntries
        }
    
    ......

    self.addEventListener("install", function (event) {
        return event.waitUntil(self.skipWaiting())
    });
    self.addEventListener("activate", function (event) {
        return event.waitUntil(self.clients.claim())
    })
})();

就這樣搞定了 ?? (具體的用法可以去 sw-toolbox 查看)

有的同學就問醉冤,service worker 這么好用,這個緩存空間到底是多大滚朵?其實冤灾,在 Chrome 可以看到前域,如圖

storage quota

可以看到辕近,大概有 30G ,我的站點只用了 183MB 匿垄,完全夠用了 ??

最后移宅,來兩張圖

from ServiceWorker
Cache Storage

由于,文章篇幅過長椿疗,后續(xù)還會繼續(xù)總結(jié) 架構 方面的優(yōu)化漏峰,例如

  • bigpipe分塊輸出
  • bigrender分塊渲染
  • ...

以及,渲染 方面的優(yōu)化届榄,例如

  • requestAnimationFrame
  • well-change
  • 硬件加速 GPU
  • ...

以及浅乔,性能測試工具,例如

  • PageSpeed
  • audits
  • ...
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市靖苇,隨后出現(xiàn)的幾起案子席噩,更是在濱河造成了極大的恐慌,老刑警劉巖贤壁,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悼枢,死亡現(xiàn)場離奇詭異,居然都是意外死亡脾拆,警方通過查閱死者的電腦和手機馒索,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來名船,“玉大人绰上,你說我怎么就攤上這事∏眨” “怎么了渔期?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渴邦。 經(jīng)常有香客問我疯趟,道長,這世上最難降的妖魔是什么谋梭? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任信峻,我火速辦了婚禮,結(jié)果婚禮上瓮床,老公的妹妹穿的比我還像新娘盹舞。我一直安慰自己,他們只是感情好隘庄,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布踢步。 她就那樣靜靜地躺著,像睡著了一般丑掺。 火紅的嫁衣襯著肌膚如雪获印。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天街州,我揣著相機與錄音兼丰,去河邊找鬼。 笑死唆缴,一個胖子當著我的面吹牛鳍征,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播面徽,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼艳丛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起氮双,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤旺聚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后眶蕉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砰粹,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年造挽,在試婚紗的時候發(fā)現(xiàn)自己被綠了碱璃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡饭入,死狀恐怖嵌器,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谐丢,我是刑警寧澤爽航,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站乾忱,受9級特大地震影響讥珍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窄瘟,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一衷佃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹄葱,春花似錦氏义、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至竣况,卻和暖如春克婶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帕翻。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工鸠补, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萝风,地道東北人嘀掸。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像规惰,于是被迫代替她去往敵國和親睬塌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345