Vue、Type escript和RxJS与Vue-Rx的结合

目前我正在写这篇文章,有很多方法来解决一个具体的问题。">目前我正在写这篇文章,有很多方法来解决一个具体的问题。</trans>

在本文中,我想给您一个灵感,它可能会改变您在应用程序中使用的当前方法。">在本文中,我想给您一个灵感,它可能会改变您在应用程序中使用的当前方法。</trans>

可能与非常复杂的实现、测试和维护相关。">RxJS可能与非常复杂的实现、测试和维护相关。

如果我告诉您,集成、实现和测试实际上非常容易,即使是在一个通用的(小型、中型、大型)VueJS应用程序中也是如此呢?(247, 238, 255);">如果我告诉您,集成、实现和测试实际上非常容易,即使是在一个通用的(小型、中型、大型)VueJS应用程序中也是如此呢?

在本文中,我将演示如何将VueJS、type escript和RxJS结合起来。我真的相信这是一个很棒的组合,还有一个插件,它真的很有用:">在本文中,我将演示如何将VueJS、type escript和RxJS结合起来。我真的相信这是一个很棒的组合,还有一个插件,它真的很有用.

你为什么要安装和使用这个插件?">你为什么要安装和使用这个插件?

因为它正在释放你的应用程序的超能力,它正在创造整个体验。">因为它正在释放你的应用程序的超能力,它正在创造整个体验。略显更流畅,作为初级或高级开发人员。">更流畅,作为初级或高级开发人员。

有了一个通用应用程序,第一个开始的命令如下:">有了一个通用应用程序,第一个开始的命令如下

npm install vue-rx rxjs --save</pre>

很明显,这将Vue-Rx和RxJS安装到项目的依赖项中。">很明显,这将Vue-Rx和RxJS安装到项目的依赖项中。

当然,我们需要告诉VueJS安装它(在全球范围内,是的):">当然,我们需要告诉VueJS安装它(在全球范围内,是的)

import Vue from 'vue'
import VueRx from 'vue-rx'

Vue.use(VueRx)</pre>

它导入最小数量的Rx操作符,并确保小的包大小。">它导入最小数量的Rx操作符,并确保小的包大小。
我亲自确认。">我亲自确认。

一旦所有的一切都设置好了,你就可以使用这个伟大的插件所暴露的任何东西了。如果我们深入研究类型记录定义,您就可以快速看到将添加到VueJS中的内容:">一旦所有的一切都设置好了,你就可以使用这个伟大的插件所暴露的任何东西了。如果我们深入研究类型记录定义,您就可以快速看到将添加到VueJS中的内容:</trans>

declare module* "vue/types/vue" {
  *interface* Vue {
    $observables: Observables;
    $watchAsObservable(expr: *string*, options?: WatchOptions): Observable<WatchObservable<*any*>>
    $watchAsObservable<T>(fn: (*this*: *this*) => T, options?: WatchOptions): Observable<WatchObservable<T>>
    $eventToObservable(event: *string*): Observable<{name: *string*, msg: *any*}>
    $subscribeTo<T>(
      observable: Observable<T>,
      next: (t: T) => *void*,
      error?: (e: *any*) => *void*,
      complete?: () => *void*): *void* $fromDOMEvent(selector: *string* | *null*, event: *string*): Observable<Event>
    $createObservableMethod(methodName: *string*): Observable<*any*>
  }
}

这看起来可能有点奇怪,但我将很快突出这些特性:">这看起来可能有点奇怪,但我将很快突出这些特性:
美元可观测值它将指出已登记的订阅;">它将指出已登记的订阅;</trans>
例如,一个可观察的功能可以帮助应用于该属性的流更改,而不是让一个泛型函数监视一个反应性属性;">
例如,一个可观察的功能可以帮助应用于该属性的流更改,而不是让一个泛型函数监视一个反应性属性;</trans>
可以将自定义事件处理程序或生命周期挂钩转换为可观察的;">:可以将自定义事件处理程序或生命周期挂钩转换为可观察的;</trans>
这是另一种很好的转换DOM事件的方法(例如,键向上,输入,点击等等)变成可观察的;">:这是另一种很好的转换DOM事件的方法(例如,键向上,输入,点击等等)变成可观察的
这也是一个很好的特性;如果将一个方法转换为一个可观察的值并将接收到的值处理为一个反应流(例如回调),这将很有帮助;">*这也是一个很好的特性;如果将一个方法转换为一个可观察的值并将接收到的值处理为一个反应流(例如回调),这将很有帮助;
它提供了手动注册一个可观察到的(声明下一个错误、完全回调)的机会,VueRx将管理注册并在不再有用的情况下处理它(例如已销毁的组件)。">:它提供了手动注册一个可观察到的(声明下一个错误、完全回调)的机会,VueRx将管理注册并在不再有用的情况下处理它(例如已销毁的组件)。

