靜態代碼工具,實現高質量代碼編寫

前言

giphy-downsized.gif

對于少量的代碼,我們通常能夠通過肉眼或者代碼編輯器,甚至控制臺出現的錯誤,找出對應有問題的代碼行,可以進行排錯,但是若是對于大量的代碼,光靠肉眼的識別,少則幾百行,多則上千行的代碼,光靠眼力勁來察覺bug,整個工作量是非常大的,也很難預測,特別是某些庫或者產品的迭代開發時

隨著代碼越來越龐大,根本不適合所謂的眼力勁來找出代碼中潛在的錯誤,那么這時候在運行代碼之前,使用代碼分析工具進行檢測,那就非常友好了,靜態分析工具能夠發現代碼中常見的出錯因素和陷阱并進行提示,從而可以避免有bug的代碼被執行

使用該工具可以發現代碼中語句結束時是否缺分號,使用了未定義的變量,以及語法錯誤,更為重要一點是,如果你使用了javascript中eval方法將字符串轉換js代碼來執行,他也會對代碼進行安全提示的檢查,只要確定這些有潛在風險的代碼操作,沒有在你的源碼中出現,就能夠減少程序中Bug

自己寫的代碼也更加有信心,而在非正規軍的團隊開發中,也許本身就沒有靜態代碼分析這一環節,很多時候,絕大部分都是以業務為導向的,無論你代碼寫得有多么low,只要能跑起來,實現了就行,對于代碼的質量和潛在的問題并沒有過多的在意,自己挖的坑,讓別人來填補

這是平時開發當中常有的事,使用這些工具本身就是非硬性的,因人而異,但是我覺得一個強悍的團隊的leader,保證各個小伙伴寫出更規范,更高效的代碼,使用這些代碼軟技能無論是對人對己,還是對產品都是很有利的,無論別人是否要求,為了自身優質代碼的輸出

習以為常的應用這些代碼軟技能也是自我很好的提升,本篇并不涉及什么高大上的內容,可歸為工具篇,一起來跟大家分享一下使用靜態代碼分析工具,實現高質量代碼的編寫使用心得,讓代碼寫得放心,遠離臭蟲

JSLint

  • 能夠代碼發布之前對代碼進行檢查校驗,發現代碼中那些已知的錯誤(語法檢測,規范,潛在的風險檢查),確保項目的遵循的代碼規范
  • 在線工具地止JSLint

在線測試代碼

在線使用jslint進行代碼檢查.gif
  • 使用方法:直接拷貝js代碼到檢測框中,然后點擊JSlint,便可得出結果,但是你卻發現,看到紅色的錯誤提示框,簡直不能忍,JSlint嚴格的對代碼的格式做出了檢查,空格,符號之類的統一的進行了檢測
  • 代碼錯誤如下所示
    • 意外的空格(函數后面)
    • 使用未定義的類對象
    • 沒有用嚴格的"use strict"語句來強制來使用
    • 使用空格,而不是制表符
    • 變量名以下劃線
    • alert方法未定義就被調用(之所以被標識未錯誤,是因為alert不是官方js語言規范的一部分,它是瀏覽器添加一個功能調試彈出框信息而已)
  • 針對JsLint復選框進行選擇,通過這些復選框針對自己的代碼,對默認的一系列規則進行修改,比如標識符開頭的下劃線以及空格,this,Es6等勾選了,就相當于設置了true,設置完成后代碼的錯誤就變少了,如下gif所示


    JsLint選中options下的復選框后設置為true后錯誤減少了.gif
  • 可以在代碼中對不同的文件設置不同的規則,方法是將這些指令放在文件的頂部,以/*jslint開頭,后面跟著一系列的選項,JSlint在分析js代碼時會應用這些選項,選項后面可以是true,也可以是false,指明該選項是否啟用,多個選項之間用逗號隔開(也可以勾選瀏覽器下方的選項,默認為false,勾選狀態為true),照錯誤提示:在代碼中添加"use strict" 增加嚴格模式 ,示例代碼如下所示:
/*jslint devel:true,nomen:true,white:true*/
/*global Class:false*/
var Accomodation  = Class.create((function(){
      "use strict" // 使用嚴格模式,在這個函數作用范圍內都會生效
      var _isLocked = true,
          publicPropertiesAndMethods = { 
              lock:function(){
                 _isLocked = true;
              },
              unlock:function(){
                 _isLocked = false;
              },
              getIsLocked:function(){
                 return _isLocked;
              },
              initialize:function(){
                 this.unlock();
              }
         };
        return publicPropertiesAndMethods; 
}()));
var House = Accomodation.extend({
      isAlarmed:false,
      alarm:function(){
          this.isAlarmed = true;
          alert("Alarm activated");
      },
      lock:function(){
          Accomodation.prototype.lock.call(this);
          this.alarm();
      }
});

