緩存&PWA實(shí)踐

緩存&PWA 實(shí)踐

一潭辈、背景

從上一篇《前端動(dòng)畫實(shí)現(xiàn)與原理分析》,我們從 Performance 進(jìn)行動(dòng)畫的性能分析澈吨,并根據(jù) Performance 分析來(lái)優(yōu)化動(dòng)畫。但谅辣,前端不僅僅是實(shí)現(xiàn)流暢的動(dòng)畫修赞。ToB 項(xiàng)目會(huì)經(jīng)常與數(shù)據(jù)的保存屈藐、渲染打交道。例如開發(fā)中搓扯,為了提高用戶體驗(yàn)公壤,遇到了一些場(chǎng)景换可,其實(shí)就是在利用某些手段,來(lái)進(jìn)行性能優(yōu)化确憨。

  • Select 下拉:做前端分頁(yè)展示 → 避免一次性渲染數(shù)據(jù)造成瀏覽器的假死狀態(tài)译荞;
  • indexedDB:存儲(chǔ)數(shù)據(jù) → 用戶下一次進(jìn)入時(shí)瓤的,保存上一次編輯的狀態(tài) ……

那不免引發(fā)思考,我們從緩存與數(shù)據(jù)存儲(chǔ)來(lái)思考吞歼,該如何優(yōu)化圈膏?

二、 HTTP 緩存

是什么篙骡?

Http 緩存其實(shí)就是瀏覽器保存通過 HTTP 獲取的所有資源稽坤,
是瀏覽器將網(wǎng)絡(luò)資源存儲(chǔ)在本地的一種行為。

請(qǐng)求的資源在哪里糯俗?

  1. 6.8kB + 200 狀態(tài)碼: 沒有命中緩存尿褪,實(shí)際的請(qǐng)求,從服務(wù)器上獲取資源叶骨;
  2. memory cache: 資源緩存在內(nèi)存中,不會(huì)請(qǐng)求服務(wù)器茫多,一般已經(jīng)加載過該資源且緩存在內(nèi)存中祈匙,當(dāng)關(guān)閉頁(yè)面時(shí)忽刽,此資源就被內(nèi)存釋放掉了;
  3. disk cache: 資源緩存在磁盤中夺欲,不會(huì)請(qǐng)求服務(wù)器跪帝,但是該資源也不會(huì)隨著關(guān)閉頁(yè)面就釋放掉;
  4. 304 狀態(tài)碼:請(qǐng)求服務(wù)器些阅,發(fā)現(xiàn)資源沒有被更改伞剑,返回 304 后,資源從本地取出市埋;
  5. service worker: 應(yīng)用級(jí)別的存儲(chǔ)手段黎泣;

HTTP 緩存分類

1. 強(qiáng)緩存

  1. 瀏覽器加載資源時(shí),會(huì)先根據(jù)本地緩存資源的 header 中的信息判斷是否命中強(qiáng)緩存缤谎。如果命中抒倚,則不會(huì)像服務(wù)器發(fā)送請(qǐng)求,而是直接從緩存中讀取資源坷澡。
  2. 強(qiáng)緩存可以通過設(shè)置 HTTP Header 來(lái)實(shí)現(xiàn):
    http1.0 → Expires: 響應(yīng)頭包含日期/時(shí)間托呕, 即在此時(shí)候之后,響應(yīng)過期频敛。
    http1.1 → Cache-Control:max-age=: 設(shè)置緩存存儲(chǔ)的最大周期项郊,超過這個(gè)時(shí)間緩存被認(rèn)為過期(單位秒)。與Expires相反斟赚,時(shí)間是相對(duì)于請(qǐng)求的時(shí)間

