Angular2 Observable和RxJS

? ? ? ?看到Observable和RxJS就感覺很親切绢片,因為之前做Android開發(fā)的時候接觸過RxJava嵌屎。Observable和RxJS的相關(guān)知識最好的文檔還是官方文檔 https://www.angular.cn/guide/observables 強(qiáng)烈推薦大家看官方文檔毫胜。

一、可觀察對象(Observable)

? ? ? ?可觀察對象(Observable)在Angular 中使用非常廣泛。可觀察對象支持在應(yīng)用中的發(fā)布者和訂閱者之間傳遞消息撮躁。 在需要進(jìn)行事件處理、異步編程和處理多個值的時候买雾,可觀察對象相對其它技術(shù)有著顯著的優(yōu)點把曼。

? ? ? ?可觀察對象的使用本質(zhì)可以認(rèn)為是一個觀察者模式杨帽。簡單的流程就是一個觀察者(Observer)通過subscribe()方法訂閱一個可觀察對象(Observable)。訂閱之后觀察者(Obsever)對可觀察者(Observable)發(fā)射的數(shù)據(jù)或數(shù)據(jù)序列就能作出響應(yīng)(next函數(shù)發(fā)射數(shù)據(jù))嗤军。涉及到三個東西:觀察者(Observer)注盈、可觀察者(Observable)、訂閱(subscribe)叙赚。

我們先給出一個簡單的實例老客,然后再分開來講觀察者(Observer)、可觀察者(Observable)震叮、訂閱(subscribe)胧砰。

import {Component} from '@angular/core';
import {of} from 'rxjs';

// 創(chuàng)建一個可觀察者對象-Observable,發(fā)射三個數(shù)據(jù)1冤荆、2、3
const myObservable = of(1, 2, 3);

// 創(chuàng)建一個觀察者對象-Observer(處理next权纤、error钓简、complete回調(diào))
const myObserver = {
    next: x => console.log('Observer got a next value: ' + x),
    error: err => console.error('Observer got an error: ' + err),
    complete: () => console.log('Observer got a complete notification'),
};

// 通過Observable的subscribe函數(shù),觀察者去訂閱可觀察者的消息
myObservable.subscribe(myObserver);

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

1.1汹想、Observer(觀察者)

? ? ? ?Observer(觀察者)用于接收Observable(可觀察者)對象通知的處理器(說白了就是就是接收Observable發(fā)送過來的消息)外邓。Observer(觀察者)需要實現(xiàn)Observer接口。
觀察者對象定義了一些回調(diào)函數(shù)來處理可觀察對象可能會發(fā)來的三種通知(Observer接口里面的方法)古掏。

通知類型(方法) 說明
next 必要损话。用來處理每個發(fā)送過來的值。在開始執(zhí)行后可能執(zhí)行零次或多次
error 可選槽唾。用來處理錯誤通知丧枪。錯誤會中斷這個可觀察對象實例的執(zhí)行過程
complete 可選。用來處理執(zhí)行完畢(complete)的通知庞萍。當(dāng)執(zhí)行完畢后拧烦,這些值就會繼續(xù)傳給下一個處理器

? ? ? ?當(dāng)然了觀察者對象可以定義這三種處理器(next、error钝计、complete)的任意組合恋博。如果你不為某種通知類型提供處理器,這個觀察者就會忽略相應(yīng)類型的通知私恬。

? ? ? ?舉個例子比如我們只想處理next()方法對應(yīng)的通知那么觀察值就可以這么寫了:

// 創(chuàng)建一個觀察者對象-Observer(只處理next回調(diào))
const myObserver = {
    next: x => console.log('Observer got a next value: ' + x),
};

1.2债沮、Observable(可觀察者)

