#003 HTML5緩存機(jī)制淺析

時(shí)間:2017-03-27 18:02:47

該文章為 《HTML5緩存機(jī)制淺析:移動(dòng)端Web加載性能優(yōu)化》 的讀書(shū)筆記,整理一下自己的收獲另伍。

一粥喜、HTML5緩存機(jī)制介紹

HTML5是新一代的HTML標(biāo)準(zhǔn)者祖,加入很多新的特性。離線存儲(chǔ)(也可稱為緩存機(jī)制)是其中一個(gè)非常重要的特性瘤旨。HTML5引入的離線存儲(chǔ),這意味著Web應(yīng)用可進(jìn)行緩存竖伯,并可在沒(méi)有因特網(wǎng)連接時(shí)進(jìn)行訪問(wèn)存哲。

1.1 HTML5應(yīng)用程序緩存帶來(lái)的優(yōu)勢(shì)

  1. 離線瀏覽
  2. 提高加載速度(從緩存中加載)
  3. 減少服務(wù)器負(fù)載(瀏覽器將只從服務(wù)器下載更新的資源,強(qiáng)緩存的則不發(fā)起HTTP請(qǐng)求七婴,協(xié)商緩存會(huì)發(fā)起HTTP祟偷,和服務(wù)器驗(yàn)證文件是否修改過(guò)了)

1.2 現(xiàn)有的HTML5緩存機(jī)制

勾選的,表示熟悉

  • [x] 瀏覽器緩存機(jī)制(強(qiáng)緩存打厘,協(xié)商緩存)
  • [x] DOM Storgage 存儲(chǔ)機(jī)制
  • [ ] Web SQL DataBase 存儲(chǔ)機(jī)制 【關(guān)系型數(shù)據(jù)庫(kù)】
  • [ ] Application Cache (App Cache)機(jī)制
  • [ ] Indexed DataBase (IndexedDB) 【NoSQL】
  • [ ] File System API

Web SQL DataBase 官方的標(biāo)準(zhǔn)文檔不在推薦使用修肠,將來(lái)也不再維護(hù),(2015年12月);

  • 各大桌面瀏覽器和移動(dòng)端瀏覽器都有很好的實(shí)現(xiàn)這個(gè)接口婚惫,兼容性問(wèn)題不大氛赐,底層基本都是 sqlite(正因?yàn)檫@樣魂爪,作為一個(gè)web標(biāo)準(zhǔn)是不可接受的)

    • 經(jīng)測(cè)試,IOS上容量最大支持50M艰管,不過(guò)用系統(tǒng)自帶的safari滓侍,超過(guò)5MB,會(huì)主動(dòng)提醒用戶是否要增加數(shù)據(jù)庫(kù)大小牲芋,不友好(不過(guò)微信里面不會(huì))
    • 5MB 如果用來(lái)存一些日志撩笆,還是夠的(當(dāng)然需要記得清理舊的日志)
    • websql 目前是主要的解決方案(騰訊用來(lái)做日志記錄,方便排查用戶的錯(cuò)誤反饋)
  • Indexed DataBase 是下一代的客服端結(jié)構(gòu)化數(shù)據(jù)持久存儲(chǔ)反感缸浦,目前各大瀏覽器中也有很好的支持夕冲,是未來(lái)用來(lái)替換 websql 的方案。

等下查下最新的情況裂逐。

這里只是官網(wǎng)推薦的比較歹鱼,具體使用還需要看目前業(yè)界上的情況。
比如websql目前還是主流的卜高,indexdb是未來(lái)用來(lái)替換的弥姻,具體需要多久,還需要等等掺涛。

1.3 目標(biāo)

  • [ ] 分析各種緩存機(jī)制的原理庭敦,用法,特點(diǎn)
  • [ ] Android移動(dòng)端Web性能加載優(yōu)化
  • [ ] 如何利用緩存機(jī)制提高Web加載性能

二薪缆、分析各個(gè)緩存機(jī)制