缺點:

  • 因為比較嚴格,對于一些無關痛癢的問題也會視作為bug,用jslint來檢查代碼并不是很方便,會增加工作量,也會對自己代碼產生疑問
  • 缺乏文檔記錄規則
  • 有限的配置選項,許多規則不能禁掉

這只是線上進行檢查,對于非線上的,針對Node.js應用框架有JSlint工具,可以通過終端命令行的方式來運行jsLint版本,使用方法如下:

  • 下載安裝nodejs(針對電腦系統位數下載對應的版本,分32為和64位,還有mac版本的)
  • 安裝JSlint到機器當中去,npm install jslint -g全局進行安裝
  • 切換到對應檢查的源文件下執行jslint XXX.js或者jslint *.js進行檢查
    使用nodeJsLintCheck在nodejs壞境終端上進行檢查.gif

JSHint

  • 同上,代碼分析工具,可對代碼進行檢查校驗,但是沒有jslint嚴格,主要是針對語法錯誤,而不是內容格式,以及那些可能使代碼不能正確運行的錯誤,而不應該因為某些格式上的問題,使代碼不通過檢查
  • 不同點:忽略function關鍵字后面的空格以及雙大括號之間的空格
  • 對于沒有用到的變量,會產生一個錯誤,也可以設置參數配置進行過濾該錯誤,配置與jslint相似,如下所示:/*jshint devel:true,unused:false*/,完整的選項列表很長,可以通過在線文檔的方式來獲取,如下網站所示:jsHint
    jsHint.png
  • 使用Node.js壞境下檢測一樣,需在nodejs的環境下,用npm安裝jsHint,然后在dos命令行下執行jshint xxx.js或者jshint *.js(對所有的js進行檢查)如下操作所示
    jsHint.gif