? ? ? ?使用Observable構(gòu)造函數(shù)可以創(chuàng)建任何類型的可觀察流。 當(dāng)執(zhí)行可觀察對象的subscribe()方法時本鸣,這個構(gòu)造函數(shù)就會把它接收到的參數(shù)作為訂閱函數(shù)來運行疫衩。 訂閱函數(shù)需要接收一個Observer對象,并把值發(fā)布給觀察者對象的next()方法荣德。其實很好理解隧土,比如有如下的代碼提针,sequenceSubscriber方法是Observable構(gòu)造函數(shù)的參數(shù)。當(dāng)調(diào)用subscribe()方法訂閱的時候就會執(zhí)行sequenceSubscriber方法里面的動作發(fā)射數(shù)據(jù)曹傀。

import {Component} from '@angular/core';
import {Observable, of} from 'rxjs';

// 可觀察者構(gòu)造函數(shù)的參數(shù)
function sequenceSubscriber(observer) {
    // 發(fā)射三個值
    observer.next(1);
    observer.next(2);
    observer.next(3);
    observer.complete();

    return {
        unsubscribe() {
        }
    };
}

// 通過構(gòu)造函數(shù)來創(chuàng)建一個可觀察者
const sequence = new Observable(sequenceSubscriber);

// 訂閱
sequence.subscribe({
    next(num) {
        console.log(num);
    },
    complete() {
        console.log('Finished sequence');
    }
});

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

1.3辐脖、 Subscribing(訂閱)

? ? ? ?光有觀察者和可觀察者是不夠的,還需要通過訂閱把他兩串聯(lián)起來才能運作起來皆愉。只有當(dāng)有人訂閱Observable的實例時嗜价,它才會開始發(fā)布值。 訂閱就是去調(diào)用Observable對象的subscribe()方法幕庐,并把一個Observer對象傳給它久锥,用來接收通知。subscribe()方法的調(diào)用會返回一個Subscription對象异剥,該對象具有一個unsubscribe()方法瑟由。當(dāng)調(diào)用該方法時,你就會停止接收通知冤寿。

1.4歹苦、多播

? ? ? ?默認(rèn)情況下可觀察對象會為每一個觀察者創(chuàng)建一次新的、獨立的執(zhí)行督怜。 訂閱了多少次就會有多少個獨立的流(next監(jiān)聽器會重復(fù)調(diào)用)殴瘦。

? ? ? ?多播:多播用來讓可觀察對象在一次執(zhí)行中同時廣播給多個訂閱者。借助支持多播的可觀察對象号杠,你不必注冊多個監(jiān)聽器蚪腋,而是復(fù)用第一個(next)監(jiān)聽器,并且把值發(fā)送給各個訂閱者姨蟋。我們通過一個簡單的實例來看多播的代碼應(yīng)該怎么寫屉凯,會把所有的觀察者放在一個數(shù)組里面,然后復(fù)用第一個觀察者的監(jiān)聽器眼溶。

多播代碼

import {Component} from '@angular/core';
import {Observable} from 'rxjs';

function multicastSequenceSubscriber() {
    // 需要發(fā)射的數(shù)據(jù)
    const seq = [1, 2, 3];
    // 觀察者數(shù)組神得,多播那肯定會有多個觀察者
    const observers = [];
    let timeoutId;

    // 在調(diào)用Observable對應(yīng)subscriber()方法的時候,會傳入進(jìn)來observer觀察者對象
    return (observer) => {
        // observers觀察者對象加入數(shù)組
        observers.push(observer);
        // 第一次有觀察者訂閱過來的時候
        if (observers.length === 1) {
            timeoutId = doSequence({
                next(val) {
                    // 遍歷每個觀察者偷仿,調(diào)用觀察者的next()方法
                    observers.forEach(obs => obs.next(val));
                },
                complete() {
                    // 遍歷每個觀察者哩簿,調(diào)用觀察者的complete()方法,調(diào)用slice(0)又沖第一個元素開始遍歷酝静。
                    // 因為前面已經(jīng)調(diào)用過observers.forEach了已經(jīng)移動到最后一個元素去了
                    observers.slice(0).forEach(obs => obs.complete());
                }
            }, seq, 0);
        }

        return {
            unsubscribe() {
                // 如果調(diào)用了取消訂閱节榜,則從數(shù)組里面刪除
                observers.splice(observers.indexOf(observer), 1);
                // 如果是最后一個,則清除 timer out
                if (observers.length === 0) {
                    clearTimeout(timeoutId);
                }
            }
        };
    };
}