在上面,这个插件公开了一个">在上面,这个插件公开了一个=流指令">可与以下内容相结合

DOM事件
很酷,但是如何正确地使用它呢?">很酷,但是如何正确地使用它呢?

在很多情况下,可以使用上述特性,在本文中,我将介绍一个简单的案例:一个泛型输入字段,它触发对给定端点执行搜索的请求。在这种情况下,有很多事情和特性需要考虑:">在很多情况下,可以使用上述特性,在本文中,我将介绍一个简单的案例:一个泛型输入字段,它触发对给定端点执行搜索的请求。在这种情况下,有很多事情和特性需要考虑:

它可以避免每次输入新字符时发送大量请求;">它可以避免每次输入新字符时发送大量请求;
并发请求呢?哪一个应该优先考虑?">并发请求呢?哪一个应该优先考虑?
如果响应包含的数据可能不包含我们所需要的所有信息呢?我们在哪里处理?">如果响应包含的数据可能不包含我们所需要的所有信息呢?我们在哪里处理?

这是一个基本的场景。在一个特定的项目中,你可能会有另外万亿的分数。你是如何组合/处理它们的?你如何确保所有的东西都是声明性的和良好的阅读?另外,单元测试呢?">这是一个基本的场景。在一个特定的项目中,你可能会有另外万亿的分数。你是如何组合/处理它们的?你如何确保所有的东西都是声明性的和良好的阅读?另外,单元测试呢?

让我们开始为我们的组件实现基本代码:">让我们开始为我们的组件实现基本代码

 <label *for=*"search">Search for something:</label>
 <input *type=*"text" *id=*"search" *class=*"input-field">
 </div>
</template>

<script *lang=*"ts">
  *import* { Component, Vue }  *from* 'vue-property-decorator';
 @Component
  *export default class* Search *extends* Vue {}
</script>

<style *scoped*>
 .input-field {
 ...
 }
</style></pre>

因为我们依赖打字稿,所以我用的是">因为我们依赖打字稿,所房产装饰师这使我们能够在组件声明中使用装饰器(例如组件、Prop等,…)。。">‘这使我们能够在组件声明中使用装饰器(例如组件、Prop等,…)。

在我们的例子中,我们希望实现一个简单的行为:键入某样东西并作为查询进行搜索。">在我们的例子中,我们希望实现一个简单的行为:键入某样东西并作为查询进行搜索。</trans>

首先,我们需要创建一个可观察的,它将所有来自字段的输入流。一种方法是依赖于输入标记中的keyup事件:">首先,我们需要创建一个可观察的,它将所有来自字段的输入流。一种方法是依赖于输入标记中的keyup事件

Component<Search>({
    subscriptions() {
        *return* ({
            news: *this*.$fromDOMEvent('input', 'keyup')
        });
    }
})
*export default class* Search *extends* Vue {
    news: any; // we will specifically type it later on
}</pre>

在使用类型记录时,注册对VueJS组件的订阅的方法,就是上面声明的方法。">在使用类型记录时,注册对VueJS组件的订阅的方法,就是上面声明的方法。</trans>
从“订阅”函数返回的对象包含一个属性“news”,该属性是一个数据属性,它将与注册的可观测值相关联(在本例中为.$fromDOMEVENT…)。。">从“订阅”函数返回的对象包含一个属性“news”,该属性是一个数据属性,它将与注册的可观测值相关联
当输入字段中输入某项内容时,与“news”相关联的可观察到的事件将接收到DOM事件。现在,让我们稍微调整一下逻辑,转而依赖一个字符串(例如,字段的值)。">当输入字段中输入某项内容时,与“news”相关联的可观察到的事件将接收到DOM事件。现在,让我们稍微调整一下逻辑,转而依赖一个字符串(例如,字段的值)。

Component<Search>({
    subscriptions() {
        *return* ({
            news: *this*.$fromDOMEvent('input', 'keyup').pipe(
                pluck<Event, *string*>('target', 'value')
            )
        });
    }
})
*export default class* Search *extends* Vue {
    news: *string*;
}</pre>

