現(xiàn)學(xué)現(xiàn)賣微信小程序開發(fā)(三):引入“Rx”刀疙,為小程序插上翅膀

現(xiàn)學(xué)現(xiàn)賣微信小程序開發(fā)(一)
現(xiàn)學(xué)現(xiàn)賣微信小程序開發(fā)(二)
現(xiàn)學(xué)現(xiàn)賣微信小程序開發(fā)(三):引入Rx,為小程序插上翅膀

引入“Rx”,為小程序插上翅膀

對(duì)于我這種“不用Rx會(huì)死星人“來說籍救,一個(gè)平臺(tái)如果沒有Rx,簡直痛苦死了渠抹。所以一直在研究怎么把RxJS引入到微信小程序中蝙昙,這幾天終于有了階段性成果闪萄,那“Rx”為什么加引號(hào)?嗯奇颠,這是個(gè)好問題败去,原因是。烈拒。为迈。經(jīng)過幾天的艱苦奮戰(zhàn),我終于還是沒有找到把RxJS庫正確引入到微信小程序的方法缺菌。所以呢葫辐,我找了一個(gè)替代品:xstream ( https://github.com/staltz/xstream )。這個(gè)類庫呢伴郁,和RxJS差不多耿战,更輕量級(jí),因?yàn)槿サ袅撕枚嗖怀S玫暮椭貜?fù)的操作符焊傅,當(dāng)然寫法上也略有區(qū)別剂陡,感覺其實(shí)沒有RxJS爽,但問題不大狐胎。

xstream的引入

和網(wǎng)上的其他類庫的引入來比較的話鸭栖,xstream引入的步驟不算太煩:

  1. 找一個(gè)目錄,使用npm install xstream握巢。
  2. 在小程序工程目錄下新建一個(gè)libs目錄晕鹊,然后再建一個(gè)xstream目錄。
  3. 然后在 node_modules/xstream 目錄中把 index.js 拷貝到 libs/xstream下暴浦。
  4. 由于這個(gè)文件依賴 symbol-observable溅话,而 symbol-observable 又依賴 ponyfill,所以我們?nèi)?node_modules/symbol-observable/lib中把 index.jsponyfill.js 都拷貝到libs/xstream下歌焦。哦飞几,對(duì)了,要把 index.js 改名成 symbol-observable.js 要不就和上面重名了独撇。
  5. 這樣的話xstream的core就可以正常工作了屑墨,但如果你需要一些其他操作符,比如debounce等纷铣,可以去 node_modules/xstream/extra 中找卵史,找到后把js文件(比如debounce.js)拷貝到 libs/xstream/extra中。
一些額外的操作符可以去xstream的extra目錄中尋找
一些額外的操作符可以去xstream的extra目錄中尋找

好了关炼,xstream的引入至此已經(jīng)完畢程腹,我們看看怎么使用。

引入完畢后的目錄結(jié)構(gòu)
引入完畢后的目錄結(jié)構(gòu)

先來體驗(yàn)一下什么是流式編程儒拂,在 pageParams.onLoad 中加上如下代碼寸潦,當(dāng)然別忘了引入xs import xs from '../../libs/xstream/index'

  // 每隔1秒計(jì)數(shù)加1,
  // 過濾出偶數(shù)
  // 將數(shù)字轉(zhuǎn)換為其平方
  // 5秒后結(jié)束

  let stream = xs.periodic(1000)
    .filter(i => i % 2 === 0)
    .map(i => i * i)
    .endWhen(xs.periodic(5000).take(1))

  // 到目前為止,stream還處于idle狀態(tài)
  // 從有第一個(gè)訂閱者開始社痛,它就處于激活狀態(tài)了

  stream.addListener({
    next: i => console.log(i),
    error: err => console.error(err),
    complete: () => console.log('completed'),
  })

到Console中看一下见转,輸出結(jié)果為0,4蒜哀,completed

console中的輸出
console中的輸出