// 每秒發(fā)射一個數(shù)據(jù)
function doSequence(observer, arr, idx) {
    return setTimeout(() => {
        observer.next(arr[idx]);
        if (idx === arr.length - 1) {
            observer.complete();
        } else {
            // 繼續(xù)執(zhí)行
            doSequence(observer, arr, ++idx);
        }
    }, 1000);
}

// 創(chuàng)建一個多播的被觀察者
const multicastSequence = new Observable(multicastSequenceSubscriber());

// 第一個觀察者訂閱
multicastSequence.subscribe({
    next(num) {
        console.log('1st subscribe: ' + num);
    },
    complete() {
        console.log('1st sequence finished.');
    }
});

// 1.5s之后别智,第二個觀察者訂閱
setTimeout(() => {
    multicastSequence.subscribe({
        next(num) {
            console.log('2nd subscribe: ' + num);
        },
        complete() {
            console.log('2nd sequence finished.');
        }
    });
}, 1500);

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

二宗苍、RxJS

2.1、RxJS簡單介紹

? ? ? ?RxJS(響應(yīng)式擴(kuò)展的JavaScript 版)是一個使用可觀察對象進(jìn)行響應(yīng)式編程的庫。它讓組合異步代碼和基于回調(diào)的代碼變得更簡單讳窟。RxJS文檔鏈接 https://rxjs-dev.firebaseapp.com/ 让歼。

RxJS是一個庫,一個工具丽啡,讓我們寫異步的代碼非常的簡單谋右。

? ? ? ?RxJS的學(xué)習(xí)關(guān)鍵在操作符的學(xué)習(xí),RxJS提供了各種各樣的操作符补箍。操作符用的對很多事情能事半功倍改执。操作符的類型有:創(chuàng)建操作符、組合操作符坑雅、過濾操作符辈挂、轉(zhuǎn)換操作符、多播操作符等等裹粤。

RxJS常用操作符

類別 操作
創(chuàng)建 from终蒂、fromPromise、fromEvent遥诉、of等
組合 combineLatest拇泣、concat、merge突那、startWith挫酿、withLatestFrom构眯、zip等
過濾 debounceTime , distinctUntilChanged , filter , take , takeUntil等
轉(zhuǎn)換 bufferTime , concatMap , map , mergeMap , scan , switchMap等
工具 tap等
多播 share等

如果想深入的學(xué)習(xí)RxJS可以多去了解里面的操作符愕难。我只能說操作符非常的強(qiáng)大。

2.2惫霸、管道pipe

? ? ? ?有的時候我們可能想把多個操作符連接起來就需要借助管道pipe()函數(shù)來實現(xiàn)猫缭。pipe() 函數(shù)以你要組合的這些函數(shù)作為參數(shù),并且返回一個新的函數(shù)壹店,當(dāng)執(zhí)行這個新函數(shù)時猜丹,就會順序執(zhí)行那些被組合進(jìn)去的函數(shù)。我們用一個簡單的實例來來看看pipe管道怎么使用硅卢,通過管道把filter操作符和map操作符鏈接起來射窒。

要是在RxJava里面要把多個操作符鏈接起來,非常的簡單直接...鏈?zhǔn)骄幊叹涂梢詫崿F(xiàn)将塑。但是RxJS里面不支持這種操作脉顿,只能通過管道把多個操作符鏈接起來。

import {Component} from '@angular/core';
import {of} from 'rxjs';
import {filter, map} from 'rxjs/operators';