为了从接收到的事件中检索输入的值,我们可以使用‘">为了从接收到的事件中检索输入的值,我们可以使用拔毛一个由RxJS公开的操作符,它允许我们从定义的对象中查看所需的属性(即使是嵌套的路径)。">一个由RxJS公开的操作符,它允许我们从定义的对象中查看所需的属性(即使是嵌套的路径)。</trans>

<figure style="display: block; margin: 0px 0px 1em; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(254, 254, 254); text-decoration-style: initial; text-decoration-color: initial;">

即使有一个字符串,它对我们也没有太大的帮助,因为我们需要一个结果列表。下一步可能是一个简单的请求,在HackerNewsAPI上搜索给定的查询(我们的输入字段值)。">即使有一个字符串,它对我们也没有太大的帮助,因为我们需要一个结果列表。下一步可能是一个简单的请求,在HackerNewsAPI上搜索给定的查询(我们的输入字段值)。</trans>

import* axios *from* 'axios';
*import* { Component, Vue } *from* 'vue-property-decorator';
*import* { *from* } *from* 'rxjs';
*import* { pluck, switchMap } *from* 'rxjs/operators';

*interface* HackerNewsResult {
    objectID: *string*;
    title?: *string*;
    url?: *string*;
}

*interface* HackerNewsSearchResponse {
    hits: Array<HackerNewsResult>
}

*const* hackerNewsEndpoint: *string* = 'http://hn.algolia.com/api/v1/search?query=';

@Component<Search>({
    subscriptions() {
        *return* ({
            news: *this*.$fromDOMEvent('input', 'keyup').pipe(
                pluck<Event, *string*>('target', 'value'),
                switchMap(value => *from*(
                    axios.get<HackerNewsSearchResponse>(`${hackerNewsEndpoint}${value}`)
                    )
                )
            )
        });
    }
})
*export default class* Search *extends* Vue {
    news: Array<HackerNewsResult>;
}</pre>

它开始采取一些实际的行为,从行中可以看到发生了什么:我们从呈现的输入中流出DOM事件,我们选择它的值,并使用来执行针对端点的请求。根据文档,它开始采取一些实际的行为,从行中可以看到发生了什么:我们从呈现的输入中流出DOM事件,我们选择它的值,并使用SwitMap来执行针对端点的请求。根据文档将每个源值投影到可观察的可观测值,该值合并到可观测的输出中,仅从最近预测的可观测值中发出值。">将每个源值投影到可观察的可观测值,该值合并到可观测的输出中,仅从最近预测的可观测值中发出值。</trans>*<trans oldtip=" To clarify, the only latest promise will be tracked down till the end (resolved — rejected). Nice! " newtip="为了澄清,唯一的最新承诺将被追查到最后(解决-拒绝)。好的,漂亮的">为了澄清,唯一的最新承诺将被追查到最后(解决-拒绝)。好的,漂亮的

<figure style="display: block; margin: 0px 0px 1em; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(254, 254, 254); text-decoration-style: initial; text-decoration-color: initial;">

现在,最好只针对特定的时间窗口执行请求,而不是每次用户输入时都执行请求。为了完成这个案子,我们可以用">现在,最好只针对特定的时间窗口执行请求,而不是每次用户输入时都执行请求。为了完成这个案子,我们可以用借债时间,它从源发出一个值,只有在特定的时间跨度过去之后才能观察到,而没有另一个源发射。">它从源发出一个值,只有在特定的时间跨度过去之后才能观察到,而没有另一个源发射。</trans>

<trans oldtip="If you have already worked with Lodash/Underscore, “debounce” might sound familiar." newtip="如果您已经使用过Lodash/下划线,那么“退出”可能听起来很熟悉。">如果您已经使用过Lodash/下划线,那么“退出”可能听起来很熟悉。</trans>

import* { debounceTime, pluck, switchMap } *from* 'rxjs/operators';
@Component<Search>({
    subscriptions() {
        *return* ({
            news: *this*.$fromDOMEvent('input', 'keyup').pipe(
                **debounceTime(300),**
                pluck<Event, *string*>('target', 'value'),
                switchMap(value => *from*(
                    axios.get<HackerNewsSearchResponse>(`${hackerNewsEndpoint}${value}`)
                    )
                )
            )
        });
    }
})
*export default class* Search *extends* Vue {
    news: Array<HackerNewsResult>;
}</pre>

为了解决上面提到的问题,向管道中添加了为了解决上面提到的问题,向管道中添加了

(dueTime: *number*, scheduler: SchedulerLike = async): MonoTypeOperatorFunction<T> {
  *return* (source: Observable<T>) => source.lift(*new* DebounceTimeOperator(dueTime, scheduler));
}</pre>

