虛擬DOM

真是DOM 的缺陷:

  • js 操縱Dom 會 影響到整個渲染流水線
  • 我們可以調用document.body.appendChild(node)往 body 節(jié)點上添加一個元素缸浦,調用該 API 之后會引發(fā)一系列的連鎖反應朵栖。
    1蚕泽、首先渲染引擎會將 node 節(jié)點添加到 body 節(jié)點之上嫌佑,
    2冒冬、然后觸發(fā)樣式計算翘簇、布局、繪制维蒙、柵格化镰绎、合成等任務,我們把這一過程稱為重排木西。
    3、除了重排之外随静,還有可能引起重繪或者合成操作八千,形象地理解就是“牽一發(fā)而動全身”

虛擬dom

虛擬dom需要解決的事情:
1燎猛、將頁面改變的內容應用到虛擬 DOM 上恋捆,而不是直接應用到 DOM 上
2、變化被應用到虛擬 DOM 上時重绷,虛擬 DOM 并不急著去渲染頁面沸停,而僅僅是調整虛擬 DOM 的內部狀態(tài),這樣操作虛擬 DOM 的代價就變得非常輕了
3昭卓、在虛擬 DOM 收集到足夠的改變時愤钾,再把這些變化一次性應用到真實的 DOM 上

Virtual Dom 的優(yōu)勢在哪里?
一般面試官都會問到Virtual Dom 的優(yōu)勢