我們來手動(dòng)復(fù)原一下過程斩箫,首先 xs.periodic(1000) 是這樣一個(gè)流

 periodic(1000)
---0---1---2---3---4---...

第一秒是發(fā)射0,0是偶數(shù)滿足filter條件撵儿,進(jìn)入轉(zhuǎn)換0的平方還是0乘客,結(jié)束條件未滿足,于是輸出0淀歇;第二秒時(shí)發(fā)射1易核,1為奇數(shù)被淘汰;第三秒時(shí)發(fā)射2浪默,2是偶數(shù)滿足filter條件牡直,進(jìn)入轉(zhuǎn)換2的平方是4,結(jié)束條件未滿足纳决,于是輸出4碰逸;第四秒時(shí)發(fā)射3,3為奇數(shù)被淘汰阔加;第五秒時(shí)輸出4饵史,4是偶數(shù)滿足filter條件,進(jìn)入轉(zhuǎn)換4的平方是16胜榔,但可惜結(jié)束條件已滿足约急,輸出completed。

這個(gè)小例子雖然簡單苗分,但是涉及到了多個(gè)流式編程的操作符厌蔽,這種串(chain)起來的感覺真是很爽。

微信小程序中的響應(yīng)式編程

由于微信小程序的基于回調(diào)函數(shù)的設(shè)計(jì)摔癣,我們需要對(duì)其API進(jìn)行封裝后使其具備響應(yīng)式編程的能力奴饮。那么我們就拿本次的 todos.onLoad 來練手,沒用xstream之前是下面的樣子择浊。

pageParams.onLoad = function () {
  const that = this
  wx.request({
    url: URL,
    data: JSON.stringify({}),
    header: { 'content-type': 'application/json' },
    method: 'GET',
    success: res => {
      console.log(res.data)
      that.setData({
        todos: res.data
      })
    },
    fail: () => console.error('something is wrong'),
    complete: () => console.log('get req completed')
  })
}

我們來用xstream改造一下吧

import xs from '../../lib/xstream/index'

pageParams.onLoad = function () {
  const that = this
  const producer = {
    start: listener => {
      start: wx.request({
        url: URL,
        data: JSON.stringify({}),
        header: { 'content-type': 'application/json' },
        method: 'GET',
        success: res => listener.next(res),
        fail: () => listener.error('something is wrong'),
        complete: () => listener.complete()
      })
     },
  stop: () => {}
 }
 let http$ = xs.create(producer)
 http$.subscribe({
   next: res => that.setData({
     todos: res.data
   }),
   error: console.log('http request failed'),
   complete: console.log('http request completed')
 })
}

我勒個(gè)去戴卜,這比原來代碼還多,搞什么琢岩?先別急投剥,我們仔細(xì)想想其實(shí)前面的一大部分代碼是在將傳統(tǒng)的函數(shù)改造成流式的函數(shù)。這些改造工作如果在普通的HTML+Javascript環(huán)境中是很好解決的担孔,因?yàn)椴徽撌荝xJS還是xstream都提供了諸如 fromfromEvent 等轉(zhuǎn)換類操作符可以方便的幫我們進(jìn)行這種傳統(tǒng)到流式的轉(zhuǎn)換江锨。但現(xiàn)在不行啊吃警,這些老外的類庫寫的時(shí)候肯定不會(huì)考慮微信的,那怎么辦啄育?只好自己寫吧酌心。

還是拿這個(gè)例子練手,我們創(chuàng)建一個(gè)叫 http.js 的文件挑豌,在這里面我們對(duì)應(yīng)4種request方法(GET安券,POST,PUT氓英,DELETE)分別構(gòu)造了專門的函數(shù)用語轉(zhuǎn)換侯勉。

import xs from '../lib/xstream/index'

const REQ_METHOD = {
  GET: 'GET',
  POST: 'POST',
  PUT: 'PUT',
  DELETE: 'DELETE'
}

let http =  {}

