Snabbdom 底層Diff算法詳解

Vue2.0Virtual DOM 是基于 Snabbdom 改造的赛蔫,針對 ChildrenDiff 過程裤唠,做了一下詳細的過程分析以及演示挤牛。

  • 前言

?? 瀏覽器在渲染 DOM 元素的時候,是非持终海‘昂貴的’墓赴,在進行 DOM 更新的時候,針對復(fù)雜 DOM 的局部更新航瞭,為了節(jié)省瀏覽器的開支诫硕,避免不必要的更新,這個時候就需要通過Diff 比較來決定更新哪些 DOM 元素刊侯,當(dāng)然章办,并不是所有的情況都能使 Virtual DOM 比原生 DOM 操作要快,這里我就不過多闡述了滨彻,好了藕届,進入主題

1.0 Snabbdom Diff 方式

?? Snabbdom 在進行 Diff 比較的時候只針對同層級進行比較,而不會進行跨層級比較亭饵,也就是說休偶,我不會管你當(dāng)前的 Children 是否一致,這里我只關(guān)心你和他辜羊。Snabbdom 更新DOM的方式是 先移 后添加或刪除踏兜, Diff 比較是雙向由外向里進行比較。

Diff方式

為了方便演示 SnabbdomDiff 的過程八秃,這里我以一個 DOM 更新的案例為大家講解

??假設(shè)當(dāng)前 DOM parentElm 有9個 DOM元素碱妆,
??這里我用數(shù)組模擬oldCh = [null, "A", "B", "C", "D", "E", "F", "G", null]表示,
??其中數(shù)組的每一個元素代表一個子節(jié)點DOM喜德,其中兩個null山橄,表示已經(jīng)移除了,這里加入null 的目的是為了后續(xù)分析源碼流程所使用。

parentElm DOM

??更新后DOM parentElm有11個DOM元素航棱,
??這里我用數(shù)組模擬newCh = [null, "A", "F", "E", "M", "O", "I", "E", "B", "G", null]表示睡雇,
??其中數(shù)組的每一個元素代表一個子節(jié)點DOM,其中兩個null饮醇,表示已經(jīng)移除了它抱,這里加入null 的目的是為了后續(xù)分析源碼流程所使用。

2.0 Snabbdom Diff 過程

??首先朴艰,我用一張圖來解釋Snabbdom 在找出差異之前是如何進行Diff 比較的

找出差異前

??Snabbdom 在找出差異之前观蓄,每一輪的比較是需要經(jīng)過8項條件比較的,具體比較【條件】如下:

??【1】.判斷 oldCh 第一個節(jié)點元素是不是空的祠墅,如果是空的表示 DOM 已經(jīng)移除侮穿,接著進行下一輪比較
??【2】.判斷 oldCh 最后一個節(jié)點元素是不是空的,如果是空的表示 DOM 已經(jīng)移除毁嗦,接著進行下一輪比較
??【3】.判斷 newCh 第一個節(jié)點元素是不是空的亲茅,如果是空的表示 DOM 已經(jīng)移除,接著進行下一輪比較
??【4】.判斷 newCh 最后一個節(jié)點元素是不是空的狗准,如果是空的表示 DOM 已經(jīng)移除克锣,接著進行下一輪比較。
??【5】.判斷 oldChnewCh 第一個節(jié)點元素是不是相同腔长,如果相同我們接著進行下一輪比較
??【6】.判斷 oldChnewCh 最后一個節(jié)點元素是不是相同袭祟,如果相同我們接著進行下一輪比較
??【7】.判斷 oldCh 第一個節(jié)點元素與 newCh 的最后一個節(jié)點元素是否相同,如果相同捞附,就將oldCh 第一個節(jié)點元素進行移動巾乳,具體如何移動,后面我會詳細闡述說明故俐。
??【8】.判斷 oldCh 最后一個節(jié)點元素與 newCh 的第一個節(jié)點元素是否相同想鹰,如果相同,就將oldCh 最后一個節(jié)點元素進行移動药版,具體如何移動辑舷,后面我會詳細闡述說明。

??由于 Diff 比較過程是雙向由外向里進行比較槽片,所以為了比較過程這里我們設(shè)置幾個記錄值