?? Cache-control

  • cache-control: max-age=3600 :表示相對(duì)時(shí)間
  • cache-control:no-cache → 可以存儲(chǔ)在本地緩存的着降,只是在原始服務(wù)器進(jìn)行新鮮度在驗(yàn)證之前,緩存不能將其提供給客戶端使用
  • cache-control: no-store → 禁止緩存對(duì)響應(yīng)進(jìn)行復(fù)制拗军,也就是真正的不緩存數(shù)據(jù)在本地任洞;
  • catch-control:public → 可以被所有用戶緩存(多用戶共享)厌殉,包括終端、CDN 等
  • cache-control: private → 私有緩存

2. 協(xié)商緩存

  1. 當(dāng)瀏覽器對(duì)某個(gè)資源的請(qǐng)求沒有命中強(qiáng)緩存侈咕,就會(huì)發(fā)一個(gè)請(qǐng)求到服務(wù)器公罕,驗(yàn)證協(xié)商緩存是否命中,如果協(xié)商緩存命中耀销,請(qǐng)求返回的 http 狀態(tài) 304楼眷,并且會(huì)顯示 Not Modified 的字符串;
  2. 協(xié)商緩存通過【last-Modified熊尉,if-Modified-Since】與【ETag, if-None-Match】來(lái)管理的罐柳。

  • 「Last-Modified、If-Modified-Since」

last-Modified : 表示本地文件最后修改的日期狰住,瀏覽器會(huì)在請(qǐng)求頭中帶上 if-Modified-since(也是上次返回的 Last-Modified 的值)张吉,服務(wù)器會(huì)將這個(gè)值與資源修改的時(shí)間進(jìn)行匹配,如果時(shí)間不一致催植,服務(wù)器會(huì)返回新的資源肮蛹,并且將 Last-modified 值更新,并作為響應(yīng)頭返回給瀏覽器创南。如果時(shí)間一致伦忠,表示資源沒有更新,服務(wù)器會(huì)返回 304 狀態(tài)稿辙,瀏覽器拿到響應(yīng)狀態(tài)碼后從本地緩存中讀取資源昆码。

  • ETag、If-None-Match」

http 1.1 中邻储, 服務(wù)器通過 Etag 來(lái)設(shè)置響應(yīng)頭緩存標(biāo)示赋咽。Etag 是由服務(wù)器來(lái)生成的。

第一次請(qǐng)求時(shí)吨娜,服務(wù)器會(huì)將資源和 ETag 一并返回瀏覽器脓匿,瀏覽器將兩者緩存到本地緩存中。

第二次請(qǐng)求時(shí)萌壳,瀏覽器會(huì)將 ETag 的值放到 If-None-Match 請(qǐng)求頭去訪問服務(wù)器亦镶,服務(wù)器接收請(qǐng)求后,會(huì)將服務(wù)器中的文件標(biāo)識(shí)與瀏覽器發(fā)來(lái)的標(biāo)識(shí)進(jìn)行比對(duì)袱瓮,如果不同缤骨, 服務(wù)器會(huì)返回更新的資源和新的 Etag,如果相同尺借,服務(wù)器會(huì)返回 304 狀態(tài)碼绊起,瀏覽器讀取緩存。

?? 思考為什么有了 Last-Modified 這一對(duì)兒燎斩,還需要 Etag 來(lái)標(biāo)識(shí)是否過期進(jìn)行命中協(xié)商緩存

  1. 文件的周期性更改虱歪,但是文件的內(nèi)容沒有改變蜂绎,僅僅改變了修改時(shí)間,這個(gè)時(shí)候笋鄙,并不希望客戶端認(rèn)為該文件被修改了师枣,而重新獲取。
  2. 如果文件文件頻繁修改萧落,比如 1s 改了 N 次践美,If-Modified-Since 無(wú)法判斷修改的,會(huì)導(dǎo)致文件已經(jīng)修改但是獲取的資源還是舊的找岖,會(huì)存在問題陨倡。
  3. 某些服務(wù)器不能精確得到文件的最后修改時(shí)間,導(dǎo)致資源獲取的問題许布。

?? 如果 Etag 與 Last-Modified 同時(shí)存在兴革,服務(wù)器會(huì)先檢查 ETag,然后在檢查 Last-Modified, 最終確定是返回 304 或 200蜜唾。