面試官不是想聽到 [直接操作/頻繁操作 DOM 的性能差」候醒,如果 DOM 操作的性能如此不堪能颁,那么 jQuery 也不至于活到今天。所以面試官更想聽到 VDOM 想解決的問題以及為什么頻繁的 DOM 操作會性能差倒淫。

DOM 引擎伙菊、JS 引擎 相互獨立,但又工作在同一線程(主線程)
JS 代碼調用 DOM API 必須 掛起 JS 引擎、轉換傳入參數數據镜硕、激活 DOM 引擎运翼,DOM 重繪后再轉換可能有的返回值,最后激活 JS 引擎并繼續(xù)執(zhí)行若有頻繁的 DOM API 調用兴枯,且瀏覽器廠商不做“批量處理”優(yōu)化血淌,
引擎間切換的單位代價將迅速積累若其中有強制重繪的 DOM API 調用,重新計算布局念恍、重新繪制圖像會引起更大的性能消耗六剥。

  • 虛擬 DOM 不會立馬進行排版與重繪操作
  • 虛擬 DOM 進行頻繁修改,然后一次性比較并修改真實 DOM 中需要改的部分峰伙,最后在真實 DOM 中進行排版與重繪疗疟,減少過多DOM節(jié)點排版與重繪損耗
  • 虛擬 DOM 有效降低大面積真實 DOM 的重繪與排版,因為最終與真實 DOM 比較差異瞳氓,可以只渲染局部

除了上面說的帶來的優(yōu)勢策彤,但并不是全部。虛擬 DOM 最大的優(yōu)勢在于抽象了原本的渲染過程匣摘,實現了跨平臺的能力店诗,而不僅僅局限于瀏覽器的 DOM,可以是安卓和 IOS 的原生組件音榜,可以是近期很火熱的小程序庞瘸,也可以是各種 GUI

直接操作 DOM 的性能并不會低于虛擬 DOM 和 Diff 算法,甚至還會優(yōu)于赠叼。

原因是因為:
比較是為了找出不同從而有的放矢的更新頁面擦囊。但是比較也是要消耗性能的。而直接操作 DOM 就是有的放矢嘴办,我們知道該更新什么不該更新什么瞬场,所以不需要有比較的過程。所以直接操作 DOM 效率可能更高

React 厲害的地方并不是說它比 DOM 快涧郊,而是說不管你數據怎么變化贯被,我都可以以最小的代價來進行更新 DOM。 方法就是我在內存里面用新的數據刷新一個虛擬 DOM 樹妆艘,然后新舊 DOM 進行比較彤灶,找出差異,再更新到 DOM 樹上

React 的出現双仍,將命令式變成了聲明式枢希,摒棄了直接操作 DOM 的細節(jié),只關注數據的變動朱沃,DOM 操作由框架來完成苞轿,從而大幅度提升了代碼的可讀性和可維護性 茅诱。
框架的意義在于為你掩蓋底層的 DOM 操作

虛擬DOM的作用:

1、Virtual DOM 在犧牲(犧牲很關鍵)部分性能的前提下搬卒,增加了可維護性瑟俭,這也是很多框架的通性。

2契邀、實現了對 DOM 的集中化操作摆寄,在數據改變時先對虛擬 DOM 進行修改,再反映到真實的 DOM 中坯门,用最小的代價來更新 DOM

3微饥、打開了函數式 UI 編程的大門

4、可以渲染到 DOM 以外的端古戴,使得框架跨平臺欠橘,比如 ReactNative,React VR 等现恼。

5肃续、組件的高度抽象化。

Vue 2.0 引入 vdom 的主要原因是 vdom 把渲染過程抽象化了叉袍,從而使得組件的抽象能力也得到提升始锚,并且可以適配 DOM 以外的渲染目標。來自尤大文章:Vue 的理念問題[6]

虛擬DOM 的缺點:

1喳逛、首次渲染大量 DOM 時瞧捌,由于多了一層虛擬 DOM 的計算,會比 innerHTML 插入慢

2润文、虛擬 DOM 需要在內存中的維護一份 DOM 的副本(更上面一條其實也差不多察郁,上面一條是從速度上,這條是空間上)

3转唉、如果虛擬 DOM 大量更改,這是合適的稳捆。但是單一的赠法,頻繁的更新的話,虛擬 DOM 將會花費更多的時間處理計算的工作乔夯。所以砖织,如果你有一個 DOM 節(jié)點相對較少頁面,用虛擬 DOM末荐,它實際上有可能會更慢侧纯。但對于大多數單頁面應用,這應該都會更快甲脏。

react結合虛擬DOM:

cf2089ad62af94881757c2f2de277890.png

  • 創(chuàng)建階段:首先依據 JSX 和基礎數據創(chuàng)建出來虛擬 DOM眶熬,它反映了真實的 DOM 樹的結構妹笆。然后由虛擬 DOM 樹創(chuàng)建出真實 DOM 樹,真實的 DOM 樹生成完后娜氏,再觸發(fā)渲染流水線往屏幕輸出頁面

  • 更新階段:如果數據發(fā)生了改變拳缠,那么就需要根據新的數據創(chuàng)建一個新的虛擬 DOM 樹;然后 React 比較兩個樹贸弥,找出變化的地方窟坐,并把變化的地方一次性更新到真實的 DOM 樹上;最后渲染引擎更新渲染流水線绵疲,并生成新的頁面哲鸳。

為什么要使用虛擬DOM?

  • 前面 說過每次更新一次dom 都會走一次渲染流程盔憨,對瀏覽器性能 有很大的影響徙菠,可能會造成瀏覽器卡頓的現象;
WechatIMG534.png

瀏覽器的引擎工作流程都差不多般渡,如上圖大致分5步:創(chuàng)建DOM tree –> 創(chuàng)建Style Rules -> 構建Render tree -> 布局Layout –> 繪制Painting

  • 1懒豹、如果用傳統(tǒng)的 api 或者jq 操作 DOM 樹,瀏覽器會從構建DOM樹開始從頭到尾執(zhí)行一遍流程驯用。
    比如:當你一次操作需要更新 10個dom 節(jié)點理想的狀態(tài)是一次性更新完成脸秽,但是瀏覽器接受第一個更新的請求之后,不知道后面還有蝴乔,就會馬上執(zhí)行流程记餐。
    緊接著下一次更新 又要重新來一遍,前面的計算也是無用的薇正。
    頻繁操作還是會出現頁面卡頓片酝,影響用戶的體驗。
    虛擬dom 會把10次更新的diff 保存到內存的js 對象中挖腰,最后一次性 attach 到dom 樹上雕沿。

  • 2、具有更強的表達能力猴仑,如生命周期和更新時機审轮。

  • 3、執(zhí)行效率高辽俗,便于diff

React Dom diff:

傳統(tǒng)diff:

  • 在傳統(tǒng)的diff算法下疾渣,對比前后兩個節(jié)點,如果發(fā)現節(jié)點改變了崖飘,會繼續(xù)去比較節(jié)點的子節(jié)點榴捡,一層一層去對比。就這樣循環(huán)遞歸去進行對比朱浴,復雜度就達到了o(n3)吊圾,n是樹的節(jié)點數

傳統(tǒng)的diff 通過對 循環(huán)遞歸 對節(jié)點對比达椰,效率比較低。算法復雜度達到 O(n^3)街夭。如果 React 只是單純的引入 diff 算法而沒有任何的優(yōu)化改進砰碴,那么其效率是遠遠無法滿足前端渲染所要求的性能,那么react 是怎么做的呢板丽?

react diff:
React 通過制定大膽的策略呈枉,將 O(n^3) 復雜度的問題轉換成 O(n) 復雜度的問題。

react diff 策略:
策略一(tree diff):Web UI 中 DOM 節(jié)點跨層級的移動操作特別少埃碱,可以忽略不計 (DOM結構發(fā)生改變-----直接卸載并重新creat)猖辫。
策略二(component diff):擁有相同類的兩個組件將會生成相似的樹形結構,擁有不同類的兩個組件將會生成不同的樹形結構砚殿。
策略三(element diff):對于同一層級的一組子節(jié)點啃憎,它們可以通過唯一 id 進行區(qū)分。

React 分別對 tree diff似炎、component diff 以及 element diff 進行算法優(yōu)化

  • 1米苹、tree diff:

對樹進行分層比較扮匠,兩棵樹只會對同一層次的節(jié)點進行比較池颈。
React 只會對同一層的節(jié)點作比較落萎,不會跨層級比較

2791851910-5becd605889ec_articlex.png

如果出現了 DOM 節(jié)點跨層級的移動操作,React diff 會有怎樣的表現呢仆嗦?

d712a73769688afe1ef1a055391d99ed_r.jpg

A 節(jié)點(包括其子節(jié)點)整個被移動到 D 節(jié)點下辉阶,由于 React 只會簡單的考慮同層級節(jié)點的位置變換,而對于不同層級的節(jié)點瘩扼,只有創(chuàng)建和刪除操作谆甜。

當根節(jié)點發(fā)現子節(jié)點中 A 消失了,就會直接銷毀 A集绰;當 D 發(fā)現多了一個子節(jié)點 A规辱,則會創(chuàng)建新的 A(包括子節(jié)點)作為其子節(jié)點。此時栽燕,React diff 的執(zhí)行情況:create A -> create B -> create C -> delete A

注意:在開發(fā)組件時按摘,保持穩(wěn)定的 DOM 結構會有助于性能的提升。例如纫谅,可以通過 CSS 隱藏或顯示節(jié)點,而不是真的移除或添加 DOM 節(jié)點

  • 2溅固、component diff:

  • 如果是同類型付秕,按照原來策略 繼續(xù)比較 virtual DOM tree;

  • 如果不是一個類型侍郭,替換整個組件下的所有子節(jié)點

  • 對于同一類型的組件询吴,有可能其 Virtual DOM 沒有任何變化掠河,如果能夠確切的知道這點那可以節(jié)省大量的 diff 運算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進行 diff猛计。

  • 3唠摹、element diff:

  • React diff 提供了三種節(jié)點操作,分別為:INSERT_MARKUP(插入)奉瘤、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)

  • 全新的節(jié)點勾拉,老的集合里面沒有 ,就重新創(chuàng)建 插入盗温;

  • 可以復用之前的藕赞,做了移動;

  • 老 component 類型卖局,在新集合里也有斧蜕,但對應的 element 不同則不能直接復用和更新,需要執(zhí)行刪除操作砚偶,或者老 component 不在新集合里的批销,也需要執(zhí)行刪除操作。