??為了記錄比較過程這里我們設(shè)置幾個記錄值
??oldSIdx :初始值為0何缓,表示當(dāng)前oldCh 比較隊列的 初始的節(jié)點位置
??newSIdx :初始值為0,表示當(dāng)前newCh 比較隊列的 初始的節(jié)點位置
??oldEIdx : 初始值為oldCh.length-1还栓,表示當(dāng)前oldCh 比較隊列的末尾的節(jié)點位置
??newEIdx : 初始值為newCh.length-1碌廓,表示當(dāng)前newCh 比較隊列的末尾的節(jié)點位置
??oldSnode :初始值為oldCh[0],表示當(dāng)前oldCh 比較隊列的初始節(jié)點
??oldEnode :初始值為oldCh[oldEIdx]剩盒,表示當(dāng)前oldCh 比較隊列的末尾節(jié)點
??newSnode :初始值為newCh[0]谷婆,表示當(dāng)前newCh 比較隊列的初始節(jié)點
??newEnode :初始值為newCh[newEIdx],表示當(dāng)前newCh 比較隊列的末尾節(jié)點

??每一輪的Diff 執(zhí)行條件:oldSIdx <= oldEIdx && newSIdx <= newEIdx,也就是說纪挎,當(dāng)兩邊的比較有一方的元素已經(jīng)全部比較完畢期贫,那么Diff中止

??初始狀態(tài) :

初始狀態(tài)

?? 第1輪Diff 比較:滿足條件【1】 oldCh 第一個節(jié)點元素是空,執(zhí)行以下步驟异袄, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行: oldSIdx位置加1通砍,oldSnode指向下一個節(jié)點> oldSnode=[++oldSIdx]

第1輪執(zhí)行過程

??第1輪Diff 比較結(jié)束后,oldCh 初始記錄點oldSIdx索引+1烤蜕,由0變?yōu)?code>1封孙,oldSnode指向下一個節(jié)點,由null變?yōu)?code>A讽营。

第1輪比較結(jié)束后

?? 第2輪Diff 比較:滿足條件【2】 oldCh 最一個節(jié)點元素是空虎忌,,執(zhí)行以下步驟橱鹏, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行: oldEIdx位置減1呐籽,oldEnode指向上一個節(jié)點> oldEnode=[--oldEIdx]

第2輪執(zhí)行過程

??第2輪Diff 比較結(jié)束后,oldCh 末尾記錄點oldEIdx索引-1蚀瘸,由8變?yōu)?code>7,oldEnode指向下一個節(jié)點庶橱,由null變?yōu)?code>G贮勃。

第2輪比較結(jié)束后

?? 第3Diff 輪比較:滿足條件【3】 newCh 第一個節(jié)點元素是空,執(zhí)行以下步驟苏章, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行: newSIdx位置加1寂嘉,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第3輪執(zhí)行過程

??第3輪Diff 比較結(jié)束后,newCh 初始記錄點newSIdx索引+1枫绅,由0變?yōu)?code>1泉孩,newSnode指向下一個節(jié)點,由null變?yōu)?code>A并淋。

第3輪比較結(jié)束后

?? 第4輪Diff 比較:滿足條件【4】 newCh 最一個節(jié)點元素是空寓搬,執(zhí)行以下步驟, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行: newEIdx位置減1县耽,newEnode指向上一個節(jié)點> newEnode=[--newEIdx]

第4輪執(zhí)行過程

??第4輪Diff 比較結(jié)束后句喷,newCh 末尾記錄點newEIdx索引-1,由10變?yōu)?code>9兔毙,newEnode指向下一個節(jié)點唾琼,由null變?yōu)?code>G。

第4輪比較結(jié)束后

?? 第5輪Diff 比較:滿足條件【5】 oldChnewCh 第一個節(jié)點元素相同澎剥,執(zhí)行以下步驟锡溯, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??oldSIdx位置加1,oldSnode指向下一個節(jié)點> oldSnode=[++oldSIdx]
??newSIdx位置加1,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第5輪執(zhí)行過程