HTTP 緩存實(shí)踐

測(cè)試環(huán)境: 利用 Koa杂曲,搭建一個(gè) node 服務(wù),用來(lái)測(cè)試如何命中強(qiáng)緩存還是協(xié)商緩存

當(dāng) index.js 沒有配置任何關(guān)于緩存的配置時(shí)灵妨, 無(wú)論怎么刷新 chrome解阅,都沒有緩存機(jī)制的落竹;

  • 注意??:在開始實(shí)驗(yàn)之前要把 network 面板的 Disable cache 勾選去掉泌霍,這個(gè)選項(xiàng)表示禁用瀏覽器緩存,瀏覽器請(qǐng)求會(huì)帶上 Cache-Control: no-cache 和 Pragma: no-cache 頭部信息述召。

1. 命中強(qiáng)緩存

app.use(async (ctx) => {
    // ctx.body = 'hello Koa'
    const url = ctx.request.url;
    if(url === '/'){
        // 訪問跟路徑返回 index.html
        ctx.set('Content-type', 'text/html');
        ctx.body = await parseStatic('./index.html')
    }else {
        ctx.set('Content-Type', parseMime(url))
        ctx.set('Expires', new Date(Date.now() + 10000).toUTCString()) // 實(shí)驗(yàn)1
        ctx.set('Cache-Control','max-age=20') // 實(shí)驗(yàn)2
        ctx.body = await parseStatic(path.relative('/', url))
    }
})

app.listen(3000, () => {
    console.log('starting at port 3000')
})

2. 命中協(xié)商緩存

         /**
         * 命中協(xié)商緩存
         * 設(shè)置 Last-Modified, If-Modified-Since
         */
         ctx.set('cache-control', 'no-cache'); // 關(guān)閉強(qiáng)緩存
         const isModifiedSince = ctx.request.header['if-modified-since'];
         const fileStat = await getFileStat(filePath);
         if(isModifiedSince === fileStat.mtime.toGMTString()){
             ctx.status = 304
         }else {
             ctx.set('Last-Modified', fileStat.mtime.toGMTString())
         }  
         ctx.body = await parseStatic(path.relative('/', url))

        /**
         * 命中協(xié)商緩存
         * 設(shè)置 Etag, If-None-Match
         */
         ctx.set('cache-control', 'no-cache');
         const ifNoneMatch = ctx.request.headers['if-none-match'];
         const fileBuffer = await parseStatic(filePath);
         const hash = crypto.createHash('md5');
         hash.update(fileBuffer);
         const etag = `"${hash.digest('hex')}"`
         if (ifNoneMatch === etag) {
            ctx.status = 304
          } else {
            ctx.set('Etag', etag)
            ctx.body = fileBuffer
          }
    }

三朱转、 瀏覽器緩存

1.Cookies

  • MDN 定義: 是服務(wù)器發(fā)送到用戶瀏覽器并報(bào)訊在本地的一小塊數(shù)據(jù),他會(huì)在瀏覽器下次想統(tǒng)一服務(wù)器再次發(fā)送請(qǐng)求時(shí)被攜帶并發(fā)送到服務(wù)器上积暖。
  • 應(yīng)用場(chǎng)景:
    • 會(huì)話狀態(tài)管理【用戶登陸狀態(tài)藤为,購(gòu)物車,游戲分?jǐn)?shù)或其他需要記錄的信息】
    • 個(gè)性化設(shè)置(如用戶自定義設(shè)置夺刑、主題等)
    • 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)
  • cookie 的讀取與寫入:
class Cookie {
    getCookie: (name) => {
        const reg = new RegExp('(^| )'+name+'=([^;]*)')
        let match = document.cookie.match(reg); //  [全量缅疟,空格,value遍愿,‘存淫;’]
        if(match) {return decodeURI(match[2])}
    }
    setCookie:(key,value,days,domain) => {
        // username=John Smith; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/
      let d = new Date();
    d.setTime(d.getTime()+(days*24*60*60*1000));
    let expires = "; expires="+d.toGMTString();
        let domain = domain ? '; domain='+domain : '';
        document.cookie = name + '=' + value + expires + domain + '; path=/'
        
    }
    deleteCookie: (name: string, domain?: string, path?: string)=> {
        // 刪除cookie,只需要將時(shí)間設(shè)置為過期時(shí)間沼填,而無(wú)需刪除cookie的value
        const d = new Date(0);
        domain = domain ? `; domain=${domain}` : '';
        path = path || '/';
        document.cookie =
            name + '=; expires=' + d.toUTCString() + domain + '; path=' + path;
    },
}
  • 存在的問題: 由于通過 Cookie 存儲(chǔ)的數(shù)據(jù)桅咆,每次請(qǐng)求都會(huì)攜帶在請(qǐng)求頭。對(duì)與一些數(shù)據(jù)是無(wú)需交給提交后端的坞笙,這個(gè)不免會(huì)帶來(lái)額外的開銷岩饼。

2.WebStorage API

瀏覽器能以一種比使用 Cookie 更直觀的方式存儲(chǔ)鍵值對(duì)

localStorage

為每一個(gè)給定的源(given origin)維持一個(gè)獨(dú)立的存儲(chǔ)區(qū)域荚虚,瀏覽器關(guān)閉,然后重新打開后數(shù)據(jù)仍然存在籍茧。

sessionStorage

為每一個(gè)給定的源(given origin)維持一個(gè)獨(dú)立的存儲(chǔ)區(qū)域版述,該存儲(chǔ)區(qū)域在頁(yè)面會(huì)話期間可用(即只要瀏覽器處于打開狀態(tài),包括頁(yè)面重新加載和恢復(fù))寞冯。

3.indexedDB 與 webSQL

webSQL

基本操作與實(shí)際數(shù)據(jù)庫(kù)操作基本一致院水。
最終的數(shù)據(jù)去向,一般只是做臨時(shí)存儲(chǔ)和大型網(wǎng)站的業(yè)務(wù)運(yùn)行存儲(chǔ)緩存的作用简十,頁(yè)面刷新后該庫(kù)就不存在了檬某。而其本身與關(guān)系數(shù)據(jù)庫(kù)的概念比較相似。

indexedDB

隨著瀏覽器的功能不斷增強(qiáng)螟蝙,越來(lái)越多的網(wǎng)站開始考慮恢恼,將大量數(shù)據(jù)儲(chǔ)存在客戶端,這樣可以減少?gòu)姆?wù)器獲取數(shù)據(jù)胰默,直接從本地獲取數(shù)據(jù)〕“撸現(xiàn)有的瀏覽器數(shù)據(jù)儲(chǔ)存方案,都不適合儲(chǔ)存大量數(shù)據(jù)牵署;

IndexedDB 是瀏覽器提供的本地?cái)?shù)據(jù)庫(kù)漏隐, 允許儲(chǔ)存大量數(shù)據(jù),提供查找接口奴迅,還能建立索引青责。這些都是 LocalStorage 所不具備的。就數(shù)據(jù)庫(kù)類型而言取具,IndexedDB 不屬于關(guān)系型數(shù)據(jù)庫(kù)(不支持 SQL 查詢語(yǔ)句)脖隶,更接近 NoSQL 數(shù)據(jù)庫(kù)。


四暇检、應(yīng)用程序緩存

Service Worker

在提及 Service Worker 之前产阱,需要對(duì) web Worker 有一些了解;