先對(duì)各個(gè)緩存機(jī)制有一個(gè)大體的了解秧廉,然后才能去考慮如何優(yōu)化。目前對(duì) 瀏覽器緩存和DOM Storage比較熟悉拣帽,其他的大體了解疼电,但是沒(méi)有項(xiàng)目中用過(guò)。

2.1 瀏覽器緩存機(jī)制

瀏覽器緩存機(jī)制是指通過(guò)HTTP協(xié)議頭里的 Cache-Control / Expires 和 Last-Modified / ETag 等字段控制文件緩存的機(jī)制诞外。

和 DOM Storage, AppCache 等緩存機(jī)制澜沟,本質(zhì)一樣。 一個(gè)在HTTP協(xié)議層實(shí)現(xiàn)(瀏覽器緩存)峡谊,一個(gè)在應(yīng)用層實(shí)現(xiàn)茫虽。

2.1.1 強(qiáng)緩存

采用 Cache-Control 和 Expires 來(lái)控制緩存。 Chrome開(kāi)發(fā)者工具NetWork既们, 查看為 form disk cache

  • Cache-Control (HTTP1.1標(biāo)準(zhǔn)中新增的字段)

    1. 控制文件本地緩存的有效時(shí)間.
    2. Cache-Control:max-age=600 表示文件本地緩存有效時(shí)間600s濒析,接下來(lái)600s內(nèi)請(qǐng)求這個(gè)資源,瀏覽器不發(fā)出HTTP請(qǐng)求啥纸,直接從本地緩存拿
    3. 是一個(gè)相對(duì)的時(shí)間
  • Expires (HTTP1.0標(biāo)準(zhǔn)中的字段)
    1. Expires: Thu, 10 Nov 2015 08:45:11 GMT 表示在這個(gè)時(shí)間前号杏,緩存有效
    2. 是一個(gè)絕對(duì)時(shí)間,由于服務(wù)器的時(shí)間和客服端的時(shí)間可能不一致造成緩存問(wèn)題,因此引入了HTTP1.1的Cache-Control
    3. 優(yōu)先級(jí)小于 Cache-Control

2.1.2 協(xié)商緩存

HTTP 的狀態(tài)為 304 表示 協(xié)商緩存

  • Last-Modified 和 If-Modified-Since

    Last-Modified是標(biāo)識(shí)文件在服務(wù)器上的最新更新時(shí)間盾致。下次請(qǐng)求時(shí)主经,如果文件緩存過(guò)期,瀏覽器通過(guò)If-Modified-Since字段帶上這個(gè)時(shí)間庭惜,發(fā)送給服務(wù)器罩驻,由服務(wù)器比較時(shí)間戳來(lái)判斷文件是否有修改。如果沒(méi)有修改护赊,服務(wù)器返回304告訴瀏覽器繼續(xù)使用緩存惠遏;如果有修改,則返回200骏啰,同時(shí)返回最新的文件节吮。

  • ETag 和 If-None-Match

    Etag也是和Last-Modified一樣,對(duì)文件進(jìn)行標(biāo)識(shí)的字段判耕。不同的是透绩,Etag的取值是一個(gè)對(duì)文件進(jìn)行標(biāo)識(shí)的特征字串。在向服務(wù)器查詢文件是否有更新時(shí)壁熄,瀏覽器通過(guò)If-None-Match字段把特征字串發(fā)送給服務(wù)器渺贤,由服務(wù)器和文件最新特征字串進(jìn)行匹配,來(lái)判斷文件是否有更新请毛。沒(méi)有更新回包304,有更新回包200瞭亮。

Etag和Last-Modified可根據(jù)需求使用一個(gè)或兩個(gè)同時(shí)使用方仿。兩個(gè)同時(shí)使用時(shí),只要滿足基中一個(gè)條件统翩,就認(rèn)為文件沒(méi)有更新仙蚜。

2.1.3 F5 和 Ctrl+F5 的特殊情況