??第5輪Diff 比較結(jié)束后:
??oldCh 初始記錄點oldSIdx索引+1祭饭,由1變?yōu)?code>2芜茵,oldSnode指向下一個節(jié)點,由A變?yōu)?code>B甜癞。
??newCh 初始記錄點newSIdx索引+1夕晓,由1變?yōu)?code>2,newSnode指向下一個節(jié)點悠咱,由A變?yōu)?code>F蒸辆。

第5輪比較結(jié)束后

?? 第6輪Diff 比較:滿足條件【6】 oldChnewCh 最后一個節(jié)點元素相同,執(zhí)行以下步驟析既, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??oldEIdx位置減1躬贡,oldEnode指向上一個節(jié)點> oldEnode=[--oldEIdx]
??newEIdx位置減1,newEnode指向上一個節(jié)點> newEnode=[--newEIdx]

第6輪執(zhí)行過程

??第6輪Diff 比較結(jié)束后:
??oldCh 末尾記錄點oldEIdx索引-1眼坏,由7變?yōu)?code>6拂玻,oldEnode指向上一個節(jié)點,由G變?yōu)?code>F宰译。
??newCh 末尾記錄點newEIdx索引-1檐蚜,由9變?yōu)?code>8,newEnode指向上一個節(jié)點沿侈,由G變?yōu)?code>B闯第。

第6輪比較結(jié)束后

?? 看到這里,不知道各位有沒有發(fā)現(xiàn)缀拭,每一輪的比較咳短,在滿足Diff算法的前 6 種判斷條件下,父節(jié)點parentElm 是沒有發(fā)生任何變化的蛛淋,那么咙好,接下來就是見證奇跡的時刻!

?? 第7輪Diff 比較:滿足條件【7】 oldCh 第一個節(jié)點元素與 newCh 最后一個節(jié)點元素相同褐荷,執(zhí)行以下步驟勾效, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??我們首先將當(dāng)前的oldSnode(B)元素移動插入到當(dāng)前oldEnode(F)元素的后面
??oldSIdx位置加1,oldSnode指向下一個節(jié)點> oldSnode=[++oldSIdx]
??newEIdx位置減1诚卸,newEnode指向上一個節(jié)點> newEnode=[--newEIdx]

第7輪執(zhí)行過程

??第7輪Diff 比較結(jié)束后:
??parentElm更新DOM葵第,將當(dāng)前的oldSnode(B)元素移動插入到當(dāng)前oldEnode(F)元素的后面
??oldCh 初始記錄點oldSIdx索引+1,由2變?yōu)?code>3合溺,oldSnode指向下一個節(jié)點卒密,由B變?yōu)?code>C。
??newCh 末尾記錄點newEIdx索引-1棠赛,由8變?yōu)?code>7哮奇,newEnode指向上一個節(jié)點膛腐,由B變?yōu)?code>E。

第7輪比較結(jié)束后

?? 第8輪Diff 比較:滿足條件【8】 oldCh 最后一個節(jié)點元素與 newCh 第一個節(jié)點元素相同鼎俘,執(zhí)行以下步驟哲身, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??我們首先將當(dāng)前的oldEnode(F)元素插入到當(dāng)前oldSnode(C)元素的前面
??oldEIdx位置減1,oldEnode指向上一個節(jié)點> oldEnode=[--oldEIdx]
??newSIdx位置加1贸伐,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第8輪執(zhí)行過程

??第8輪Diff 比較結(jié)束后:
??parentElm更新DOM勘天,將當(dāng)前的oldEnode(F)元素插入到當(dāng)前oldSnode(C)元素的前面
??oldCh 末尾記錄點oldEIdx索引-1,由6變?yōu)?code>5捉邢,oldEnode指向上一個節(jié)點脯丝,由F變?yōu)?code>E。
??newCh 初始記錄點newSIdx索引+1伏伐,由2變?yōu)?code>3宠进,newSnode指向下一個節(jié)點,由F變?yōu)?code>E藐翎。

第8輪比較結(jié)束后

?? 第9輪Diff 比較:滿足條件【6】 oldChnewCh 最后一個節(jié)點元素相同材蹬,執(zhí)行以下步驟, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??oldEIdx位置減1吝镣,oldEnode指向上一個節(jié)點> oldEnode=[--oldEIdx]
??newEIdx位置減1堤器,newEnode指向上一個節(jié)點> newEnode=[--newEIdx]