webWorker : Web Worker 是瀏覽器內(nèi)置的線程所以可以被用來(lái)執(zhí)行非阻塞事件循環(huán)的 JavaScript 代碼块仆。 js 是單線程构蹬,一次只能完成一件事,如果出現(xiàn)一個(gè)復(fù)雜的任務(wù)悔据,線程就會(huì)被阻塞庄敛,嚴(yán)重影響用戶體驗(yàn), Web Worker 的作用就是允許主線程創(chuàng)建 worker 線程蜜暑,與主線程同時(shí)進(jìn)行铐姚。worker 線程只需負(fù)責(zé)復(fù)雜的計(jì)算,然后把結(jié)果返回給主線程就可以了。簡(jiǎn)單的理解就是隐绵,worker 線程執(zhí)行復(fù)雜計(jì)算并且頁(yè)面(主線程)ui 很流暢之众,不會(huì)被阻塞。


Service Worker 是瀏覽器和網(wǎng)絡(luò)之間的虛擬代理依许。其解決了如何正確緩存往后網(wǎng)站資源并使其在離線時(shí)可用的問題棺禾。

Service Worker 運(yùn)行在一個(gè)與頁(yè)面 js 主線程獨(dú)立的線程上,并且無(wú)權(quán)訪問 DOM 結(jié)構(gòu)峭跳。他的 API 是非阻塞的膘婶,并且可以在不同的上下文之間發(fā)送和接收消息。

他不僅僅提供離線功能蛀醉,還可以做包括處理通知悬襟、在單獨(dú)的線程上執(zhí)行繁重的計(jì)算等事務(wù)。Service Workers 非常強(qiáng)大拯刁,因?yàn)樗麄兛梢钥刂凭W(wǎng)絡(luò)請(qǐng)求脊岳,修改網(wǎng)絡(luò)請(qǐng)求,返回緩存的自定義響應(yīng)或者合成響應(yīng)垛玻。

2.PWA

?? PWA割捅,全稱 Progressive web apps,即漸進(jìn)式 Web 應(yīng)用帚桩。PWA 技術(shù)主要作用為構(gòu)建跨平臺(tái)的 Web 應(yīng)用程序亿驾,并使其具有與原生應(yīng)用程序相同的用戶體驗(yàn)。
?? PWA 的核心: 最根本账嚎、最基本的莫瞬,就是 Service Worker 以及在其內(nèi)部使用的 Cache API。只要通過 Service Worker 與 Cache API醉锄,實(shí)現(xiàn)了對(duì)網(wǎng)站頁(yè)面的緩存乏悄、對(duì)頁(yè)面請(qǐng)求的攔截、對(duì)頁(yè)面緩存的操縱 恳不。

為什么使用 PWA:

傳統(tǒng)的 Web 存在的問題:

  1. 缺乏直接入口,需要記住他的域名开呐,或者是保存在收藏夾烟勋,尋找起來(lái)不夠方便;
  2. 依賴于網(wǎng)絡(luò)筐付。只要客戶端處于斷網(wǎng)的狀態(tài)卵惦,整個(gè) web 就處于癱瘓狀態(tài),客戶端無(wú)法使用瓦戚;
  3. 無(wú)法像 Native APP 推送消息沮尿。

傳統(tǒng) Native APP 存在的問題:

  1. 需要安裝與下載。哪怕只是使用 APP 的某個(gè)功能,也是需要全盤下載的畜疾;
  2. 開發(fā)成本高赴邻,一般需要兼容安卓與 IOS 系統(tǒng);
  3. 發(fā)布需要審核啡捶;
  4. 更新成本高……

PWA 的存在姥敛,就是為了解決以上問題所帶來(lái)的麻煩:
優(yōu)勢(shì):

  1. 桌面入口,打開便捷瞎暑;
  2. 離線可用彤敛,不用過度依賴網(wǎng)絡(luò);
  3. 安裝方便了赌;
  4. 一次性開發(fā)墨榄,無(wú)需審核,所有平臺(tái)可用勿她;
  5. 能夠進(jìn)行消息推送
  • Web App Manifest Web App Manifest(Web 應(yīng)用程序清單)概括地說(shuō)是一個(gè)以 JSON 形式集中書寫頁(yè)面相關(guān)信息和配置的文件渠概。
{
  "short_name": "User Mgmt",
  "name": "User Management",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "logo192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "logo512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": ".", // 調(diào)整網(wǎng)站的起始鏈接
  "display": "standalone", // 設(shè)定網(wǎng)站提示模式 : standalone 表示隱藏瀏覽器的UI
  "theme_color": "#000000", // 設(shè)定網(wǎng)站每個(gè)頁(yè)面的主題顏色
  "background_color": "#ffffff" // 設(shè)定背景顏色
}
  • ServiceWorker.js 代碼