http.get = (url, data={}, header={'content-type': 'application/json'}) => {
  return http_request(url, REQ_METHOD.GET, data, header) 
}

http.post = (url, data={}, header={'content-type': 'application/json'}) => {
  return http_request(url, REQ_METHOD.POST, data, header)
}

http.put = (url, data={}, header={'content-type': 'application/json'}) => {
  return http_request(url, REQ_METHOD.PUT, data, header)
}

http.delete = (url, data={}, header={'content-type': 'application/json'}) => {
  return http_request(url, REQ_METHOD.DELETE, data, header)
}

function http_request(
  url, 
  method=REQ_METHOD.GET, 
  data={}, 
  header={'content-type': 'application/json'}) {
  const producer = {
    start: listener => {
      wx.request({
        url: url,
        data: JSON.stringify(data),
        header: header,
        method: method, 
        success: res => listener.next(res),
        fail: () => listener.error(`http request failed: ${url} | method: ${method} | data: ${data} | header: ${header}`),
        complete: () => listener.complete()
      })
    },
    stop: () => {}
  }
  return xs.create(producer)
}

module.exports = {
  http: http
}

這樣一個(gè)工具類建好之后呢,我們的 onLoad 函數(shù)就變得很簡單了是不是铝阐?

pageParams.onLoad =  function() {
  const that = this
  http.get(URL).subscribe({
    next: res => that.setData({
      todos: res.data
    }),
    error: err => console.error(err),
    complete: () => console.info('Todos--get completed')
  })
}

你想了一下跟我說址貌,你特么唬我是不是,我不用xstream也可以這樣封裝饰迹,代碼也會(huì)簡潔很多啊芳誓。別急別急,我們費(fèi)這么大勁把它轉(zhuǎn)換成流式函數(shù)啊鸭,不是只是為了簡潔锹淌,而是我們可用使用響應(yīng)式編程的很多特性了。比如上面的代碼我們加一個(gè)需求赠制,在出錯(cuò)后再進(jìn)行若干次重試赂摆,但一切要控制在一個(gè)超時(shí):比如10秒內(nèi)。這個(gè)需求其實(shí)還是挺常見的钟些,但是常規(guī)寫法是比較痛苦的烟号。我們看看用響應(yīng)式編程方式怎么做。

let demo$ = xs.periodic(1000)
  .map(x => {
    const i = Math.floor((Math.random() * 10) + 1);
    if(x > i)
      x.throw(new Error('something is wrong'))
    return x
  })
demo$
  .replaceError((err) => demo$)
  .endWhen(xs.periodic(10000))
  .subscribe({
    next: x => console.log(x),
    error: err => console.warn(err),
    complete: () => console.info('I am completed')
  })

上面代碼中我們每隔一秒( periodic(1000) )輸出一個(gè)從0開始每次增長1的自然數(shù)政恍,然后在轉(zhuǎn)換函數(shù)中生成一個(gè)1-10的隨機(jī)數(shù)汪拥,如果前面數(shù)據(jù)流發(fā)射的數(shù)大于這個(gè)隨機(jī)數(shù),我們就手動(dòng)拋出一個(gè)異常篙耗,反之原樣返回這個(gè)數(shù)字迫筑。定義好這個(gè)數(shù)據(jù)流后,我們按需求進(jìn)行處理:

第一個(gè)需求:遇到異常應(yīng)該重試宗弯,那我們使用 replaceError((err) => demo$)脯燃,每次遇到異常,我們都再執(zhí)行一遍前面的數(shù)據(jù)流蒙保。
第二個(gè)需求:我們應(yīng)該控制超時(shí)時(shí)間10秒辕棚,所以使用 .endWhen(xs.periodic(10000))

這樣就輕松的解決了這個(gè)問題,我們來看看輸出,一開始從0-3是比較正常逝嚎,然后程序拋出了異常扁瓢。我們的 replaceError((err) => demo$) 捕獲到這個(gè)異常并且用 demo$ 替換錯(cuò)誤,也就是說再次執(zhí)行懈糯。慢著涤妒,那不是死循環(huán)了嗎单雾?沒事赚哗,我們后面有個(gè)退出條件就是10秒結(jié)束該流。