// 通過RxJS的of創(chuàng)建操作符創(chuàng)建一個Observable對象点寥,并且通過管道把filter操作符和map操作符鏈接起來
const squareOdd = of(1, 2, 3, 4, 5)
    .pipe(
        // 只需要奇數(shù)
        filter(n => n % 2 !== 0),
        // 值平方
        map(n => n * n)
    );

// 訂閱
squareOdd.subscribe(x => console.log(x));

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

2.3艾疟、RxJS錯誤處理

? ? ? ?RxJS除了可以在訂閱時提供error()處理器外,RxJS 還提供了catchError操作符來處理一些不是致命的錯誤。什么意思蔽莱,我們知道一旦走到error()方法去了之后整個數(shù)據(jù)流就直接斷了弟疆,比如我們順序發(fā)送100個數(shù)據(jù),第一個數(shù)據(jù)發(fā)送的時候就發(fā)生了錯誤后面的99個數(shù)據(jù)都沒辦法再發(fā)送了盗冷。RxJS里面的catchError操作符可以避免這種情況怠苔,他讓你有一個修復(fù)的機(jī)會,我們可以在catchError里面做一些特殊的處理正塌,當(dāng)?shù)谝粋€數(shù)據(jù)發(fā)送的時候的錯誤嘀略,我們可以通過某種方式讓數(shù)據(jù)可以繼續(xù)發(fā)送。 比如如下的實例乓诽,第一個數(shù)據(jù)發(fā)射的時候產(chǎn)生了錯誤帜羊,我們接著從2開始發(fā)送數(shù)據(jù)。

RxJS里面的catchError 操作符讓我們對一些錯誤可以做一些修復(fù)鸠天。

import {Component} from '@angular/core';
import {range} from 'rxjs';
import {catchError, map} from 'rxjs/operators';

const apiData = range(1, 100).pipe(
    map(value => {
        if (value === 1) {
            // 第一個數(shù)據(jù)就發(fā)生錯誤了
            throw new Error('Value expected!');
        }
        return value;
    }),
    // 當(dāng)有錯誤返回的時候讼育,我們又從2開始發(fā)送
    catchError(err => range(2, 99))
);

apiData.subscribe({
    next(x) {
        console.log('data: ', x);
    },
    error(err) {
        console.log('errors already caught... will not run');
    }
});

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

三、Angular里面的可觀察對象

? ? ? ?Angular通常使用可觀察對象作為處理各種常用異步操作的接口稠集。

3.1奶段、事件發(fā)送器 EventEmitter

? ? ? ?EventEmitter類我們在前一篇文章(組件交互)有提過,當(dāng)子組件想向父組件發(fā)送消息的時候我們就用到了EventEmitter剥纷。EventEmitter用來從組件的 @Output() 屬性中發(fā)布一些值痹籍。EventEmitter擴(kuò)展了Observable,并添加了一個 emit()方法晦鞋,這樣它就可以發(fā)送任意值了蹲缠。當(dāng)你調(diào)用emit() 時,就會把所發(fā)送的值傳給訂閱上來的觀察者的next()方法悠垛。

子組件代碼

import {Component, EventEmitter, Output} from '@angular/core';

@Component({
  selector: 'app-data-child',
  template: `
    <button (click)="vote(true)">點擊</button>
  `
})
export class DataChildComponent {

  // @Output定義一個準(zhǔn)備回調(diào)父組件的事件EventEmitter也是可以傳遞參數(shù)的
  @Output() voted = new EventEmitter<boolean>();


  vote(agreed: boolean) {
    // 把事件往上拋出去线定,可以帶參數(shù)
    this.voted.emit(agreed);
  }
}

父組件代碼