Eslint

  • 同上,代碼校驗工具檢查
  • ESLint不僅可以進行代碼風格的檢驗,而且可以檢查代碼中的bug和其他問題
  • 對于ES6,ESLint也是明智的選擇,在上面提到jslint,jsHint工具中,ESLint對ES6支持的最為友好廣泛
  • 中文文檔[Eslint](http://eslint.cn/docs/user-guide/configuring “Eslint中文文檔”)
  • 英文文檔Eslint
    優點:
  • 不依賴于具體的編碼風格
  • 內置規則和自定義規則共用一套規則 API
  • 每條規則,各自獨立,可以開啟或關閉,可以將結果設置成警告或者錯誤
  • ESLint 并不推薦任何編碼風格,規則都是自由的
  • 通過豐富文檔減少溝通成本,盡可能的簡單透明,測試的重要性

使用方法:

  • 在線使用,復制粘貼代碼到檢測框中,可對代碼進行檢查,使用如下gif所示


    在線使用Eslint.gif
  • 推薦使用命令行工具使用Eslint,安裝Nodejs,安裝Eslint
    *安裝eslint npm i -g eslint
    使用命令行方式安裝Eslint.gif
  • 在用eslint對源代碼進行檢查之前,先要初始化eslint --init初始化,按照提示,支持Es6,commonJS,React等選擇,執行完后,執行eslint xxx.js 可在終端上查看到具體的錯誤提示
  • 具體的每個命令配置參數,可結合官方文檔進行逐一查看


    Eslint.gif

    如上為eslint 檢測示例代碼所示

小結

JSlint,jsHint,ESlint比較

  • 優先選擇Eslint,文檔齊全(中英文都有),況且很多自動化構建工具webpack等都集成了Eslint等代碼檢查工具,令人麻煩一點就是配置參數,設置狀態,具體得還是得緊跟官方文檔,只要耐下心來,通讀一遍,若有不懂,Google,stackoverflow,github,博客園,百度等關鍵字進行搜索嘛,總有滿意的答案,jsHint是第二選擇,它對代碼的語法進行檢查,忽略代碼格式,最后考慮使用jslint,不是說它不好,只是覺得太過于嚴格,眉毛胡子一把抓的感覺,對一些代碼格式也強行限制了,若想要去除一些非語法錯誤,還得自行配置

javascript中的單元測試

上面的靜態代碼分析工具能夠檢測出代碼中存在的錯誤,如果想要更進一步的對代碼進行分析,那么就需要進行單元測試了,把代碼中每個函數都寫成一個具有獨立的功能單元,這個單元有輸入和輸出,并執行一個 明確,有文檔說明的操作,那么測試也就是這個javascript函數,它要做的是用不同的輸入依次執行你寫的每個函數,然后檢查函數的輸出是否符合預期值
如下示例代碼

var add  = function(){
    var total = 0,
    len = arguments.length;                    
    for(var index = 0;index<len;index++){
         total+= arguments[index];
    }
    return total;
}
console.log(add(1,2,3,4,5));  //15

控制臺輸出結果


一個簡單的測試函數.png

針對函數的設計單元測試要用不同的輸入來執行這個函數,比如說空值,非number數值等進行測試這樣能夠保證該函數的行為符合預期,而且對于不同的輸入組合能夠輸出正確結果,在有些時候,當我們想要得到某些特殊值的時候,我們還會進行判斷,對得出結果進行過濾,篩選,通過對代碼中的每個函數進行嚴格的測試,能夠確保代碼少出問題
如下代碼對結果進行條件判斷:

var add  = function(){
    var total = 0,
    len = arguments.length;
    for(var index = 0;index<len;index++){
        total+= arguments[index];
    }
    if(isNaN(total)){
        console.log("請輸入正確的number值");
    }else{
       return total;
    }
    /*
    if(typeof total === "number"){
        return total;
    }else{
        console.log("請輸入正確的number值");
    }
    */  
}
console.log(add("asdfs"));  // 請輸入正確的number值

控制臺輸出測試輸出結果


一個簡單的測試函數結果判斷.png

javascript單元測試框架

可以手動編寫代碼挨個的進行單元測試,但是很多時候,已經有現成的測試框架供我們使用

jasmine(茉莉花)使用

上述的框架有興趣的話,可以依次的進行測試學習的,至于單元測試的框架很多,我覺得你可以通過star數來自行判斷,選擇最多的那個,也是最流行的那個,使用的人多,文檔也是最多的,工作方式都比較類似,都需要三個文件

  • 首先包含框架本身代碼的javascript庫文件,這個庫文件需要在HTML頁面中使用,只要在這個HTML頁面中引用這個框架庫文件即可
  • 另外還需要一個包含一個測試的Javascript代碼的文件,以及包含單元測試的文件,當用瀏覽器打開這個HTML頁面時,這些單元測試就會被自動執行,并將詳細的執行結果輸出,包括通過的測試和失敗的測試,都會顯示在屏幕上
    官方文檔
    GETSTARTED-->JASMINE FOR NODE.JS-->MORE INFORMATION
    jasmine.gif

jasmine單元測試

  • 將針對某個函數或者方法的一系列單元測試組織到一個組中,然后不同的單元測試組又可以被進一步組織在一塊,這樣彼此相關的代碼就可以一起測試,這樣一組單元測試稱為一個測試套件(text suite),而單獨的測試被稱為規格(spec)
  • 一般來說代碼庫中的每個文件都會有一個與之對應的測試套件文件,這些測試文件可以組織到一個單獨的文件夾中,一般這個文件夾命名為spec
  • 測試套件與相關代碼文件相同的文件名,并在測試套件文件名后面加上-spec來區別,表示是測試套文件,比如相關代碼文件為xxx.js,那么與之對應的測試文件可以命名為xxx-spec.js


    jasmine單元測試框架.gif

    示例代碼測試套件文件如下所示

describe("The add function", function() {
    it("Adds 1 + 2 + 3 together correctly" , function() {
        var output = add(1, 2, 3);
        expect(output).toEqual(6);
    });

    it("Adds negative numbers together correctly", function() {
        var output = add(-1, -2, -3);
        expect(output).toEqual(-6);
    });
});

上面的示例代碼中使用jasmine的describle()函數,并通過對該函數將針對add()函數的所有單元測試幾種在一個組中,describle()函,第一個參數為描述the add function,用來對這一組測試進行描述,第二個參數為回調函數,每個測試被包裝在一個單獨的匿名函數當中,每個單元測試都是用jasmine的it方法來定義的,其中同樣第一個參數包含一個字符串來對單元測試進行描述,該測試函數還包含了對add()函數的一次調用,最后用jasmine的export()函數對返回值進行檢查,expect函數后面跟著toEqual()函數的鏈式調用,這個函數就是一個匹配器,匹配器是一個jasmine函數,能夠檢查被測試函數的輸出,并與預期的結果進行比較,看兩者是否相匹配
測試代碼如下所示

<!DOCTYPE>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner</title>
  <link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
  <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>

  <!-- include source files here... -->
  <!-- <script type="text/javascript" src="src/Player.js"></script>
  <script type="text/javascript" src="src/Song.js"></script> -->
  <script type="text/javascript" src="spec/add.js"></script>
  <script type="text/javascript" src="spec/add-spec.js"></script>

  <!-- include spec files here... -->
  <!-- <script type="text/javascript" src="spec/SpecHelper.js"></script>
  <script type="text/javascript" src="spec/PlayerSpec.js"></script> -->
 

  <script type="text/javascript">
    (function() {
      var jasmineEnv = jasmine.getEnv();
      jasmineEnv.updateInterval = 1000;

      var htmlReporter = new jasmine.HtmlReporter();

      jasmineEnv.addReporter(htmlReporter);

      jasmineEnv.specFilter = function(spec) {
        return htmlReporter.specFilter(spec);
      };

      var currentWindowOnload = window.onload;

      window.onload = function() {
        if (currentWindowOnload) {
          currentWindowOnload();
        }
        execJasmine();
      };

      function execJasmine() {
        jasmineEnv.execute();
      }

    })();
  </script>
</head>
<body>
</body>
</html>

這里使用的是jasmine的1.3.1版本的,按照如上gif操作找到對應的版本進行下載即可,找到一個SpecRunner.html的示例文件,直接打開即可,把你自己要測試的源碼xxx.js文件和測試套件放在spec目錄里面,在html頁面中直接引入即可,在瀏覽器中打開html文件即可,會自動的運行這些單元測試,然后再瀏覽器上輸出結果,從瀏覽器的結果中顯示出可以看出,對于給定的兩個單元測試結果都符合預期,瀏覽器中輸出結果為綠色的,表示的是正確的


jasmine測試.gif
  • 針對如上代碼add()函數進行單元測試
  • 針對add()函數輸入空值的情況,還有非空值的情況,對于前者,希望在沒有輸入的時候輸出為空,對于后者,希望將輸入中的數值輸入進行加和,并且忽略輸入中的非數值部分
    示例代碼如下所示
describe("The add function", function() {
    // 第一個單元測試
    it("Adds 1 + 2 + 3 together correctly", function() {
        var output = add(1, 2, 3);
        expect(output).toEqual(6);
    });
    // 第二個單元測試
    it("Adds negative numbers together correctly", function() {
        var output = add(-1, -2, -3);
        expect(output).toEqual(-6);
    });
    // 第三個單元測試
    it("Returns 0 if no inputs are provided", function() {
        var output = add();
        expect(output).toEqual(0);
    });
    // 第四個單元測試
    it("Adds only numeric inputs together", function() {
        var output = add(1, "1", 2, "2", 3, "3");
        expect(output).toEqual(6);
    });
});

將上面的代碼單獨為一個js腳本文件引入到SpecRunner.html中去,在瀏覽器打開


針對代碼add()函數測試.gif

對于前三個單元測試給出的條件與結果符合,測試成功,而最后一個單元測試不通過,瀏覽器標識為紅色,從瀏覽上jasmine版本下方的四個圓點點,其實就是對應的每個單元測試的結果,如果為綠色,那么表示成功,反之失敗,會用紅色的X表示,關于失敗的具體原因,點擊失敗處的鏈接會給出提示,我們預期單元測試的結果函數輸出應該是6但是實際輸卻為112233,其實它輸出是一個字符串(其實不測試也很好理解一個數字加上一個字符串肯定是字符串,在沒有對數值進行轉換類型的話,肯定是字符串)

小結
上述例子很簡單,其實不用單元測試,自己也能夠進行判斷,只是為了作為單元測試演示jasmine是怎樣進行單元測試的,單元測試暴露出代碼中存在的一些潛在的問題,這些問題可能導致在真實網頁中運行時發生錯誤,所以我們要回到自己寫的源代碼中進行修正,加條件判斷進行過濾篩選,檢查輸入的參數是否為數字,如果是的話,那么輸出正確的值,不是的話,給出一個友好的用戶提示,至于一些條件則具體根據實際情況的業務走,當然也這其中也包括編碼經驗,總之,使用單元測試能夠很好的保證代碼的健壯性,在復雜的業務的邏輯處理中,使用單元測試還是很有必要的,使用單元測試的過程其實就是排坑掃雷的過程,如上上代碼條件代碼所示
注意

  • 自己編寫的單元測試應該針對事自己所編寫的代碼,而不應該是第三方提供的庫,比如現成的jQuery庫,對于類似這種第三方庫,插件等不需要你來對它進行編寫單元測試,這個單元測試基本上創建該庫的作者來負責的(當然你想把它拿來進行單元測試,也是可以的,分析某些庫的源碼時還是很有用的)
  • 代碼在網頁中運行時會遇到各種不同的輸入,雖然單元測試覆蓋了很多情況,但還是可能遇到新的錯誤情形,并會出現新的錯誤輸出,如果遇到這種情況,不要急于修改代碼問題,而應該創建一個單元進行測試,而且這個單元測試所使用的輸入數據正是那些使得網頁發生錯誤的輸入,這么做的是為了問題的重現,創建完單元測試在來修改我們的代碼,直到包括新添加的測試在內的所有測試都通過為止,我們應該把這個心的單元測試保留在測試代碼中,這樣才能保證將來函數代碼發生變化時能夠再次捕獲到這個錯誤

兩款棒棒的在線測試服務

基于虛擬機的在線測試服務,通過這些服務可以同時在不同的web瀏覽器和操作系統上自動的運行單元測試,并獲取測試結果,這些服務是對手動運行測試的一種替代,都是免費開源的

jasmine框架中其他的匹配器

  • toEqual():給定一系列的輸入后,會產生一個明確的數值的輸出結果
  • expect(out).not.toEqual(6):測試一個函數調用的結果是不是與某個特定值正好相反,可以將屬性not和另外一個匹配器(toEqual())組合使用
  • expect(output).toMatch(/[a-s]/);
    *expect(output).not.toMatch(/[t-z]/);
  • expect(output).toBe(true):測試一個值是否為真
  • expect(output).toBe(null):測試一個值是否為nul空
  • expect(output).toBeTruthy():
  • expect(output).not.toBeNull();
  • expect(output).toBeFalsy();
  • expect(output).toBeDefined();測試單元測試輸出值是否為undefined
  • expect(output).not.toBeDefined();測試單元測試輸出值不是undefined
  • expect(output).toContain("川川"):如果函數返回的是一個數組,要判斷數組中是否包含一個特定的值,用toContain()匹配器,這里判斷是不是包含川川
  • expect(output).not.toContain("美美");與上面的相反,不包含美美這一特定值
  • export(output).toBeGraterThan(3);如果哈數包含一些數學操作而且這些操作對于同樣的輸入并不總是輸出相同的結果,要測試這些函數,可以使用toBeGraterThan()或者toBeLessThan()匹配器
  • export(output).not.toBeLessThan(5);
  • expect(3.1425).toBeCloseTo(3.14,2); // true,toBecloseTo()是一個數學匹配器,利用這個匹配器可以判斷函數的符點值,輸出是否和某個期望的值相近,匹配器的第二個參數是指兩者比較時精確到小數點后第幾位,這里是2,則保留的是2位
  • expect(3.1425).toBecloseTo(3,0); // true
    *expect(output).toThrow();如果函數在遇到無效的輸入參數時會拋出異常,那么就可以使用toThrow()匹配器來對此進行測試
  • expect(output).not.ToThrow();

至于更多的jasmine匹配器可以參考官方文檔,當然自己也是可以自定義編寫定制版的匹配函數的

總結:
本篇從剛開始介紹了在代碼提交前三種校驗工具,分別是jslint,jshint,Eslint主要是用于發現代碼中那些已知的錯誤(語法檢測,規范,潛在的風險檢查),確保項目遵循的代碼規范,其中jslnt比較嚴格

連代碼格式什么空格也不放過,缺點也很明顯,文檔記錄規則不全,而jsHint相對比較好,不拘于代碼風格格式等問題,較為自由,文檔說明都很全,個人也是比較推薦這種,而Eslint更是前兩者的升級版,最為強大,功能齊全,中英文檔也齊全,對Es6``nodejs等支持度高,唯一可能覺得麻煩的就是配置比較唬人

如果使用過一些前端自動化構建工具的話,那么會比較熟悉,那些webpack等打包工具其實內部也集成了這些代碼校驗工具,或者也可以單獨在代碼編輯器(比如sublime)中安裝這些代碼校驗的工具,具體三種校驗工具的簡單使用可見上文,緊接著就是大篇幅的進行 javascript中的單元測試,介紹了Qunit,Mocha,jasmine,并且著重的介紹了如何使用jasmine框架進行單元的測試

自己編寫的源碼獨立文件,與要對它進行測試單元的文件,放入到SpecRunner.html文件當中,依賴jasmine庫對應版本的css和js,在瀏覽器上打開SpecRunner.html文件就能看到對應測試單元的結果,具體一些細節操作見上面,又介紹了兩款比較好的在線測試服務的工具,Browserstack,Sauce Labs,以及介紹了jasmine框架中其他的匹配器的使用

其實這種代碼檢測工具很多,之前呢,我對于單元測試非常不解,覺得很是神秘,遙不可及,單元測試更多的是后端小伙伴進行業務邏輯處理測試就很常見了的,而現在前端的業務也越來越復雜,同樣也存在在著復雜的交互邏輯處理,我覺得同樣也是需要單元測試的

可能平時更多是在實現UI層比較多,對于這種頻繁使用單元測試確實很少用,但是習慣使用代碼檢驗工具,和單元測試無疑是一個好的習慣,能夠保證生產優質的代碼,這也是保證產品的迭代開發質量,為什么bug修的沒完沒了,我想,這也是有一定的原因的,其實,這些工具的軟技能,細心的小伙伴發現,在一些招聘需求上,也是屢見不鮮的,也是自我進階的一部分

以下是本篇提點概要

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容