better-Bscroll

謝謝作者的文章? 非常喜歡? 請允許收藏!

博客園首頁博問閃存新隨筆訂閱管理


vue之better-scroll的封裝,包含下拉刷新,上拉加載功能及UI(核心為借鑒,我僅僅是給輪子套上了外胎...)

先發(fā)原文作者、地址等信息。我把內(nèi)容全部搬過來了,也可以去看原文。內(nèi)容絕對是滿滿的干貨,給原作者點贊!(我添加的內(nèi)容在轉(zhuǎn)載過來的后面,內(nèi)容不多)

作者: ustbhuangyi

鏈接:http://www.imooc.com/article/18232來源:慕課網(wǎng)

在我們?nèi)粘5囊苿佣隧椖块_發(fā)中,處理滾動列表是再常見不過的需求了,以滴滴為例,可以是這樣豎向滾動的列表,如圖所示:

也可以是橫向滾動的導航欄,如圖所示:

可以打開“微信 —> 錢包—>滴滴出行”體驗效果。

我們在實現(xiàn)這類滾動功能的時候,會用到我寫的第三方庫,better-scroll。

什么是 better-scroll

better-scroll 是一個移動端滾動的解決方案,它是基于 iscroll 的重寫,它和 iscroll 的主要區(qū)別在這里。better-scroll 也很強大,不僅可以做普通的滾動列表,還可以做輪播圖、picker 等等。

better-scroll 的滾動原理

不少同學可能用過 better-scroll,我收到反饋最多的問題是:

我的 better-scroll 初始化了, 但是沒法滾動。

不能滾動是現(xiàn)象,我們得搞清楚這其中的根本原因。在這之前,我們先來看一下瀏覽器的滾動原理:

瀏覽器的滾動條大家都會遇到,當頁面內(nèi)容的高度超過視口高度的時候,會出現(xiàn)縱向滾動條;當頁面內(nèi)容的寬度超過視口寬度的時候,會出現(xiàn)橫向滾動條。也就是當我們的視口展示不下內(nèi)容的時候,會通過滾動條的方式讓用戶滾動屏幕看到剩余的內(nèi)容。