手動(dòng)刷新頁(yè)面(F5),瀏覽器會(huì)直接認(rèn)為緩存已經(jīng)過(guò)期(可能緩存還沒(méi)有過(guò)期)厂汗,在請(qǐng)求中加上字段:Cache-Control:max-age=0委粉,發(fā)包向服務(wù)器查詢是否有文件是否有更新。

可能存在304協(xié)商緩存

強(qiáng)制刷新頁(yè)面(Ctrl+F5)娶桦,瀏覽器會(huì)直接忽略本地的緩存(有緩存也會(huì)認(rèn)為本地沒(méi)有緩存)贾节,在請(qǐng)求中加上字段:Cache-Control:no-cache(或 Pragma:no-cache),發(fā)包向服務(wù)重新拉取文件衷畦。

不存在強(qiáng)緩存和協(xié)商緩存

三栗涂、LocalStorage, SessionStorage

一個(gè)簡(jiǎn)單的鍵值對(duì)存儲(chǔ)系統(tǒng)祈争,接口簡(jiǎn)單實(shí)用斤程,兼容性也很好。有大小限制菩混,提供5M(不同瀏覽器可能不同忿墅,區(qū)分host扁藕, cookie 只有 4k)

3.1 LocalStorage和 SessionStorage的用法

interface Storage { 

    readonly attribute unsigned long length;
    
    [IndexGetter] DOMString key(in unsigned long index); 
    
    [NameGetter] DOMString getItem(in DOMString key); 
    
    [NameSetter] void setItem(in DOMString key, in DOMString data); 
    
    [NameDeleter] void removeItem(in DOMString key); 
    
    void clear();
};

3.2 LocalStorage 和 SessionStorage 的區(qū)別

LocalStorage 和 SessionStorage 的區(qū)別, SessionStorage 在頁(yè)面選項(xiàng)卡關(guān)閉后疚脐,就不存在了亿柑, 但是 LocalStorage會(huì)還存在。但是注意:頁(yè)面刷新的時(shí)候亮曹,SessionStorage還是存在的橄杨。

比如在 頁(yè)面刷新后,表單填寫(xiě)的內(nèi)容還存在照卦,這個(gè)時(shí)候使用 SessionStorage最有用了式矫。

3.3 總結(jié)

分析:Dom Storage給Web提供了一種更錄活的數(shù)據(jù)存儲(chǔ)方式,存儲(chǔ)空間更大(相對(duì)Cookies)役耕,用法也比較簡(jiǎn)單采转,方便存儲(chǔ)服務(wù)器或本地的一些臨時(shí)數(shù)據(jù)。

  • 適合存儲(chǔ)簡(jiǎn)單數(shù)據(jù)
  • 如果存結(jié)構(gòu)化數(shù)據(jù)瞬痘,可以借住JSON的功能
  • 不適合存儲(chǔ)復(fù)雜故慈,存儲(chǔ)空間要求比較大的數(shù)據(jù),還有不適合存靜態(tài)文件

四框全、Web SQL Database存儲(chǔ)機(jī)制

目前用來(lái)存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)還是比較主流的一個(gè)選擇察绷。 參考 《騰訊開(kāi)發(fā)工程師:前端異常監(jiān)控到底怎么做》

IOS下,最大支持50M , 系統(tǒng)自帶瀏覽器Safari津辩,默認(rèn)超過(guò)5M的時(shí)候拆撼,會(huì)彈出提示讓用戶增加大小,不太友好喘沿。

微信瀏覽器下闸度,不會(huì)提示。

4.1 簡(jiǎn)單例子

<script>
    if(window.openDatabase){
      //打開(kāi)數(shù)據(jù)庫(kù)蚜印,如果沒(méi)有則創(chuàng)建
      var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024);
       //通過(guò)事務(wù)莺禁,創(chuàng)建一個(gè)表,并添加兩條記錄
      db.transaction(function (tx) {
           tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
           tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
           tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
       });
      //查詢表中所有記錄窄赋,并展示出來(lái)
     db.transaction(function (tx) {
         tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
             var len = results.rows.length, i;
             msg = "<p>Found rows: " + len + "</p>";
             for(i=0; i<len; i++){
                 msg += "<p>" + results.rows.item(i).log + "</p>";
             }
             document.querySelector('#status').innerHTML =  msg;
             }, null);
      });
}
</script>
<div id="status" name="status">Status Message</div><br>