7541670c089b84c59b84e9438e92a8e9_r.jpg

按照diff 規(guī)則染坯,B != A均芽,則創(chuàng)建并插入 B 至新集合,刪除老集合 A酒请;以此類推骡技,創(chuàng)建并插入 A、D 和 C羞反,刪除 B布朦、C 和 D。這樣比較繁瑣昼窗,因為都是相同的節(jié)點是趴,移動就可以減少操作,所以提出了優(yōu)化的方法:允許開發(fā)者對同一層級的同組子節(jié)點澄惊,添加唯一 key 進行區(qū)分唆途,雖然只是小小的改動,性能上卻發(fā)生了翻天覆地的變化

主要分析新老集合中存在相同節(jié)點但位置不同時掸驱,對節(jié)點進行位置移動的

總結

  • React 通過制定大膽的 diff 策略肛搬,將 O(n3) 復雜度的問題轉換成 O(n) 復雜度的問題
  • React 通過分層求異的策略,對 tree diff 進行算法優(yōu)化
  • React 通過相同類生成相似樹形結構毕贼,不同類生成不同樹形結構的策略温赔,對 component diff 進行算法優(yōu)化
  • React 通過設置唯一 key的策略,對 element diff 進行算法優(yōu)化
  • 建議鬼癣,在開發(fā)組件時陶贼,保持穩(wěn)定的 DOM 結構會有助于性能的提升