Console中demo$的輸出
Console中demo$的輸出

當(dāng)然需要注意一點(diǎn)硅堆,在xstream中所有的流默認(rèn)都是Hot Observable屿储。怎么理解這個(gè)概念呢,想象一下我們?cè)诳措娨曋辈ソヌ樱覀兯械娜瞬还苣闶鞘裁磿r(shí)候打開的電視够掠,我們開的內(nèi)容、進(jìn)度都是一樣的茄菊。但Cold Observable并不一樣疯潭,相當(dāng)于是網(wǎng)絡(luò)視頻,你看到第20分鐘面殖,但這個(gè)時(shí)候我打開還是從頭開始看竖哩。這個(gè)內(nèi)在含義我們舉一個(gè)小例子,下面是用RxJS寫的一個(gè)每隔1秒生成一個(gè)增長1的自然數(shù)流脊僚,第一個(gè)訂閱者立即開始相叁,另一個(gè)訂閱者2秒之后開始訂閱,我們會(huì)看到下面的景象

RxJS實(shí)現(xiàn)的Cold Obervable的效果
RxJS實(shí)現(xiàn)的Cold Obervable的效果

但同樣邏輯用xstream實(shí)現(xiàn)的下面代碼辽幌,出來的是另一番景象增淹。

  let demo$ = xs.periodic(1000)

  demo$.addListener({
    next: x => console.log(x)
  })

  setTimeout(()=>{
    demo$.addListener({
      next: x => console.log(x)
    })
  }, 2000)
用xstream生成的Hot Observable的效果
用xstream生成的Hot Observable的效果

當(dāng)然在很多場(chǎng)景中,這種差別不會(huì)帶來本質(zhì)的變化乌企,比如http請(qǐng)求虑润,本身就是一次性的請(qǐng)求,所以hot和cold的結(jié)果是一樣的加酵。當(dāng)然RxJS作為大而全的類庫是既支持Hot Observable又支持Cold Observable的拳喻。xstream的作者其實(shí)也是RxJS的contributor,但他認(rèn)為在web前端領(lǐng)域hot的應(yīng)用頻率遠(yuǎn)比cold要強(qiáng)虽画,所以做了這個(gè)精簡版的響應(yīng)式類庫舞蔽。

事件的處理

上述方法用于普通API的封裝一點(diǎn)問題也沒有,但是在做輸入事件時(shí)码撰,我遇到了一些小麻煩渗柿。當(dāng)然獲取輸入事件并不是很困難,微信小程序?qū)τ谳斎胧录慕壎ㄒ彩窃?wxml 中的 <input> 控件中用 bindinput 來指定一個(gè)eventHandler,這里我們起了個(gè)名叫 addTodo朵栖。

<input bindinput="addTodo" placeholder="What do you want to do today?"/>

標(biāo)準(zhǔn)的微信小程序可以這樣來寫事件處理颊亮。

pageParams.addTodo = function(event) {
  ...
}

如果要把事件截獲并以數(shù)據(jù)流輸出的話,我們需要在onLoad中進(jìn)行事件處理函數(shù)的定義陨溅,比如下面的代碼可以讓我們實(shí)現(xiàn)對(duì)于輸入事件的定義终惑,在其定義中我們其實(shí)使用了流數(shù)據(jù)的發(fā)射作為其函數(shù)體。

pageParams.onLoad =  function() {
  ...
  const evProducer = {
    start: listener => {
      this.addTodo = ev => {
        listener.next(ev.detail.value)
      }
    },
    stop: () => { }
  }
  const input$ = xs.create(evProducer)
}

這樣封裝后门扇,我們可以使用一些操作符來實(shí)現(xiàn)雹有,比如濾波器等功能,下面的代碼片段就是濾掉快速輸入時(shí)(小于400毫秒)的事件臼寄。

  input$.compose(debounce(400)).subscribe({
    next: val => console.log(val)
  })