import {Component} from '@angular/core';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <p>點擊 {{clickCount}} 次</p>
    <app-data-child (voted)="onVoted($event)"></app-data-child>
  `
})
export class DataParentComponent {

  clickCount = 0;

  /**
   * 子組件拋上來的事件
   */
  onVoted(agreed: boolean) {
    this.clickCount++;
  }
}

? ? ? ?上面代碼中我們在子組件里面通過@Output() voted = new EventEmitter<boolean>();定義了一個EventEmitter類型的輸出變量voted。并且父組件里面綁定到了這個輸出變量(其實就是訂閱的關(guān)系)确买。我們可以理解下他們是怎么工作的斤讥。當(dāng)子組件里面的按鈕被點擊之后,調(diào)用了EventEmitter的emit()函數(shù)湾趾。這個時候EventEmitter會去檢測有那些觀察值訂閱了這個EventEmitter芭商。然后調(diào)用這些訂閱者的next()函數(shù)從而調(diào)用父組件模板里面寫的函數(shù)。

3.2搀缠、HTTP

? ? ? ?Angular的HttpClient從HTTP 方法調(diào)用中返回了可觀察對象铛楣。例如,http.get('url')胡嘿、http.post('url')返回的對象就是可觀察對象蛉艾。我們還是用一個非常簡單的實例來說明。組件里面有一個按鈕,點擊這個按鈕的時候通過htt.get(url)獲取url上對應(yīng)的數(shù)據(jù)勿侯。

service對應(yīng)代碼

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable()
export class NetworkProtocolsService {

  constructor(private http: HttpClient) {
  }

  /**
   * 獲取配置信息-返回Observable
   */
  getConfig() {
    return this.http.get('assets/config.json');
  }
}

組件對應(yīng)代碼

import {Component} from '@angular/core';
import {NetworkProtocolsService} from '../network-protocols.service';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <button (click)="onButtonClick()">點擊獲取配置信息</button>
    <p>配置信息:{{message}}</p>
  `,
  providers: [NetworkProtocolsService]
})
export class DataParentComponent {

  message = '';

  constructor(private protocolsService: NetworkProtocolsService) {
  }


  // 點擊按鈕獲取配置信息,簡單的把配置信息顯示出來
  onButtonClick() {
    this.protocolsService.getConfig()
      .subscribe(
        data => this.message = data.toString(),
        err => console.error('network get error: ' + err));

  }
}

3.3拓瞪、AsyncPipe(異步管道)

? ? ? ?AsyncPipe(異步管道)訂閱一個 Observable或Promise對象(這里我們只講Observable),并返回它發(fā)出的最新值助琐。 當(dāng)通過next()方法發(fā)出新值時祭埂,訂閱的異步管道就知道數(shù)據(jù)發(fā)送變化了,然后標(biāo)識組件需執(zhí)行變化檢測兵钮。 當(dāng)組件被銷毀時蛆橡,異步管道自動取消訂閱,以避免潛在的內(nèi)存泄漏掘譬。

? ? ? ?如下實例泰演,time是一個Observable,并且并且每一秒都發(fā)射一個新的值葱轩。