第9輪執(zhí)行過程

??第9輪Diff 比較結(jié)束后:
??oldCh 末尾記錄點oldEIdx索引-1,由5變?yōu)?code>4末贾,oldEnode指向上一個節(jié)點吼旧,由E變?yōu)?code>D。
??newCh 末尾記錄點newEIdx索引-1未舟,由7變?yōu)?code>6,newEnode指向上一個節(jié)點掂为,由E變?yōu)?code>I裕膀。

第9輪比較結(jié)束后

?? 看到這里,經(jīng)過以上每一輪的比較勇哗,基本上前 8 種Diff條件都已經(jīng)差不多全部執(zhí)行了昼扛,接下來的就是我們找出差異之后的DOM更新了。

?? 第10輪Diff 比較:前 8 種Diff條件都不滿足欲诺,則需要新增節(jié)點
?? 執(zhí)行以下步驟抄谐, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??parentElm更新DOM,將當(dāng)前的oldEnode(E)元素插入到當(dāng)前oldSnode(C)元素的前面
??newSIdx位置+1扰法,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第10輪執(zhí)行過程

??第10輪Diff 比較結(jié)束后:
??parentElm更新DOM蛹含,將當(dāng)前的oldEnode(E)元素插入到當(dāng)前oldSnode(C)元素的前面
??newCh 初始記錄點newSIdx索引+1,由3變?yōu)?code>4塞颁,newSnode指向下一個節(jié)點浦箱,由E變?yōu)?code>M吸耿。

第10輪比較結(jié)束后

?? 第11輪Diff 比較:前 8 種Diff條件都不滿足,則需要新增節(jié)點
?? 執(zhí)行以下步驟酷窥,根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??parentElm更新DOM咽安,將當(dāng)前的oldEnode(M)元素插入到當(dāng)前oldSnode(C)元素的前面
??newSIdx位置+1,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第11輪執(zhí)行過程

??第11輪Diff 比較結(jié)束后:
??parentElm更新DOM蓬推,將當(dāng)前的oldEnode(M)元素插入到當(dāng)前oldSnode(C)元素的前面
??newCh 初始記錄點newSIdx索引+1妆棒,由4變?yōu)?code>5,newSnode指向下一個節(jié)點沸伏,由M變?yōu)?code>O糕珊。

第11輪比較結(jié)束后

;

?? 第12輪Diff 比較:前 8 種Diff條件都不滿足,則需要新增節(jié)點
?? 執(zhí)行以下步驟馋评, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??parentElm更新DOM放接,將當(dāng)前的oldEnode(O)元素插入到當(dāng)前oldSnode(C)元素的前面
??newSIdx位置+1,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第12輪執(zhí)行過程

??第12輪Diff 比較結(jié)束后:
??parentElm更新DOM留特,將當(dāng)前的oldEnode(O)元素插入到當(dāng)前oldSnode(C)元素的前面
??newCh 初始記錄點newSIdx索引+1纠脾,由5變?yōu)?code>6,newSnode指向下一個節(jié)點蜕青,由M變?yōu)?code>O苟蹈。

第12輪比較結(jié)束后

?? 第13輪Diff 比較:前 8 種Diff條件都不滿足,則需要新增節(jié)點
?? 執(zhí)行以下步驟右核, 根據(jù)結(jié)果接著進行下一輪比較

??執(zhí)行:
??parentElm更新DOM慧脱,將當(dāng)前的oldEnode(I)元素插入到當(dāng)前oldSnode(C)元素的前面
??newSIdx位置+1,newSnode指向下一個節(jié)點> newSnode=[++newSIdx]

第13輪執(zhí)行過程

??第13輪Diff 比較結(jié)束后:
??parentElm更新DOM贺喝,將當(dāng)前的oldEnode(I)元素插入到當(dāng)前oldSnode(C)元素的前面
??newCh 初始記錄點newSIdx索引+1菱鸥,由6變?yōu)?code>7,newSnode指向下一個節(jié)點躏鱼,由I變?yōu)?code>E氮采。
??此時 newSIdx > newEIdx,已經(jīng)不能滿足下一輪Diff 比較的條件染苛,Diff 比較比較結(jié)束