但上面形式的封裝有個(gè)問題就是我們要把這個(gè)封裝提取出來作為一個(gè)單獨(dú)函數(shù)時(shí)霸奕,由于this.addTodo仍為初始化,無法作為參數(shù)傳遞吉拳,而且這個(gè) addTodo 我也不想寫死质帅。怎么辦好呢?我試了幾種方案后采用了使用 Object.defineProperty 的形式去動(dòng)態(tài)定義pageParams對(duì)象的命名屬性留攒,當(dāng)然還是有一些問題煤惩,仍然需要給這些方法一個(gè)初始值(有同學(xué)如果有更好的建議請(qǐng)指教)。下面就是目前實(shí)現(xiàn)的抽象封裝代碼炼邀,在下面的代碼中魄揉,由于我們對(duì)外發(fā)射的是事件(event),所以其實(shí)它不光可以用于輸入事件汤善,理論上任意事件都可以什猖。也就是說我們自己實(shí)現(xiàn)了類似 Rx.Observable.fromEvent 的功能。

import xs from '../lib/xstream/index'

let event = {}

event.fromEvent = (srcObj, propertyName) => {
  const evProducer = {
    start: (listener) => {
      Object.defineProperty(
        srcObj, 
        propertyName, 
        {value: ev => listener.next(ev)})
    },
    stop: () => {}
  }
  return xs.create(evProducer)
}

module.exports = {
  event: event
}

Todo的完善

按著我們上面的封裝红淡,現(xiàn)在的 todos.js 看起來是這個(gè)樣子不狮。我們可以看到和Web API交互的部分都不在Page中了,雖然微信中不支持service在旱,但我們完全可以另寫一個(gè)文件存儲(chǔ)Web API的交互摇零。

import { xs, http, event, debounce } from '../../wxstream/index'
const URL = 'http://localhost:3000/todos'

// 非常遺憾的是目前仍需要初始化event handler,否則會(huì)出現(xiàn)undefined錯(cuò)誤
let pageParams = {
  data: { todos: [] },
  addTodo: () => {},
  changeText: () => {},
  removeTodo: () => {},
  toggleTodo: () => {}
}

// 獲得所有todos的流
const todos$ = http.get(URL).map(res => res.data)

// 輸入框的文本變化事件流
const input$ = event
  .fromEvent(pageParams, 'changeText')
  .compose(debounce(500))
  .map(ev => ev.detail.value)

// 添加按鈕的點(diǎn)觸事件流
const addTodo$ = event
  .fromEvent(pageParams, 'addTodo')
  .mapTo(null) //null because we do NOT care about the value

// 根據(jù)按鈕的點(diǎn)擊和輸入合并成一個(gè)新的流桶蝎,提交服務(wù)器產(chǎn)生新的Todo
const newTodo$ = xs.combine(input$, addTodo$)
  .map(([input, click]) => {
    const todoToBeAdded = {
      desc: input,
      completed: false
    }
    return http.post(URL, todoToBeAdded)
  })
  .flatten()

// 監(jiān)視toggleTodo事件驻仅,該事件發(fā)生后提交服務(wù)器更新該Todo
const toggleTodo$ = event
  .fromEvent(pageParams, 'toggleTodo')
  .map(ev => ev.target.dataset.todo)
  .map(todo => {
    const url = `${URL}/${todo.id}`
    const updatedTodo = Object.assign({}, todo, { completed: !todo.completed })
    return http.put(url, updatedTodo).mapTo(updatedTodo)
  })
  .flatten()

// 監(jiān)視r(shí)emoveTodo事件,該事件發(fā)生后提交服務(wù)器更新該Todo
const removeTodo$ = event
  .fromEvent(pageParams, 'removeTodo')
  .map(ev => ev.target.dataset.todo)
  .map(todo => {
    const url = `${URL}/${todo.id}`;
    return http.delete(url, todo).mapTo(todo)
  })
  .flatten()