4.2 分析

  • 適合存儲(chǔ)結(jié)構(gòu)復(fù)雜的數(shù)據(jù)
  • 使用起來(lái)相對(duì)麻煩點(diǎn)哟冬,需要了解SQL語(yǔ)句
  • 不是和做靜態(tài)文件的存儲(chǔ)

五、Application Cache 機(jī)制

似乎是為了支持 Web App 離線使用二開(kāi)發(fā)的緩存機(jī)制忆绰。 緩存 和 瀏覽器緩存機(jī)制類似柒傻。

  • 按文件單位進(jìn)行緩存
  • 通過(guò)manifest來(lái)控制文件的緩存
  • 大小5M的限制

AppCache的原理有兩個(gè)關(guān)鍵點(diǎn):manifest屬性和 manifest文件。

  • 緩存文件有更新需要更新 manifest文件 [正常有更新文件就修改mainfest里面的一個(gè)版本號(hào)]
CACHE MANIFEST
# 2012-02-21 v1.0.0
/theme.css
/logo.gif
/main.js
NETWORK:
login.asp
FALLBACK:
/html/ /offline.html <br>

5.2 分析

分析:AppCache看起來(lái)是一種比較好的緩存方法较木,除了緩存靜態(tài)資源文件外红符,也適合構(gòu)建Web離線 App。在實(shí)際使用中有些需要注意的地方,有一些可以說(shuō)是”坑“预侯。

  1. 要更新緩存的文件致开,需要更新包含它的manifest文件,那怕只加一個(gè)空格萎馅。常用的方法双戳,是修改manifest文件注釋中的版本號(hào)。如:# 2012-02-21 v1.0.0糜芳;

  2. 被緩存的文件飒货,瀏覽器是先使用,再通過(guò)檢查manifest文件是否有更新來(lái)更新緩存文件峭竣。這樣緩存文件可能用的不是最新的版本塘辅。

  3. 在更新緩存過(guò)程中,如果有一個(gè)文件更新失敗皆撩,則整個(gè)更新會(huì)失敗扣墩。

  4. manifest和引用它的HTML要在相同Host。

  5. manifest文件中的文件列表扛吞,如果是相對(duì)路徑呻惕,則是相對(duì)manifest文件的相對(duì)路徑。

  6. manifest也有可能更新出錯(cuò)滥比,導(dǎo)致緩存文件更新失敗亚脆。

  7. 沒(méi)有緩存的資源在已經(jīng)緩存的HTML中不能加載,即使有網(wǎng)絡(luò)盲泛。例如:http://appcache-demo.s3-website-us-east-1.amazonaws.com/without-network/型酥。

  8. manifest文件本身不能被緩存,且manifest文件的更新使用的是瀏覽器緩存機(jī)制查乒。所以manifest

  9. 文件的Cache-Control緩存時(shí)間不能設(shè)置太長(zhǎng)。
    另外郁竟,根據(jù)官方文檔玛迄,AppCache已經(jīng)不推薦使用了,標(biāo)準(zhǔn)也不會(huì)再支持∨锬叮現(xiàn)在主流的瀏覽器都是還支持AppCache的蓖议,以后就不太確定了。

六讥蟆、 Indexed Database

IndexedDB也是一種數(shù)據(jù)庫(kù)的存儲(chǔ)機(jī)制勒虾,但不同于已經(jīng)不再支持的Web SQL Database。IndexedDB不是傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù)瘸彤,可歸為NoSQL數(shù)據(jù)庫(kù)修然。IndexedDB又類似于Dom Storage的key-value的存儲(chǔ)方式,但功能更強(qiáng)大,且存儲(chǔ)空間更大愕宋。

  • NoSQL數(shù)據(jù)庫(kù)
  • 類似DOM Storage 鍵值對(duì)存儲(chǔ)方式