第13輪最后一次比較結(jié)束

??經(jīng)過13輪的新舊子節(jié)點Diff比較鹊漠,parentElm通過將舊節(jié)點移動,新節(jié)點增加茶行,進行了DOM的更新躯概,接下來就是根據(jù)最終oldChnewCh剩余情況進行parentElm的刪除或者新增。
??如果當(dāng)前newCh[newSIdx,newEIdx]有剩余畔师,說明還有需要新增的元素娶靡,那么我們根據(jù)剩余的節(jié)點區(qū)間,依次插入到最終比較的newEnode后面看锉。
??如果當(dāng)前oldCh[oldSIdx,oldEIdx]有剩余固蛾,說明這些元素是需要刪除的结执,那么我們根據(jù)剩余的節(jié)點區(qū)間,依次刪除艾凯。

old節(jié)點刪除過程

最終DOM局部更新完成

3.0 代碼模擬過程

基于Snabbdom献幔,通過數(shù)組來表示DOM節(jié)點元素,這里我稍微改造了一下趾诗,基于數(shù)組操作來模擬DOM的更新


/**
 * author:Echonessy
 * des:
 * date:2020.07.05
 * target: Diff 算法模擬
 * */


let oldCh = [null,"A","B","C","D","E","F","G",null];
let newCh = [null,"A","F","E","M","O","I","E","B","G",null];
// let oldCh = [null,"A","B","C","D","E","F","G",null];
// let newCh = [null,"A","F","C","D","E","M","O","I","E","B","G",null];
let parentElm = oldCh.slice();
diff(parentElm,oldCh,newCh);
// 判斷節(jié)點是否相同
export function same(vnode1, vnode2) {
    return vnode1 === vnode2
}

// 模擬DOM節(jié)點插入與移動
function insertBerfore(parent,newNode,referenceNode,order) {
    switch (order) {
        case 'left':parent.splice(parent.indexOf(referenceNode),0,newNode);parent.splice(parent.indexOf(newNode.split('-')[1]),1);break;
        case 'right':parent.splice(parent.indexOf(referenceNode),0,newNode);parent.splice(parent.lastIndexOf(newNode.split('-')[1]),1);break;
        case 'diff':parent.splice(parent.indexOf(referenceNode.indexOf('-')!=-1?referenceNode.split('-')[1]:referenceNode),0,newNode);break;
        case 'add':parent.splice(parent.lastIndexOf(referenceNode),0,newNode);break;
    }
}