我們都知道 react 的核心的思想:
內存維護虛擬dom (js 對象)啤贩,數據變化時(setState),自動更新虛擬 DOM拜秧,得到一顆新樹痹屹,然后 Diff 新老虛擬 DOM 樹,找到有變化的部分枉氮,得到一個 Change(Patch)志衍,將這個 Patch 加入隊列,最終批量更新這些 Patch 到 DOM 中

調和階段(Reconciler)

React 會自頂向下通過遞歸嘲恍,遍歷新數據生成新的 Virtual DOM足画,然后通過 Diff 算法,找到需要變更的元素(Patch)佃牛,放到更新隊列里面去淹辞。

在協(xié)調階段,采用遞歸的遍歷方法 俘侠,稱為Stack Reconciler 這種方式:
一旦任務開始進行象缀,就無法中斷,那么 js 將一直占用主線程爷速, 一直要等到整棵 Virtual DOM 樹計算完成之后央星,才能把執(zhí)行權交給渲染引擎,那么這就會導致一些用戶交互惫东、動畫等任務無法立即得到處理莉给,就會有卡頓,非常的影響用戶體驗

針對任務一旦執(zhí)行廉沮,無法中斷颓遏,js 一直占用主線程導致卡頓的問題如何優(yōu)化?

為什么會出現卡頓滞时?

  • 1叁幢、處理用戶交互;
  • 2坪稽、js 解析執(zhí)行
  • 3曼玩、幀開始。窗口尺寸變更窒百,頁面滾去等的處理
  • 4黍判、requestAnimationFrame
  • 5、布局
  • 6篙梢、繪制

以上任意一個環(huán)節(jié)占用的時間過長都用可能造成卡頓的現象顷帖;在協(xié)調階段 js 執(zhí)行過長,那么就有可能本來應該渲染下一幀,但是當前js 還在執(zhí)行窟她,導致卡頓感。

解決方法:
把渲染更新過程拆分成多個子任務蔼水,每次只做一小部分震糖,做完看是否還有剩余時間,如果有繼續(xù)下一個任務趴腋;如果沒有吊说,掛起當前任務,將時間控制權交給主線程优炬,等主線程不忙的時候在繼續(xù)執(zhí)行颁井。 操作系統(tǒng)常用任務調度策略之一。

操作系統(tǒng)常用任務調度策略:
先來先服務(FCFS)調度算法;
短作業(yè)(進程)優(yōu)先調度算法(SJ/PF);
最高優(yōu)先權優(yōu)先調度算法(FPF);
高響應比優(yōu)先調度算法(HRN);
時間片輪轉法(RR);
多級隊列反饋法蠢护。

  • 合作式調度主要就是用來分配任務的雅宾,當有更新任務來的時候,不會立馬diff葵硕,而是把更新推入 Update Queue 中眉抬,然后交給 Scheduler 去處理。
  • 兩個執(zhí)行幀之間懈凹,主線程通常會有一小段空閑時間蜀变,requestIdleCallback可以在這個空閑期(Idle Period)調用空閑期回調(Idle Callback),執(zhí)行一些任務介评。
  • requestIdleCallback方法提供 deadline库北,即任務執(zhí)行限制時間,以切分任務们陆,避免長時間執(zhí)行寒瓦,阻塞UI渲染而導致掉幀;

1棒掠、如何拆分子任務孵构?
2、有剩余時間怎么去調度應該執(zhí)行哪一個任務烟很?
3颈墅、沒有剩余時間之前的任務怎么辦?

對于上面的問題 雾袱,react 通過 Fiber 來解決

渲染階段 (render)

遍歷更新隊列恤筛,通過調用宿主環(huán)境的API,實際更新渲染對應元素芹橡。

Fiber

fiber 代表 一種工作單元毒坛,需要重新實現一個堆棧幀的調度,可以按照自己的調度算法執(zhí)行他們,另外這些調度可以自己控制煎殷,所以本質上Fiber 也可以理解為一個虛擬的堆棧幀屯伞。 Fiber 是一種數據結構(堆棧幀),也可以說是一種解決可中斷的調用任務的一種解決方案豪直,它的特性就是時間分片(time slicing)和暫停(supense)劣摇。