6.1 基本使用

<script type="text/javascript">
var db;
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
//瀏覽器是否支持IndexedDB
if (window.indexedDB) {
   //打開(kāi)數(shù)據(jù)庫(kù)玻靡,如果沒(méi)有,則創(chuàng)建
   var openRequest = window.indexedDB.open("people_db", 1);
   //DB版本設(shè)置或升級(jí)時(shí)回調(diào)
   openRequest.onupgradeneeded = function(e) {
       console.log("Upgrading...");
       var thisDB = e.target.result;
       if(!thisDB.objectStoreNames.contains("people")) {
           console.log("Create Object Store: people.");
           //創(chuàng)建存儲(chǔ)對(duì)象中贝,類似于關(guān)系數(shù)據(jù)庫(kù)的表
           thisDB.createObjectStore("people", { autoIncrement:true });
          //創(chuàng)建存儲(chǔ)對(duì)象囤捻, 還創(chuàng)建索引
          //var objectStore = thisDB.createObjectStore("people",{ autoIncrement:true });
         // //first arg is name of index, second is the path (col);
        //objectStore.createIndex("name","name", {unique:false});
       //objectStore.createIndex("email","email", {unique:true});
     }
}
//DB成功打開(kāi)回調(diào)
openRequest.onsuccess = function(e) {
    console.log("Success!");
    //保存全局的數(shù)據(jù)庫(kù)對(duì)象,后面會(huì)用到
    db = e.target.result;
   //綁定按鈕點(diǎn)擊事件
    document.querySelector("#addButton").addEventListener("click", addPerson, false);
    document.querySelector("#getButton").addEventListener("click", getPerson, false);
    document.querySelector("#getAllButton").addEventListener("click", getPeople, false);
    document.querySelector("#getByName").addEventListener("click", getPeopleByNameIndex1, false);
}
  //DB打開(kāi)失敗回調(diào)
  openRequest.onerror = function(e) {
      console.log("Error");
      console.dir(e);
   }
}else{
    alert('Sorry! Your browser doesn\'t support the IndexedDB.');
}
//添加一條記錄
function addPerson(e) {
    var name = document.querySelector("#name").value;
    var email = document.querySelector("#email").value;
    console.log("About to add "+name+"/"+email);
    var transaction = db.transaction(["people"],"readwrite");
var store = transaction.objectStore("people");
   //Define a person
   var person = {
       name:name,
       email:email,
       created:new Date()
   }
   //Perform the add
   var request = store.add(person);
   //var request = store.put(person, 2);
   request.onerror = function(e) {
       console.log("Error",e.target.error.name);
       //some type of error handler
   }
   request.onsuccess = function(e) {
      console.log("Woot! Did it.");
   }
}
//通過(guò)KEY查詢記錄
function getPerson(e) {
    var key = document.querySelector("#key").value;
    if(key === "" || isNaN(key)) return;
    var transaction = db.transaction(["people"],"readonly");
    var store = transaction.objectStore("people");
    var request = store.get(Number(key));
    request.onsuccess = function(e) {
        var result = e.target.result;
        console.dir(result);
        if(result) {
           var s = "<p><h2>Key "+key+"</h2></p>";
           for(var field in result) {
               s+= field+"="+result[field]+"<br/>";
           }
           document.querySelector("#status").innerHTML = s;
         } else {
            document.querySelector("#status").innerHTML = "<h2>No match!</h2>";
         }
     }
}
//獲取所有記錄
function getPeople(e) {
    var s = "";
     db.transaction(["people"], "readonly").objectStore("people").openCursor().onsuccess = function(e) {
        var cursor = e.target.result;
        if(cursor) {
            s += "<p><h2>Key "+cursor.key+"</h2></p>";
            for(var field in cursor.value) {
                s+= field+"="+cursor.value[field]+"<br/>";
            }
            s+="</p>";
            cursor.continue();
         }
         document.querySelector("#status2").innerHTML = s;
     }
}
//通過(guò)索引查詢記錄
function getPeopleByNameIndex(e)
{
    var name = document.querySelector("#name1").value;
    var transaction = db.transaction(["people"],"readonly");
    var store = transaction.objectStore("people");
    var index = store.index("name");
    //name is some value
    var request = index.get(name);
    request.onsuccess = function(e) {
       var result = e.target.result;
       if(result) {
           var s = "<p><h2>Name "+name+"</h2><p>";
           for(var field in result) {
               s+= field+"="+result[field]+"<br/>";
           }
           s+="</p>";
    } else {
        document.querySelector("#status3").innerHTML = "<h2>No match!</h2>";
     }
   }
}
//通過(guò)索引查詢記錄
function getPeopleByNameIndex1(e)
{
    var s = "";
    var name = document.querySelector("#name1").value;
    var transaction = db.transaction(["people"],"readonly");
    var store = transaction.objectStore("people");
    var index = store.index("name");
    //name is some value
    index.openCursor().onsuccess = function(e) {
        var cursor = e.target.result;
        if(cursor) {
            s += "<p><h2>Key "+cursor.key+"</h2></p>";
            for(var field in cursor.value) {
                s+= field+"="+cursor.value[field]+"<br/>";
            }
            s+="</p>";
            cursor.continue();
         }
         document.querySelector("#status3").innerHTML = s;
     }
}
</script>
<p>添加數(shù)據(jù)<br/>
<input type="text" id="name" placeholder="Name"><br/>
<input type="email" id="email" placeholder="Email"><br/>
<button id="addButton">Add Data</button>
</p>
<p>根據(jù)Key查詢數(shù)據(jù)<br/>
<input type="text" id="key" placeholder="Key"><br/>
<button id="getButton">Get Data</button>
</p>
<div id="status" name="status"></div>
<p>獲取所有數(shù)據(jù)<br/>
<button id="getAllButton">Get EveryOne</button>
</p>
<div id="status2" name="status2"></div>
<p>根據(jù)索引:Name查詢數(shù)據(jù)<br/>
    <input type="text" id="name1" placeholder="Name"><br/>
    <button id="getByName">Get ByName</button>