import {Component} from '@angular/core';
import {Observable} from 'rxjs';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <div><code>observable|async</code>:
      Time: {{ time | async }}
    </div>`
})
export class DataParentComponent {

  time = new Observable(observer => {
      setInterval(() => observer.next(new Date().toString()), 1000);
    }
  );
}


3.4睦焕、路由器(router)

? ? ? ?Router.events路由器的事件都是以可觀察對象的形式提供的。而且路由器的事件有以下七種靴拱。如下所示:

路由器事件 說明
NavigationStart 會在導(dǎo)航開始時觸發(fā)
RoutesRecognized 會在路由器解析完 URL垃喊,并識別出了相應(yīng)的路由時觸發(fā)
RouteConfigLoadStart 會在 Router 對一個路由配置進(jìn)行惰性加載之前觸發(fā)
RouteConfigLoadEnd 會在路由被惰性加載之后觸發(fā)
NavigationEnd 會在導(dǎo)航成功結(jié)束之后觸發(fā)
NavigationCancel 會在導(dǎo)航被取消之后觸發(fā)。 這可能是因為在導(dǎo)航期間某個路由守衛(wèi)返回了 false
NavigationError 會在導(dǎo)航由于意料之外的錯誤而失敗時觸發(fā)

? ? ? ?因為路由器事件是以O(shè)bservable的形式提供的袜炕,我們也可以使用RxJS里面的filter()操作符來找到感興趣的事件本谜,并且訂閱它們。如下代碼我們只關(guān)心NavigationStart事件偎窘。

import {Component, OnInit} from '@angular/core';
import {Observable} from 'rxjs';
import {NavigationStart, Router} from '@angular/router';
import {filter} from 'rxjs/operators';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: ``
})
export class DataParentComponent implements OnInit {

  navStart: Observable<NavigationStart>;

  constructor(private router: Router) {
    // 我們只是關(guān)心NavigationStart事件
    this.navStart = router.events.pipe(
      filter(evt => evt instanceof NavigationStart)
    ) as Observable<NavigationStart>;
  }

  ngOnInit(): void {
    this.navStart.subscribe(evt => console.log('Navigation Started!'));
  }
}


3.5乌助、響應(yīng)式表單 (reactive forms)

? ? ? ?響應(yīng)式表單具有一些屬性,它們使用可觀察對象來監(jiān)聽表單控件的值评架。FormControl的valueChanges屬性和statusChanges屬性包含了會發(fā)出變更事件的可觀察對象眷茁。訂閱可觀察的表單控件屬性是在組件類中觸發(fā)應(yīng)用邏輯的途徑之一炕泳。

? ? ? ?用一個簡單的實例來說明纵诞,當(dāng)input里面的值變化的時候,我們能獲取input的值的變化培遵。

import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <h2>Hero Detail</h2>
    <h3><i>FormControl in a FormGroup</i></h3>
    <form [formGroup]="heroForm" novalidate>
      <div class="form-group">
        <label class="center-block">Name:
          <input class="form-control" formControlName="name">
        </label>
      </div>
    </form>
  `
})
export class DataParentComponent implements OnInit {

  heroForm = new FormGroup({
    name: new FormControl()
  });

  ngOnInit() {
    const nameControl = this.heroForm.get('name');
    // 當(dāng)表單里面數(shù)據(jù)變化的時候浙芙,可以收到通知。nameControl.valueChanges就是一個Observable
    nameControl.valueChanges.forEach(
      (value: string) => console.log(value)
    );
  }


}



? ? ? ?以上就是對Angular2以上Observable和RxJS的簡答介紹籽腕。最后還是想告訴大家最好的文檔還是官方文檔 https://www.angular.cn/guide/observables

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗡呼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子皇耗,更是在濱河造成了極大的恐慌南窗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異万伤,居然都是意外死亡窒悔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門敌买,熙熙樓的掌柜王于貴愁眉苦臉地迎上來简珠,“玉大人,你說我怎么就攤上這事虹钮×郑” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵芙粱,是天一觀的道長祭玉。 經(jīng)常有香客問我,道長春畔,這世上最難降的妖魔是什么攘宙? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮拐迁,結(jié)果婚禮上蹭劈,老公的妹妹穿的比我還像新娘。我一直安慰自己线召,他們只是感情好铺韧,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缓淹,像睡著了一般哈打。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讯壶,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天料仗,我揣著相機(jī)與錄音,去河邊找鬼伏蚊。 笑死立轧,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躏吊。 我是一名探鬼主播氛改,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼比伏!你這毒婦竟也來了胜卤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤赁项,失蹤者是張志新(化名)和其女友劉穎葛躏,沒想到半個月后澈段,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡舰攒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年均蜜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芒率。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡囤耳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偶芍,到底是詐尸還是另有隱情充择,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布匪蟀,位于F島的核電站椎麦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏材彪。R本人自食惡果不足惜观挎,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望段化。 院中可真熱鬧嘁捷,春花似錦、人聲如沸显熏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喘蟆。三九已至缓升,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕴轨,已是汗流浹背港谊。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留橙弱,地道東北人歧寺。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像膘螟,于是被迫代替她去往敵國和親成福。 傳聞我的和親對象是個殘疾皇子碾局,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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