Fiber 是如何工作的?
  • ReactDOM.render() 和 setState 的時候開始創(chuàng)建更新弓乙;
  • 將創(chuàng)建的更新加入任務隊列末融,等待調度
  • 在 requestIdleCallback 空閑時執(zhí)行任務
  • 從根節(jié)點開始遍歷 Fiber Node,并且構建 WokeInProgress Tree暇韧。
  • 生成 effectList
  • 根據 EffectList 更新 DOM勾习。

第一:
從 ReactDOM.render() 方法開始,把接收的 React Element 轉換為 Fiber 節(jié)點懈玻,并為其設置優(yōu)先級巧婶,創(chuàng)建 Update,加入到更新隊列酪刀,這部分主要是做一些初始數據的準備

創(chuàng)建ReactRoot 實例 root 調用root.render -> updateContainer-> 設置優(yōu)先級

第二:
主要是三個函數:scheduleWork粹舵、requestWork、performWork骂倘,即安排工作眼滤、申請工作、正式工作三部曲历涝,React 16 新增的異步調用的功能則在這部分實現诅需,這部分就是 Schedule 階段

第三:
遍歷所有的 Fiber 節(jié)點,通過 Diff 算法計算所有更新工作荧库,產出 EffectList 給到 commit 階段使用堰塌,這部分的核心是 beginWork 函數

Fiber的關鍵特性如下::

  • 增量渲染(把渲染任務拆分成塊,勻到多幀)
  • 更新時能夠暫停分衫,終止场刑,復用渲染任務
  • 給不同類型的更新賦予優(yōu)先級
  • 并發(fā)方面新的基礎能力
Fiber Node
{
  ...
  // 跟當前Fiber相關本地狀態(tài)(比如瀏覽器環(huán)境就是DOM節(jié)點)
  stateNode: any,

    // 單鏈表樹結構
  return: Fiber | null,// 指向他在Fiber節(jié)點樹中的`parent`,用來在處理完這個節(jié)點之后向上返回
  child: Fiber | null,// 指向自己的第一個子節(jié)點
  sibling: Fiber | null,  // 指向自己的兄弟結構蚪战,兄弟節(jié)點的return指向同一個父節(jié)點

  // 更新相關
  pendingProps: any,  // 新的變動帶來的新的props
  memoizedProps: any,  // 上一次渲染完成之后的props
  updateQueue: UpdateQueue<any> | null,  // 該Fiber對應的組件產生的Update會存放在這個隊列里面
  memoizedState: any, // 上一次渲染的時候的state

  // Scheduler 相關
  expirationTime: ExpirationTime,  // 代表任務在未來的哪個時間點應該被完成牵现,不包括他的子樹產生的任務
  // 快速確定子樹中是否有不在等待的變化
  childExpirationTime: ExpirationTime,

 // 在Fiber樹更新的過程中,每個Fiber都會有一個跟其對應的Fiber
  // 我們稱他為`current <==> workInProgress`
  // 在渲染完成之后他們會交換位置
  alternate: Fiber | null,

  // Effect 相關的
  effectTag: SideEffectTag, // 用來記錄Side Effect
  nextEffect: Fiber | null, // 單鏈表用來快速查找下一個side effect
  firstEffect: Fiber | null,  // 子樹中第一個side effect
  lastEffect: Fiber | null, // 子樹中最后一個side effect
  ....
};
Fiber Reconciler

Fiber Reconciler 是 React 里的調和器邀桑,這也是任務調度完成之后瞎疼,如何去執(zhí)行每個任務,如何去更新每一個節(jié)點的過程

reconcile 過程分為2個階段(phase):
1壁畸、(可中斷)render/reconciliation 通過構造 WorkInProgress Tree 得出 Change贼急。
2茅茂、(不可中斷)commit 應用這些DOM change。

reconciliation 階段

由于 reconciliation 階段是可中斷的太抓,一旦中斷之后恢復的時候又會重新執(zhí)行空闲,所以很可能 reconciliation 階段的生命周期方法會被多次調用

commit 階段

commit 階段可以理解為就是將 Diff 的結果反映到真實 DOM 的過程

commit 階段會執(zhí)行如下的聲明周期方法:

  • getSnapshotBeforeUpdate
  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