</p>
<div id="status3" name="status3"></div><br>

6.2 分析

分析:IndexedDB是一種靈活且功能強(qiáng)大的數(shù)據(jù)存儲(chǔ)機(jī)制邻寿,它集合了Dom Storage和Web SQL Database的優(yōu)點(diǎn)蝎土,用于存儲(chǔ)大塊或復(fù)雜結(jié)構(gòu)的數(shù)據(jù),提供更大的存儲(chǔ)空間绣否,使用起來(lái)也比較簡(jiǎn)單誊涯。可以作為Web SQL Database的替代枝秤。不太適合靜態(tài)文件的緩存醋拧。

  1. 以key-value 的方式存取對(duì)象,可以是任何類型值或?qū)ο蟮淼ǘM(jìn)制丹壕。

  2. 可以對(duì)對(duì)象任何屬性生成索引,方便查詢薇溃。

  3. 較大的存儲(chǔ)空間菌赖,默認(rèn)推薦250MB(分Host),比Dom Storage的5MB要大得多沐序。

  4. 通過(guò)數(shù)據(jù)庫(kù)的事務(wù)(tranction)機(jī)制進(jìn)行數(shù)據(jù)操作琉用,保證數(shù)據(jù)一致性。

  5. 異步的 API 調(diào)用策幼,避免造成等待而影響體驗(yàn)邑时。

七、File System API

  • HTML5新加入的存儲(chǔ)機(jī)制
  • 為 webApp提供了虛擬的文件系統(tǒng)特姐,類似Native App訪問(wèn)本地系統(tǒng)一樣

7.1 File System 的優(yōu)勢(shì)

  • 滿足大塊的二進(jìn)制文件存儲(chǔ)
  • 通過(guò)預(yù)加載資源文件提供性能
  • 可以直接編輯文件