// 移除vNode
export function removeVnodes(parentElm,vnodes,startIdx,endIdx){
    // 循環(huán)vnodes
    for (; startIdx <= endIdx; ++startIdx) {
        let ch = vnodes[startIdx];
        if (ch != null) {
            var index = parentElm.indexOf(ch);
            parentElm.splice(index, 1)
        }
    }
}
// 添加vNode
function addVnodes(parentElm,before,vnodes,startIdx,endIdx) {
    for (; startIdx <= endIdx; ++startIdx) {
        const ch = vnodes[startIdx];
        if (ch != null) {
            insertBerfore(parentElm,'new-'+ch,before,'add')
        }
    }
}
// diff 過程
function diff(parentElm,oldCh,newCh) {
    console.log('初始')
    console.log(oldCh)
    console.log(newCh)
    console.log(parentElm)
    let oldSIdx = 0, newSIdx = 0;
    let oldEIdx = oldCh.length - 1;
    let oldSnode = oldCh[0];
    let oldEnode = oldCh[oldEIdx];
    let newEIdx = newCh.length - 1;
    let newStnode = newCh[0];
    let newEnode = newCh[newEIdx];
    let conunt = 0; // 計數(shù)器蜡感,記錄循環(huán)次數(shù)
    // 兩數(shù)組比較結(jié)束之前,循環(huán)調(diào)用Diff
    while (oldSIdx <= oldEIdx && newSIdx <= newEIdx){
        if (oldSnode == null) {
            oldSnode = oldCh[++oldSIdx];
        } else if (oldEnode == null) {
            oldEnode = oldCh[--oldEIdx];
        } else if (newStnode == null) {
            newStnode = newCh[++newSIdx];
        } else if (newEnode == null) {
            newEnode = newCh[--newEIdx];
        } else if (same(oldSnode, newStnode)){
            oldSnode = oldCh[++oldSIdx];
            newStnode = newCh[++newSIdx];
        } else if (same(oldEnode, newEnode)) {
            oldEnode = oldCh[--oldEIdx];
            newEnode = newCh[--newEIdx];
        }else if (same(oldSnode, newEnode)) {
            insertBerfore(parentElm,'old-'+oldSnode,oldCh[oldEIdx+1],'left');
            oldSnode = oldCh[++oldSIdx];
            newEnode = newCh[--newEIdx];
        }else if (same(oldEnode, newStnode)) {
            insertBerfore(parentElm,'old-'+oldEnode,oldCh[oldSIdx],'right');
            oldEnode = oldCh[--oldEIdx];
            newStnode = newCh[++newSIdx];
        } else {
            insertBerfore(parentElm,'new-'+newStnode,oldCh[oldSIdx],'diff');
            newStnode = newCh[++newSIdx];
        }
        console.log('------------------------------------------------------------------')
        console.log('第'+ (++conunt) + '次對比,oldSIdx:'+oldSIdx + ' oldEIdx:' +oldEIdx + ' newSIdx:'+newSIdx+ ' newEIdx:'+newEIdx )
        console.log(oldCh.slice(oldSIdx,oldEIdx+1))
        console.log(newCh.slice(newSIdx,newEIdx+1))
        console.log(parentElm);
    }
    // 最后比較完成之后
    if (oldSIdx <= oldEIdx || newSIdx <= newEIdx) {
        console.log('Diff 對比結(jié)束')
        console.log(oldCh)
        console.log(newCh)
        if (oldSIdx > oldEIdx) {
            // 'old-'用來模擬區(qū)分新舊節(jié)點
            console.log('新增節(jié)點')
            let before =newCh[newEIdx+1] == null ? null : 'old-'+ newCh[newEIdx+1];
            addVnodes(parentElm, before, newCh, newSIdx, newEIdx);
        } else {
            console.log('移除節(jié)點')
            removeVnodes(parentElm, oldCh, oldSIdx, oldEIdx);
        }
        console.log('模擬DOM更新完成')
        console.log(parentElm);
    }
}

4.0 附件

Snabbdom 源代碼地址:https://github.com/snabbdom/snabbdom

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恃泪,一起剝皮案震驚了整個濱河市郑兴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贝乎,老刑警劉巖情连,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異览效,居然都是意外死亡却舀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門锤灿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挽拔,“玉大人,你說我怎么就攤上這事但校◇ψ纾” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵状囱,是天一觀的道長术裸。 經(jīng)常有香客問我,道長亭枷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任奶栖,我火速辦了婚禮,結(jié)果婚禮上门坷,老公的妹妹穿的比我還像新娘宣鄙。我一直安慰自己,他們只是感情好默蚌,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布冻晤。 她就那樣靜靜地躺著,像睡著了一般绸吸。 火紅的嫁衣襯著肌膚如雪鼻弧。 梳的紋絲不亂的頭發(fā)上设江,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音攘轩,去河邊找鬼叉存。 笑死,一個胖子當(dāng)著我的面吹牛度帮,可吹牛的內(nèi)容都是我干的歼捏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笨篷,長吁一口氣:“原來是場噩夢啊……” “哼瞳秽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起率翅,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤练俐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冕臭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腺晾,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年浴韭,在試婚紗的時候發(fā)現(xiàn)自己被綠了丘喻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡念颈,死狀恐怖泉粉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榴芳,我是刑警寧澤嗡靡,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站窟感,受9級特大地震影響讨彼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柿祈,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一哈误、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏嚎,春花似錦蜜自、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虚茶,卻和暖如春戈鲁,著一層夾襖步出監(jiān)牢的瞬間仇参,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工婆殿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诈乒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓鸣皂,卻偏偏與公主長得像抓谴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寞缝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355