注意區(qū)別 reconciler、reconcile 和 reconciliation走敌,reconciler 是調和器进副,是一個名詞,可以說是 React 工作的一個模塊悔常,協(xié)調模塊;reconcile 是調和器調和的動作给赞,是一個動詞机打;而 reconciliation 只是 reconcile 過程的第一個階段。

Fiber Tree 和 WorkInProgress Tree
  • React 在 render 第一次渲染時片迅,會通過 React.createElement 創(chuàng)建一顆 Element 樹残邀,可以稱之為 Virtual DOM Tree,

  • 由于要記錄上下文信息柑蛇,加入了 Fiber芥挣,每一個 Element 會對應一個 Fiber Node,將 Fiber Node 鏈接起來的結構成為 Fiber Tree耻台。

  • 它反映了用于渲染 UI 的應用程序的狀態(tài)空免。這棵樹通常被稱為 current 樹(當前樹,記錄當前頁面的狀態(tài))

  • 在后續(xù)的更新過程中(setState)盆耽,每次重新渲染都會重新創(chuàng)建 Element, 但是 Fiber 不會蹋砚,Fiber 只會使用對應的 Element 中的數據來更新自己必要的屬性

  • Fiber Tree 一個重要的特點是鏈表結構,將遞歸遍歷編程循環(huán)遍歷摄杂,然后配合 requestIdleCallback API, 實現任務拆分坝咐、中斷與恢復

WechatIMG537.png

每一個 Fiber Node 節(jié)點與 Virtual Dom 一一對應,所有 Fiber Node 連接起來形成 Fiber tree, 是個單鏈表樹結構

當調用 setState 的時候又是如何 Diff 得到 change 的呢

采用的是一種叫雙緩沖技術(double buffering)析恢,這個時候就需要另外一顆樹:WorkInProgress Tree墨坚,它反映了要刷新到屏幕的未來狀態(tài)

WorkInProgress Tree 構造完畢,得到的就是新的 Fiber Tree映挂,然后喜新厭舊(把 current 指針指向WorkInProgress Tree泽篮,丟掉舊的 Fiber Tree)就好了

創(chuàng)建 WorkInProgress Tree 的過程也是一個 Diff 的過程,Diff 完成之后會生成一個 Effect List袖肥,這個 Effect List 就是最終 Commit 階段用來處理副作用的階段咪辱。

源碼實現(中文版): https://libin1991.github.io/2019/10/25/React-Fiber%E8%B0%83%E5%BA%A6%E5%8E%9F%E7%90%86

源碼實現(英文版): https://pomb.us/build-your-own-react/

Fiber 介紹: http://www.ayqy.net/blog/dive-into-react-fiber/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市椎组,隨后出現的幾起案子油狂,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件专筷,死亡現場離奇詭異弱贼,居然都是意外死亡,警方通過查閱死者的電腦和手機磷蛹,發(fā)現死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門吮旅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人味咳,你說我怎么就攤上這事庇勃。” “怎么了槽驶?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵责嚷,是天一觀的道長。 經常有香客問我掂铐,道長罕拂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任全陨,我火速辦了婚禮爆班,結果婚禮上,老公的妹妹穿的比我還像新娘辱姨。我一直安慰自己柿菩,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布雨涛。 她就那樣靜靜地躺著碗旅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镜悉。 梳的紋絲不亂的頭發(fā)上祟辟,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音侣肄,去河邊找鬼旧困。 笑死,一個胖子當著我的面吹牛稼锅,可吹牛的內容都是我干的吼具。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼矩距,長吁一口氣:“原來是場噩夢啊……” “哼拗盒!你這毒婦竟也來了?” 一聲冷哼從身側響起锥债,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陡蝇,失蹤者是張志新(化名)和其女友劉穎痊臭,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體登夫,經...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡广匙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了恼策。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸦致。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涣楷,靈堂內的尸體忽然破棺而出分唾,到底是詐尸還是另有隱情,我是刑警寧澤狮斗,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布鳍寂,位于F島的核電站,受9級特大地震影響情龄,放射性物質發(fā)生泄漏。R本人自食惡果不足惜捍壤,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一骤视、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹃觉,春花似錦专酗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疗隶,卻和暖如春佑笋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背斑鼻。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工蒋纬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坚弱。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓蜀备,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荒叶。 傳聞我的和親對象是個殘疾皇子碾阁,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內容