7.2 兩種文件空間

  • 臨時(shí)型

由瀏覽器自動(dòng)分配的晶丘,但可能被瀏覽器收回

  • 持久性

需要顯示的申請(qǐng),申請(qǐng)時(shí)瀏覽器會(huì)給用戶提示唐含,讓用戶確定浅浮。瀏覽器不會(huì)收回(提供用戶則不太友好),大小不夠用需要再次申請(qǐng)捷枯。

7.3 分析

File System API給Web App帶來(lái)了文件系統(tǒng)的功能滚秩,Native文件系統(tǒng)的功能在Web App中都有相應(yīng)的實(shí)現(xiàn)。任何需要通過(guò)文件來(lái)管理數(shù)據(jù)淮捆,或通過(guò)文件系統(tǒng)進(jìn)行數(shù)據(jù)管理的場(chǎng)景都比較適合郁油。

八本股、參考文章

  1. 《HTML5緩存機(jī)制淺析:移動(dòng)端Web加載性能優(yōu)化》
  2. 《騰訊開(kāi)發(fā)工程師:前端異常監(jiān)控到底怎么做》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市已艰,隨后出現(xiàn)的幾起案子痊末,更是在濱河造成了極大的恐慌,老刑警劉巖哩掺,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凿叠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嚼吞,警方通過(guò)查閱死者的電腦和手機(jī)盒件,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)舱禽,“玉大人炒刁,你說(shuō)我怎么就攤上這事√苤桑” “怎么了翔始?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)里伯。 經(jīng)常有香客問(wèn)我城瞎,道長(zhǎng),這世上最難降的妖魔是什么疾瓮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任脖镀,我火速辦了婚禮,結(jié)果婚禮上狼电,老公的妹妹穿的比我還像新娘蜒灰。我一直安慰自己,他們只是感情好肩碟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布强窖。 她就那樣靜靜地躺著,像睡著了一般削祈。 火紅的嫁衣襯著肌膚如雪翅溺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天岩瘦,我揣著相機(jī)與錄音,去河邊找鬼窿撬。 笑死启昧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的劈伴。 我是一名探鬼主播密末,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼握爷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了严里?” 一聲冷哼從身側(cè)響起新啼,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刹碾,沒(méi)想到半個(gè)月后燥撞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迷帜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年物舒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戏锹。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冠胯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锦针,到底是詐尸還是另有隱情荠察,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布奈搜,位于F島的核電站悉盆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏媚污。R本人自食惡果不足惜舀瓢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耗美。 院中可真熱鬧京髓,春花似錦、人聲如沸商架。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛇摸。三九已至备图,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赶袄,已是汗流浹背揽涮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饿肺,地道東北人蒋困。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像敬辣,于是被迫代替她去往敵國(guó)和親雪标。 傳聞我的和親對(duì)象是個(gè)殘疾皇子零院,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 轉(zhuǎn)載:H5緩存機(jī)制淺析-移動(dòng)端Web加載性能優(yōu)化【干貨】 作者:賀輝超,騰訊游戲平臺(tái)與社區(qū)產(chǎn)品部 高級(jí)工程師 目錄...
    meng_philip123閱讀 11,483評(píng)論 6 48
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理村刨,服務(wù)發(fā)現(xiàn)告抄,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 一嵌牺、前言 工作上遇到一個(gè)這樣的需求打洼,一個(gè)H5頁(yè)面在APP端,如果勾選已讀狀態(tài)髓梅,則下次打開(kāi)該鏈接拟蜻,會(huì)跳過(guò)此頁(yè)面。用到...
    eraser123閱讀 992評(píng)論 2 47
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評(píng)論 25 707
  • 上一章 十 他說(shuō)一會(huì)枯饿,歇一陣酝锅,將那日在李家祠堂中發(fā)生之事又講述一遍,直講到眾人散去奢方,沉吟一會(huì)兒才接道搔扁,“我們見(jiàn)天色...
    德萬(wàn)托阿閱讀 668評(píng)論 0 4