作为参数传递给函数的数字表示时间窗口,以毫秒为单位。每次用户输入某项内容时,下一个值将在300 ms后发出(因此,它将聚合下一个条目,以防万一,并在所需的时间之后触发下一个值)">作为参数传递给函数的数字表示时间窗口,以毫秒为单位。每次用户输入某项内容时,下一个值将在300 ms后发出(因此,它将聚合下一个条目,以防万一,并在所需的时间之后触发下一个值)</trans>

<figure style="display: block; margin: 0px 0px 1em; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, STXihei, "Microsoft YaHei", 微软雅黑, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(254, 254, 254); text-decoration-style: initial; text-decoration-color: initial;">

image

<figcaption style="display: block; margin: auto; text-align: center;">http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-debounceTime</figcaption>

</figure>

快好了。现在,我们需要接受响应,使用有效的信息过滤结果并将其呈现给用户。">快好了。现在,我们需要接受响应,使用有效的信息过滤结果并将其呈现给用户。

<template>
 <div>
 <label *for=*"search">Search for something:</label>
 <input *type=*"text" *id=*"search" *class=*"input-field">
 ** <ul *v-for=*"item in news">
 <li *:key=*"item.objectID">
 <a *:href=*"item.url">{{ item.title }}</a>
 </li>
 </ul>**
 </div>
</template>

<script *lang=*"ts">
 ...</pre>

*import* { debounceTime, map, pluck, switchMap } *from* 'rxjs/operators';

    @Component<Search>({
 subscriptions(*this*: Vue) {
  *return* ({
 news: *this*.$fromDOMEvent('input', 'keyup').pipe(
 debounceTime(300),
 pluck<Event, *string*>('target', 'value'),
 switchMap(value => *from*(
 axios.get<HackerNewsSearchResponse>(`${hackerNewsEndpoint}${value}`)
 )
 ),
  **pluck<AxiosResponse, Array<HackerNewsResult>>('data', 'hits'),
 map((results: Array<HackerNewsResult>) => results.filter((news: HackerNewsResult) => Boolean(news.title && news.url)))**
                )
            });
        }
    })
    *export default class* Search *extends* Vue {
        news: Array<HackerNewsResult> = [];
    }

首先,我在管道中增加了两个额外的步骤:">首先,我在管道中增加了两个额外的步骤

考虑到来自Axios承诺的成功响应,我们从JSON“data”中选择“HITS”(这包含基于查询的结果);">考虑到来自Axios承诺的成功响应,我们从JSON“data”中选择“HITS”(这包含基于查询的结果);后来,我介绍了">后来,我介绍了地图遍历流中的每个值(包含结果的数组),只过滤包含有效标题和URL的有效值;">遍历流中的每个值(包含结果的数组),只过滤包含有效标题和URL的有效值;</trans>

为了呈现数组,我在模板中创建了一个简单的列表,它迭代新闻数组,并呈现一个简单的链接,显示新闻标题并在href中实现URL。">为了呈现数组,我在模板中创建了一个简单的列表,它迭代新闻数组,并呈现一个简单的链接,显示新闻标题并在href中实现URL。

就这样IMHO,它看起来非常整洁和“容易”阅读。我知道这可能不是简单的,但是一旦熟悉了RxJS和反应性方法,维护/测试就变得更加容易了。现在停下来好好想想。传统的声明式方法很容易出错,而可维护性/测试将处于边缘。">就这样IMHO,它看起来非常整洁和“容易”阅读。我知道这可能不是简单的,但是一旦熟悉了RxJS和反应性方法,维护/测试就变得更加容易了。现在停下来好好想想。传统的声明式方法很容易出错,而可维护性/测试将处于边缘。</trans>

您还可以添加额外的步骤,例如">现在,感谢RxJS,您还可以添加额外的步骤,"如果出现错误和许多其他特性。此外,如果您热衷于测试流,我建议您使用">如果出现错误和许多其他特性。此外,如果您热衷于测试流,我建议您使用大理石测试并对所观察到的结果进行测试,得到一定的结果。">并对所观察到的结果进行测试,得到一定的结果。

interface* HandleObservableOptions {
    time?: *number*;
    scheduler?: SchedulerLike;
}

*export const* handleObservable = *function* (observable: Observable<Event>, options: HandleObservableOptions = {}): Observable<Array<HackerNewsResult>> {
    *const* { time = 300, scheduler } = options;
     *return* observable.pipe(
       debounceTime(time, scheduler),
       ...    
    );
};</pre>