那么對于 better-scroll 也是一樣的道理,我們先來看一下 better-scroll 常見的 html 結(jié)構(gòu):

  • ...
  • ...
  • ...

    為了更加直觀,我們再來看一張圖:

    綠色部分為 wrapper,也就是父容器,它會有固定的高度。黃色部分為 content,它是父容器的第一個子元素,它的高度會隨著內(nèi)容的大小而撐高。那么,當 content 的高度不超過父容器的高度,是不能滾動的,而它一旦超過了父容器的高度,我們就可以滾動內(nèi)容區(qū)了,這就是 better-scroll 的滾動原理。

    那么,我們怎么初始化 better-scroll 呢,如果是上述 html 結(jié)構(gòu),那么初始化代碼如下:

    import BScroll from 'better-scroll'let wrapper= document.querySelector('.wrapper')

    let scroll=newBScroll(wrapper, {})

    better-scroll 對外暴露了一個 BScroll 的類,我們初始化只需要 new 一個類的實例即可。第一個參數(shù)就是我們 wrapper 的 DOM 對象,第二個是一些配置參數(shù),具體參考better-scroll 的文檔

    better-scroll 的初始化時機很重要,因為它在初始化的時候,會計算父元素和子元素的高度和寬度,來決定是否可以縱向和橫向滾動。因此,我們在初始化它的時候,必須確保父元素和子元素的內(nèi)容已經(jīng)正確渲染了。如果子元素或者父元素 DOM 結(jié)構(gòu)發(fā)生改變的時候,必須重新調(diào)用scroll.refresh()方法重新計算來確保滾動效果的正常。所以同學們反饋的 better-scroll 不能滾動的原因多半是初始化 better-scroll 的時機不對,或者是當 DOM 結(jié)構(gòu)發(fā)送變化的時候并沒有重新計算 better-scroll。

    better-scroll 遇見 Vue

    相信很多同學對Vue.js都不陌生,當 better-scroll 遇見 Vue,會擦出怎樣的火花呢?

    如何在 Vue 中使用 better-scroll

    很多同學開始接觸使用 better-scroll 都是受到了我的一門教學課程——《Vue.js高仿餓了么外賣App》的影響。在那門課程中,我們把 better-scroll 和 Vue 做了結(jié)合,實現(xiàn)了很多列表滾動的效果。在 Vue 中的使用方法如下:

    ?

    ? ?
      ? ? ?
    • ...
    • ? ? ?
    • ...
    • ...
    ?
    import BScroll from'better-scroll'exportdefault{

    mounted() {this.$nextTick(() =>{this.scroll =newBscroll(this.$refs.wrapper, {})

    })

    }

    }

    Vue.js 提供了我們一個獲取 DOM 對象的接口——vm.$refs。在這里,我們通過了this.$refs.wrapper訪問到了這個 DOM 對象,并且我們在 mounted 這個鉤子函數(shù)里,this.$nextTick的回調(diào)函數(shù)中初始化 better-scroll 。因為這個時候,wrapper 的 DOM 已經(jīng)渲染了,我們可以正確計算它以及它內(nèi)層 content 的高度,以確保滾動正常。

    這里的this.$nextTick是一個異步函數(shù),為了確保 DOM 已經(jīng)渲染,感興趣的同學可以了解一下它的內(nèi)部實現(xiàn)細節(jié),底層用到了 MutationObserver 或者是setTimeout(fn, 0)。其實我們在這里把this.$nextTick替換成setTimeout(fn, 20)也是可以的(20 ms 是一個經(jīng)驗值,每一個 Tick 約為 17 ms),對用戶體驗而言都是無感知的。

    異步數(shù)據(jù)的處理

    在我們的實際工作中,列表的數(shù)據(jù)往往都是異步獲取的,因此我們初始化 better-scroll 的時機需要在數(shù)據(jù)獲取后,代碼如下:

    ?

    ? ?
      ? ? ?
    • {{item}}
    • ? ?
    ?
    import BScroll from'better-scroll'exportdefault{

    data() {return{

    data: []

    }

    },

    created() {

    requestData().then((res)=>{this.data =res.datathis.$nextTick(() =>{this.scroll =newBscroll(this.$refs.wrapper, {})

    })

    })

    }

    }

    這里的 requestData 是偽代碼,作用就是發(fā)起一個 http 請求從服務(wù)端獲取數(shù)據(jù),并且這個函數(shù)返回的是一個 promise(實際項目中我們可能會用axios或者vue-resource)。我們獲取到數(shù)據(jù)的后,需要通過異步的方式再去初始化 better-scroll,因為 Vue 是數(shù)據(jù)驅(qū)動的, Vue 數(shù)據(jù)發(fā)生變化(this.data = res.data)到頁面重新渲染是一個異步的過程,我們的初始化時機是要在 DOM 重新渲染后,所以這里用到了this.$nextTick,當然替換成setTimeout(fn, 20)也是可以的。

    為什么這里在 created 這個鉤子函數(shù)里請求數(shù)據(jù)而不是放到 mounted 的鉤子函數(shù)里?因為 requestData 是發(fā)送一個網(wǎng)絡(luò)請求,這是一個異步過程,當拿到響應(yīng)數(shù)據(jù)的時候,Vue 的 DOM 早就已經(jīng)渲染好了,但是數(shù)據(jù)改變 —> DOM 重新渲染仍然是一個異步過程,所以即使在我們拿到數(shù)據(jù)后,也要異步初始化 better-scroll。

    數(shù)據(jù)的動態(tài)更新

    我們在實際開發(fā)中,除了數(shù)據(jù)異步獲取,還有一些場景可以動態(tài)更新列表中的數(shù)據(jù),比如常見的下拉加載,上拉刷新等。比如我們用 better-scroll 配合 Vue 實現(xiàn)下拉加載功能,代碼如下:

    ?

    ? ?
      ? ? ?
    • {{item}}
    • ? ?
    ? ?
    ?
    import BScroll from'better-scroll'exportdefault{

    data() {return{

    data: []

    }

    },

    created() {this.loadData()

    },

    methods: {

    loadData() {

    requestData().then((res)=>{this.data = res.data.concat(this.data)this.$nextTick(() =>{if(!this.scroll) {this.scroll =newBscroll(this.$refs.wrapper, {})this.scroll.on('touchend', (pos) =>{//下拉動作if(pos.y > 50) {this.loadData()

    }

    })

    }else{this.scroll.refresh()

    }

    })

    })

    }

    }

    }

    這段代碼比之前稍微復雜一些, 當我們在滑動列表松開手指時候, better-scroll 會對外派發(fā)一個 touchend 事件,我們監(jiān)聽了這個事件,并且判斷了 pos.y > 50(我們把這個行為定義成一次下拉的動作)。如果是下拉的話我們會重新請求數(shù)據(jù),并且把新的數(shù)據(jù)和之前的 data 做一次 concat,也就更新了列表的數(shù)據(jù),那么數(shù)據(jù)的改變就會映射到 DOM 的變化。需要注意的一點,這里我們對this.scroll做了判斷,如果沒有初始化過我們會通過new BScroll初始化,并且綁定一些事件,否則我們會調(diào)用this.scroll.refresh方法重新計算,來確保滾動效果的正常。

    這里,我們就通過 better-scroll 配合 Vue,實現(xiàn)了列表的下拉刷新功能,上拉加載也是類似的套路,一切看上去都是 ok 的。但是,我們發(fā)現(xiàn)這里寫了大量命令式的代碼(這一點不是 Vue.js 推薦的),如果有很多類似滾動的組件,我們就需要寫很多類似的命令式且重復性的代碼,而且我們把數(shù)據(jù)請求和 better-scroll 也做了強耦合,這些對于一個追求編程逼格的人來說,就不 ok 了。

    scroll 組件的抽象和封裝

    因此,我們有強烈的需求抽象出來一個 scroll 組件,類似小程序的 scroll-view 組件,方便開發(fā)者的使用。

    首先,我們要考慮的是 scroll 組件本質(zhì)上就是一個可以滾動的列表組件,至于列表的 DOM 結(jié)構(gòu),只需要滿足 better-scroll 的 DOM 結(jié)構(gòu)規(guī)范即可,具體用什么標簽,有哪些輔助節(jié)點(比如下拉刷新上拉加載的 loading 層),這些都不是 scroll 組件需要關(guān)心的。因此, scroll 組件的 DOM 結(jié)構(gòu)十分簡單,如下所示:

    這里我們用到了 Vue 的特殊元素—— slot 插槽,它可以滿足我們靈活定制列表 DOM 結(jié)構(gòu)的需求。接下來我們來看看 JS 部分:

    import BScroll from'better-scroll'exportdefault{

    props: {/**

    * 1 滾動的時候會派發(fā)scroll事件,會截流。

    * 2 滾動的時候?qū)崟r派發(fā)scroll事件,不會截流。

    * 3 除了實時派發(fā)scroll事件,在swipe的情況下仍然能實時派發(fā)scroll事件*/probeType: {

    type: Number,default: 1},/**

    * 點擊列表是否派發(fā)click事件*/click: {

    type: Boolean,default:true},/**

    * 是否開啟橫向滾動*/scrollX: {

    type: Boolean,default:false},/**

    * 是否派發(fā)滾動事件*/listenScroll: {

    type: Boolean,default:false},/**

    * 列表的數(shù)據(jù)*/data: {

    type: Array,default:null},/**

    * 是否派發(fā)滾動到底部的事件,用于上拉加載*/pullup: {

    type: Boolean,default:false},/**

    * 是否派發(fā)頂部下拉的事件,用于下拉刷新*/pulldown: {

    type: Boolean,default:false},/**

    * 是否派發(fā)列表滾動開始的事件*/beforeScroll: {

    type: Boolean,default:false},/**

    * 當數(shù)據(jù)更新后,刷新scroll的延時。*/refreshDelay: {

    type: Number,default: 20}

    },

    mounted() {//保證在DOM渲染完畢后初始化better-scrollsetTimeout(() =>{this._initScroll()

    },20)

    },

    methods: {

    _initScroll() {if(!this.$refs.wrapper) {return}//better-scroll的初始化this.scroll =newBScroll(this.$refs.wrapper, {

    probeType:this.probeType,

    click:this.click,

    scrollX:this.scrollX

    })//是否派發(fā)滾動事件if(this.listenScroll) {

    let me=thisthis.scroll.on('scroll', (pos) =>{

    me.$emit('scroll', pos)

    })

    }//是否派發(fā)滾動到底部事件,用于上拉加載if(this.pullup) {this.scroll.on('scrollEnd', () =>{//滾動到底部if(this.scroll.y <= (this.scroll.maxScrollY + 50)) {this.$emit('scrollToEnd')

    }

    })

    }//是否派發(fā)頂部下拉事件,用于下拉刷新if(this.pulldown) {this.scroll.on('touchend', (pos) =>{//下拉動作if(pos.y > 50) {this.$emit('pulldown')

    }

    })

    }//是否派發(fā)列表滾動開始的事件if(this.beforeScroll) {this.scroll.on('beforeScrollStart', () =>{this.$emit('beforeScroll')

    })

    }

    },

    disable() {//代理better-scroll的disable方法this.scroll &&this.scroll.disable()

    },

    enable() {//代理better-scroll的enable方法this.scroll &&this.scroll.enable()},

    refresh(){//代理better-scroll的refresh方法this.scroll &&this.scroll.refresh()},scrollTo(){//代理better-scroll的scrollTo方法this.scroll &&this.scroll.scrollTo.apply(this.scroll, arguments)},scrollToElement(){//代理better-scroll的scrollToElement方法this.scroll &&this.scroll.scrollToElement.apply(this.scroll, arguments)}},watch:{//監(jiān)聽數(shù)據(jù)的變化,延時refreshDelay時間后調(diào)用refresh方法重新計算,保證滾動效果正常data(){

    setTimeout(()=>{this.refresh()},this.refreshDelay)}}}

    JS 部分實際上就是對 better-scroll 做一層 Vue 的封裝,通過 props 的形式,把一些對 better-scroll 定制化的控制權(quán)交給父組件;通過 methods 暴露的一些方法對 better-scroll 的方法做一層代理;通過 watch 傳入的 data,當 data 發(fā)生改變的時候,在適當?shù)臅r機調(diào)用 refresh 方法重新計算 better-scroll 確保滾動效果正常,這里之所以要有一個 refreshDelay 的設(shè)置是考慮到如果我們對列表操作用到了 transition-group 做動畫效果,那么 DOM 的渲染完畢時間就是在動畫完成之后。

    有了這一層 scroll 組件的封裝,我們來修改剛剛最復雜的代碼(假設(shè)我們已經(jīng)全局注冊了 scroll 組件)。

    ? ? ?

      ? ? ?
    • {{item}}
    • ? ?
    ? ?
    ? import BScroll from'better-scroll'exportdefault{

    data() {return{

    data: [],

    pulldown:true}

    },

    created() {this.loadData()

    },

    methods: {

    loadData() {

    requestData().then((res)=>{this.data = res.data.concat(this.data)

    })

    }

    }

    }

    可以很明顯的看到我們的 JS 部分精簡了非常多的代碼,沒有對 better-scroll 再做命令式的操作了,同時把數(shù)據(jù)請求和 better-scroll 也做了剝離,父組件只需要把數(shù)據(jù) data 通過 prop 傳給 scroll 組件,就可以保證 scroll 組件的滾動效果。同時,如果想實現(xiàn)下拉刷新的功能,只需要通過 prop 把 pulldown 設(shè)置為 true,并且監(jiān)聽 pulldown 的事件去做一些數(shù)據(jù)獲取并更新的動作即可,整個邏輯也是非常清晰的。

    插件 Vue 化引發(fā)的一些思考

    這篇文章我不僅僅是要教會大家封裝一個 scroll 組件,還想傳遞一些把第三方插件(原生 JS 實現(xiàn))Vue 化的思考過程。很多學習 Vue.js 的同學可能還停留在 “XX 效果如何用 Vue.js 實現(xiàn)” 的程度,其實把插件 Vue 化有兩點很關(guān)鍵,一個是對插件本身的實現(xiàn)原理很了解,另一個是對 Vue.js 的特性很了解。對插件本身的實現(xiàn)原理了解需要的是一個思考和鉆研的過程,這個過程可能困難,但是收獲也是巨大的;而對 Vue.js 的特性的了解,是需要大家對 Vue.js 多多使用,學會從平時的項目中積累和總結(jié),也要善于查閱 Vue.js 的官方文檔,關(guān)注一些 Vue.js 的升級等。

    所以,我們拒絕伸手黨,但也不是鼓勵大家什么時候都要去造輪子,當我們在使用一些現(xiàn)成插件的同時,也希望大家能多多思考,去探索一下現(xiàn)象背后的本質(zhì),把 “XX 效果如何用 Vue.js 實現(xiàn)” 這句話從問號變成句號。

    以下內(nèi)容是我在作者基礎(chǔ)上添加了一些交互效果,和作者的放在一起做成一個組件,可以直接拿去用。為了更容易看懂我的思路,進行了簡要的注釋。

    {{pulldownTip.text}}
    {{loadingStatus.status}}import BScroll from'better-scroll'exportdefault{

    props: {/**

    * 1 滾動的時候會派發(fā)scroll事件,會截流。

    * 2 滾動的時候?qū)崟r派發(fā)scroll事件,不會截流。

    * 3 除了實時派發(fā)scroll事件,在swipe的情況下仍然能實時派發(fā)scroll事件*/probeType: {

    type: Number,default:1},/**

    * 點擊列表是否派發(fā)click事件*/click: {

    type: Boolean,default:true},/**

    * 是否開啟橫向滾動*/scrollX: {

    type: Boolean,default:false},/**

    * 是否派發(fā)滾動事件*/listenScroll: {

    type: Boolean,default:false},/**

    * 列表的數(shù)據(jù)*/data: {

    type: Array,default:null},/**

    * 是否派發(fā)滾動到底部的事件,用于上拉加載*/pullup: {

    type: Boolean,default:false},/**

    * 是否派發(fā)頂部下拉的事件,用于下拉刷新*/pulldown: {

    type: Boolean,default:false},/**

    * 是否派發(fā)列表滾動開始的事件*/beforeScroll: {

    type: Boolean,default:false},/**

    * 當數(shù)據(jù)更新后,刷新scroll的延時。*/refreshDelay: {

    type: Number,default:20},/**

    * 如果啟用loading交互,傳遞loading的狀態(tài)

    * isShow: false

    * showIcon: false,? ? // 是否顯示loading的icon

    * status: ''? // '正在加載...', '刷新成功', '刷新失敗', ''*/loadingStatus: {

    type: Object,default:function() {return{

    showIcon:false,

    status:''};

    }

    },/**

    * 是否啟用下拉刷新的交互*/pulldownUI: {

    type: Boolean,default:false},/**

    * 是否啟用上拉加載的交互*/pullupUI: {

    type: Boolean,default:false}

    },

    data() {return{

    loadingConnecting:false,

    pulldownTip: {

    text:'下拉刷新',//松開立即刷新rotate:''//icon-rotate},

    };

    },

    mounted() {//保證在DOM渲染完畢后初始化better-scrollsetTimeout(()=>{this._initScroll()

    },20)

    },

    methods: {

    _initScroll() {if(!this.$refs.wrapper) {return;

    }//better-scroll的初始化this.scroll=newBScroll(this.$refs.wrapper, {

    probeType:this.probeType,

    click:this.click,

    scrollX:this.scrollX

    });//是否派發(fā)滾動事件if(this.listenScroll||this.pulldown||this.pullup) {

    let me=this;this.scroll.on('scroll', (pos)=>{if(this.listenScroll) {

    me.$emit('scroll', pos);

    }if(this.pulldown) {//下拉動作if(pos.y>50) {this.pulldownTip={

    text:'松開立即刷新',

    rotate:'icon-rotate'}

    }else{this.pulldownTip={

    text:'下拉刷新',//松開立即刷新rotate:''//icon-rotate}

    }

    }if(this.pullup) {

    }

    })

    }//是否派發(fā)滾動到底部事件,用于上拉加載if(this.pullup) {this.scroll.on('scrollEnd', ()=>{

    console.log('scrollEnd');

    console.log(this.scroll);//滾動到底部if(this.scroll.y<=(this.scroll.maxScrollY+50)) {this.$emit('scrollToEnd');

    }

    });

    }//是否派發(fā)頂部下拉事件,用于下拉刷新if(this.pulldown) {this.scroll.on('touchend', (pos)=>{//下拉動作if(pos.y>50) {

    setTimeout(()=>{//重置提示信息this.pulldownTip={

    text:'下拉刷新',//松開立即刷新rotate:''//icon-rotate}

    },600);this.$emit('pulldown');

    }

    });

    }//是否派發(fā)列表滾動開始的事件if(this.beforeScroll) {this.scroll.on('beforeScrollStart', ()=>{this.$emit('beforeScroll')

    });

    }

    },

    disable() {//代理better-scroll的disable方法this.scroll&&this.scroll.disable();

    },

    enable() {//代理better-scroll的enable方法this.scroll&&this.scroll.enable();

    },

    refresh() {//代理better-scroll的refresh方法this.scroll&&this.scroll.refresh();

    },

    scrollTo() {//代理better-scroll的scrollTo方法this.scroll&&this.scroll.scrollTo.apply(this.scroll, arguments);

    },

    scrollToElement() {//代理better-scroll的scrollToElement方法this.scroll&&this.scroll.scrollToElement.apply(this.scroll, arguments);

    }

    },

    watch: {//監(jiān)聽數(shù)據(jù)的變化,延時refreshDelay時間后調(diào)用refresh方法重新計算,保證滾動效果正常data() {

    setTimeout(()=>{this.refresh();

    },this.refreshDelay);

    }

    }

    }$cube-size: 10px; // 項目中用了scss,沒用的話,替換掉樣式中的變量即可

    .better-scroll-root{background-color:rgba(7, 17, 27, 0.7);.loading-pos, .pulldown-tip {

    position:absolute;left:0;top:0;width:100%;height:35px;color:#fcfcfc;text-align:center;z-index:2000;}.loading-pos{background-color:rgba(7, 17, 27, 0.7);}.pulldown-tip{top:-50px;height:50px;line-height:50px;z-index:1;}.pull-icon{position:absolute;top:0;left:30%;color:#a5a1a1;font-size:1.5em;transition:all 0.15s ease-in-out;}.pull-icon.icon-rotate{transform:rotate(180deg);}.loading-container{position:absolute;height:$cube-size;width:$cube-size;left:35%;top:50%;transform:translate(-50%, -50%);perspective:40px;}.loading-connecting{line-height:35px;}.cube{height:$cube-size;width:$cube-size;transform-origin:50% 50%;transform-style:preserve-3d;animation:rotate 3s infinite ease-in-out;}.side{position:absolute;height:$cube-size;width:$cube-size;border-radius:50%;}.side1{background:#4bc393;transform:translateZ($cube-size);}.side2{background:#FF884D;transform:rotateY(90deg) translateZ($cube-size);}.side3{background:#32526E;transform:rotateY(180deg) translateZ($cube-size);}.side4{background:#c53fa3;transform:rotateY(-90deg) translateZ($cube-size);}.side5{background:#FFCC5C;transform:rotateX(90deg) translateZ($cube-size);}.side6{background:#FF6B57;transform:rotateX(-90deg) translateZ($cube-size);}@keyframes rotate{0%{

    transform:rotateX(0deg) rotateY(0deg);}50%{transform:rotateX(360deg) rotateY(0deg);}100%{transform:rotateX(360deg) rotateY(360deg);}}

    }

    下拉刷新,上拉加載(暫時未做),刷新中等效果如下:

    need-to-insert-img

    need-to-insert-img

    need-to-insert-img

    以上內(nèi)容還不夠精細,等這段時間忙過去了會繼續(xù)優(yōu)化。如有bug,歡迎各位看官批評指正。

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

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