let sub_todos, sub_new, sub_toggle, sub_remove 

// 現(xiàn)在頁面邏輯中沒有服務(wù)端API的交互了登渣,只有對(duì)成員數(shù)組的控制
pageParams.onLoad = function() {
  const that = this
  sub_todos = todos$.subscribe({
    next: todos => that.setData({
      todos: todos
    }),
    error: err => {console.log(err)},
    complete: () => {console.log('Todos--get completed')}
  })

  sub_new = newTodo$.subscribe({
    next: res => that.setData({
      todos: [
        ...that.data.todos,
        res.data
      ]
    })
  })

  sub_toggle = toggleTodo$.subscribe({
    next: value => that.setData({
      todos: that.data.todos.map(todo => {
        if (todo.id === value.id) {
          return value
        }
        return todo
      })
    }),
    error: err => console.error(err),
    complete: () => console.info('Todos--toggle completed')
  })

  sub_remove = removeTodo$.subscribe({
    next: value => that.setData({
      todos: that.data.todos.filter(todo => todo.id !== value.id)
    }),
    error: err => console.error(err),
    complete: () => console.info('Todos--toggle completed')
  })
}

// 取消訂閱噪服,釋放內(nèi)存
pageParams.onUnload = () => {
  if(sub_todos !== null)
    sub_todos.unsubscribe()
  if(sub_new !== null)
    sub_new.unsubscribe()
  if(sub_toggle !== null)
    sub_toggle.unsubscribe()
  if(sub_remove !== null)
    sub_remove.unsubscribe()
}

Page(pageParams)

wxstream(微信小程序的流式封裝)

我為了這件事,建了一個(gè)Github項(xiàng)目叫 wxstreamhttps://github.com/wpcfan/wxstream ) 就是“微信stream”的縮寫胜茧。只要把這個(gè)項(xiàng)目拉下來粘优,拷貝到微信小程序目錄仇味,就立即可用了,包括xstream的支持都在里面了雹顺。目前還沒什么文檔丹墨,接口也大部分都沒測(cè)過呢,實(shí)在汗顏嬉愧。后續(xù)我逐漸添加文檔和進(jìn)行測(cè)試吧贩挣,現(xiàn)在只是個(gè)骨架,大家也幫忙測(cè)一下吧;-)没酣。我看看過幾天有時(shí)間再研究一些redux怎么在微信小程序中使用王财。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市四康,隨后出現(xiàn)的幾起案子搪搏,更是在濱河造成了極大的恐慌狭握,老刑警劉巖闪金,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異论颅,居然都是意外死亡哎垦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門恃疯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏设,“玉大人,你說我怎么就攤上這事今妄≈?冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵盾鳞,是天一觀的道長犬性。 經(jīng)常有香客問我,道長腾仅,這世上最難降的妖魔是什么乒裆? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮推励,結(jié)果婚禮上鹤耍,老公的妹妹穿的比我還像新娘。我一直安慰自己验辞,他們只是感情好稿黄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跌造,像睡著了一般杆怕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上客叉,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天遂蛀,我揣著相機(jī)與錄音料扰,去河邊找鬼界斜。 笑死苏遥,一個(gè)胖子當(dāng)著我的面吹牛厕怜,可吹牛的內(nèi)容都是我干的侮东。 我是一名探鬼主播墩瞳,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伟姐,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼收苏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起愤兵,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鹿霸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后秆乳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懦鼠,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年屹堰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肛冶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扯键,死狀恐怖睦袖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荣刑,我是刑警寧澤馅笙,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站厉亏,受9級(jí)特大地震影響董习,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叶堆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一阱飘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧虱颗,春花似錦沥匈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畦粮,卻和暖如春散址,著一層夾襖步出監(jiān)牢的瞬間乖阵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工预麸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞪浸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓吏祸,卻偏偏與公主長得像对蒲,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贡翘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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