/* eslint-disable no-restricted-globals */

// 確定哪些資源需要緩存
const CACHE_VERSION = 0;
const CACHE_NAME = 'cache_v' + CACHE_VERSION;
const CACHE_URL = [
  '/',
  'index.html',
  'favicon.ico',
  'serviceWorker.js',
  'static/js/bundle.js',
  'manifest.json',
  'users'
]
const preCache = () => {
  return caches
    .open(CACHE_NAME)
    .then((cache) => {
      return cache.addAll(CACHE_URL)
    })
}
const clearCache = () => {
  return caches.keys().then(keys => {
    keys.forEach(key => {
      if (key !== CACHE_NAME) {
        caches.delete(key)
      }
    })
  })
}
// 進(jìn)行緩存
self.addEventListener('install', (event) => {
  event.waitUntil(
    preCache()
  )
})

// 刪除舊的緩存
self.addEventListener('activated', (event) => {
  event.waitUntil(
    clearCache()
  )
})

console.log('hello, service wold');

self.addEventListener('fetch', (event) => {
  console.log('request:', event.request.url)
  event.respondWith(
    fetch(event.request).catch(() => { // 優(yōu)先網(wǎng)絡(luò)請(qǐng)求,如果失敗嫂拴,則從緩存中拿資源
      return caches.match(event.request)
    })
  )
})
  • PWA 調(diào)試

當(dāng)離線的時(shí)候依然拿到緩存的資源播揪,并且正常展示,可以看出資源被 serviceWorker 緩存筒狠。

借助開發(fā)者工具:
chrome devtools : chrome://inspect/#service-workers 猪狈,可以展示當(dāng)前設(shè)備上激活和存儲(chǔ)的 service worker

五、總結(jié)與思考

參考優(yōu)秀網(wǎng)站:

  1. 語(yǔ)雀: https://www.yuque.com/dashboard 辩恼;
  2. PWA 例子: https://mdn.github.io/pwa-examples/js13kpwa/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雇庙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子灶伊,更是在濱河造成了極大的恐慌疆前,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聘萨,死亡現(xiàn)場(chǎng)離奇詭異竹椒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)米辐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門胸完,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人翘贮,你說(shuō)我怎么就攤上這事赊窥。” “怎么了狸页?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵锨能,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)址遇,這世上最難降的妖魔是什么熄阻? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮傲隶,結(jié)果婚禮上饺律,老公的妹妹穿的比我還像新娘。我一直安慰自己跺株,他們只是感情好复濒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乒省,像睡著了一般巧颈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袖扛,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天砸泛,我揣著相機(jī)與錄音,去河邊找鬼蛆封。 笑死唇礁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惨篱。 我是一名探鬼主播盏筐,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼砸讳!你這毒婦竟也來(lái)了琢融?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤簿寂,失蹤者是張志新(化名)和其女友劉穎漾抬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體常遂,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纳令,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烈钞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泊碑。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖毯欣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臭脓,我是刑警寧澤酗钞,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響砚作,放射性物質(zhì)發(fā)生泄漏窘奏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一葫录、第九天 我趴在偏房一處隱蔽的房頂上張望着裹。 院中可真熱鬧,春花似錦米同、人聲如沸骇扇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)少孝。三九已至,卻和暖如春熬苍,著一層夾襖步出監(jiān)牢的瞬間稍走,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工柴底, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留婿脸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓柄驻,卻偏偏與公主長(zhǎng)得像狐树,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凿歼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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