[https://github.com/ReactiveX/rxjs/blob/master/doc/marble-testing.md](https://github.com/ReactiveX/rxjs/blob/master/doc/marble-testing.md)</pre>

// This test will actually run *synchronously*
it('generate the stream correctly', () => {
  scheduler.run(helpers => {
    const { cold, expectObservable, expectSubscriptions } = helpers;
    const obs =  handleObservable(cold('-a--b--c---|'), { 
      time: 300, 
      scheduler
    });
    const subs =     '^----------!';
    const expected = '-a-----c---|';

    expectObservable(obs).toBe(expected);
    expectSubscriptions(obs.subscriptions).toBe(subs);
  });

上面的片段仅仅是测试看起来的一个例子(注意:根据您的环境,真正的测试可能略有不同)。多亏了大理石策略,才有可能测试你能想到的所有可能的路径。也许这是下一篇()文章的一个有效主题,因为有很多事情要讨论。">上面的片段仅仅是测试看起来的一个例子(注意:根据您的环境,真正的测试可能略有不同)。多亏了大理石策略,才有可能测试你能想到的所有可能的路径。也许这是下一篇()文章的一个有效主题,因为有很多事情要讨论。</trans>

<trans oldtip="Anyway, the complete solution might look like this." newtip="无论如何,完整的解决方案可能是这样的。">无论如何,完整的解决方案可能是这样的。</trans>

<template>
 <div>
 <label *for=*"search">Search for something:</label>
 <input *type=*"text" *id=*"search" *class=*"input-field">
 <ul *v-for=*"item in news">
 <li *:key=*"item.objectID">
 <a *:href=*"item.url">{{ item.title }}</a>
 </li>
 </ul>
 </div>
</template>

<script *lang=*"ts">
  *import* axios, { AxiosResponse } *from* 'axios';
  *import* { Component, Vue } *from* 'vue-property-decorator';
  *import* { *from*, Observable, SchedulerLike } *from* 'rxjs';
  *import* { debounceTime, map, pluck, switchMap } *from* 'rxjs/operators';

  *interface* HackerNewsResult {
 objectID: *string*;
 title?: *string*;
 url?: *string*;
 }

  *interface* HackerNewsSearchResponse {
 hits: Array<HackerNewsResult>
 }

  *interface* HandleObservableOptions {
 time?: *number*;
 scheduler?: SchedulerLike;
 }

  *const* hackerNewsEndpoint: *string* = 'http://hn.algolia.com/api/v1/search?query=';

  *export const* handleObservable = *function* (observable: Observable<Event>, options: HandleObservableOptions = {}): Observable<Array<HackerNewsResult>> {
  *const* { time = 300, scheduler } = options;
  *return* observable.pipe(
 debounceTime(time, scheduler),
 pluck<Event, *string*>('target', 'value'),
 switchMap(value => *from*(
 axios.get<HackerNewsSearchResponse>(`${hackerNewsEndpoint}${value}`)
 )
 ),
 pluck<AxiosResponse, Array<HackerNewsResult>>('data', 'hits'),
 map((results: Array<HackerNewsResult>) => results.filter((news: HackerNewsResult) => Boolean(news.title && news.url)))
 );
 };

 @Component<Search>({
 subscriptions(*this*: Vue) {
  *return* ({
 news: handleObservable(*this*.$fromDOMEvent('input', 'keyup'))
 });
 }
 })
  *export default class* Search *extends* Vue {
 news: Array<HackerNewsResult> = [];
 }

<style *scoped*>
 .input-field {
 width: 50%;
 }

这可能是一个起点,许多东西可能会得到增强,但我建议您在应用程序中考虑这个策略,并在代码中替换疯狂的流。">这可能是一个起点,许多东西可能会得到增强,但我建议您在应用程序中考虑这个策略,并在代码中替换疯狂的流。

干杯

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,462评论 25 707
  • 古典音乐是一个独立的流派,艺术手法讲求洗练,追求理性地表达情感,内涵深刻,能发人深思,更能使人高尚,免于低俗。并历...
    八度黑白阅读 3,507评论 2 51
  • 我们吃饭的时候,知道吃麻辣要找川渝,吃香辣找湖南菜,吃清凉爽口的甜品找港式......因为一方水土养一方食,这块地...
    乐小饮阅读 309评论 0 3
  • (五) 像每一个开学一样,教室里叽叽喳喳声一片,仿佛自己真的很久很久没有见到彼此一样,恨不得将自己一整个假期的...
    沈白_阅读 155评论 0 0