React@15.6.2源碼解析---從 ReactDOM.render 到頁面渲染(4)Transaction

英文注釋翻譯

React 中的 Transaction 創(chuàng)建一個(gè)黑盒環(huán)境來對方法進(jìn)行封裝侣夷,它能夠封裝任何方法,以便在調(diào)用方法之前和之后維護(hù)某些不變量(即使在調(diào)用封裝的方法時(shí)拋出異常)。

任何實(shí)例化事務(wù)的人都可以在創(chuàng)建時(shí)提供不變量的實(shí)施者恬试∷裥觯“Transaction”類本身將為您提供一個(gè)額外的自動(dòng)不變量(_isInTransaction)——任何事務(wù)實(shí)例在已經(jīng)運(yùn)行時(shí)不應(yīng)該運(yùn)行這個(gè)不變量。您通常會(huì)創(chuàng)建一個(gè)“事務(wù)”的單一實(shí)例推正,以便多次重用恍涂,該實(shí)例可能用于包裝幾個(gè)不同的方法。包裝器非常簡單——它們只需要實(shí)現(xiàn)兩個(gè)方法植榕。

使用范圍

  1. 保存‘調(diào)和’前后的輸入選擇范圍再沧。恢復(fù)選擇尊残,即使在發(fā)生意外錯(cuò)誤的情況下炒瘸。
  2. 重排Dom時(shí),禁用事件寝衫,避免引發(fā)多余的 blur/focus 事件顷扩,同時(shí)確保重排之后重新激活事件系統(tǒng)
  3. work線程調(diào)和后,由主線程更新UI
  4. 渲染完成之后慰毅,調(diào)用所有的componentDidUpdate
  5. (Future case)包裝“ReactWorker”隊(duì)列的特定刷新隘截,以保存“scrollTop”(自動(dòng)滾動(dòng)感知DOM)。
  6. (Future case)DOM更新前后的布局計(jì)算汹胃。

事務(wù)性插件API

  1. 'initialize': 具有“初始化”方法的模塊婶芭,該方法返回任何預(yù)計(jì)算。
  2. 'close': 以及接受預(yù)計(jì)算的“close”方法统台〉窭蓿“close”在包裝的流程完成或失敗時(shí)調(diào)用。

自定義Transaction: 通過將Tracaction中的屬性通過Object.assign添加到自定義transaction的原型鏈中

個(gè)人理解

Transaction 對我們所要執(zhí)行的方法進(jìn)行一個(gè)封裝贱勃,創(chuàng)建了一個(gè)黑盒環(huán)境井赌。每個(gè) Transaction 都會(huì)被一個(gè)或多個(gè) wrapper 包裹,每一個(gè) wrapper 都包含兩個(gè)屬性贵扰,一個(gè)是initialize 一個(gè)是 close仇穗,當(dāng) transaction執(zhí)行的時(shí)候,首先會(huì)執(zhí)行所有的initialize并添加異常機(jī)制戚绕,之后執(zhí)行我們包裝的方法纹坐,最后在執(zhí)行所有的close并添加異常機(jī)制。

屬性概述
_isInTransaction:Transaction是否在運(yùn)行

reinitializeTransaction:初始化Transaction

getTransactionWrappers:獲取包裹的wrapper

isInTransaction:判斷當(dāng)前是否在運(yùn)行的方法

perform:Transaction的核心方法舞丛,用來執(zhí)行包裹的method

inistializeAll:執(zhí)行所有wrapperinitialize

closeAll:執(zhí)行所有wrapperclose

自定義Transaction

當(dāng)我們衍生出一個(gè)自定義Transaction時(shí)耘子,只需要使用Object.assign將Transaction的所有方法添加到我們自定義的Transaction上果漾,然后重寫getTransactionWrappers方法。

官方給的一個(gè)圖很形象

wrappers是在Transaction被創(chuàng)建時(shí)注入的谷誓,實(shí)質(zhì)上是通過上面說的通過Object.assign方法重寫getTransactionWrappers方法得到包裹的wrappers绒障,當(dāng)運(yùn)行perform方法時(shí),首先執(zhí)行的是所有的initialize之后是method最后是所有的close

Transaction

reinitializeTransaction

用于 Transaction 初始化捍歪,在實(shí)例化Transaction時(shí)户辱,都執(zhí)行了這個(gè)方法。

// Transaction.js
reinitializeTransaction: function () {
    this.transactionWrappers = this.getTransactionWrappers();
    if (this.wrapperInitData) {
      this.wrapperInitData.length = 0;
    } else {
      this.wrapperInitData = [];
    }
    this._isInTransaction = false;
  },

通過調(diào)用getTransactionWrappers方法糙臼,給Transaction實(shí)例添加transactionWrappers屬性庐镐,之后附加wrapperInitData為空數(shù)組,并將運(yùn)行標(biāo)志_isInTransaction設(shè)為false变逃。

其中wrapperInitData數(shù)組是用來存儲(chǔ)initialize的返回值的必逆,用原文注釋中的說法就是,存儲(chǔ)initialize返回的預(yù)計(jì)算數(shù)據(jù)韧献,在close方法中需要用到這些預(yù)計(jì)算末患,實(shí)質(zhì)上就是用來判斷是否出現(xiàn)了異常。

getTransactionWrappers

  // Transaction.js
  /**
   * @abstract
   * @return {Array<TransactionWrapper>} Array of transaction wrappers.
   */
  getTransactionWrappers: null,

這個(gè)屬性用來獲取Transaction外部的wrappers锤窑,在定義Transaction時(shí)璧针,會(huì)通過Object.assign方法重寫該屬性

isInTransaction

  // Transaction.js
  isInTransaction: function () {
    return !!this._isInTransaction;
  },

判斷當(dāng)前Transaction是否在執(zhí)行,使用!!兩個(gè)感嘆號渊啰,是可以做類型判斷

perform

  // Transaction.js
  perform: function (method, scope, a, b, c, d, e, f) {
    /* eslint-enable space-before-function-paren */
    !!this.isInTransaction() ? /**/
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      // Catching errors makes debugging more difficult, so we start with
      // errorThrown set to true before setting it to false after calling
      // close -- if it's still set to true in the finally block, it means
      // one of these calls threw.
      // 先設(shè)置為 true 在調(diào)用 close 之后設(shè)置 false探橱,如果最后還是 true 的話 那么中途出現(xiàn)了異常
      errorThrown = true;
      this.initializeAll(0);
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // If `method` throws, prefer to show that stack trace over any thrown
          // by invoking `closeAll`.
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          // Since `method` didn't throw, we don't want to silence the exception
          // here.
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },
  • Transaction的核心方法,用來執(zhí)行包裹的method绘证。

  • 首先會(huì)判斷當(dāng)前Transaction是否在運(yùn)行隧膏,是的話會(huì)報(bào)錯(cuò)

之后會(huì)將_isInTransaction設(shè)為true,表明該Transaction在執(zhí)行

  • 調(diào)用initializeAll(0)參數(shù)0表示從第一個(gè)wrapper開始運(yùn)行嚷那,0表示wrappers數(shù)組的下標(biāo)

  • errorThrown 用來判斷執(zhí)行method期間是否出現(xiàn)了異常

  • 執(zhí)行method

  • 執(zhí)行所有的close

  • _isInTransaction 設(shè)為false 表明Transaction執(zhí)行完成

  • 返回method 的返回值

initializeAll

  // Transaction.js 
  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        // Catching errors makes debugging more difficult, so we start with the
        // OBSERVED_ERROR state before overwriting it with the real return value
        // of initialize -- if it's still set to OBSERVED_ERROR in the finally
        // block, it means wrapper.initialize threw.
        this.wrapperInitData[i] = OBSERVED_ERROR;
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === OBSERVED_ERROR) {
          // The initializer for wrapper i threw an error; initialize the
          // remaining wrappers but silence any exceptions from them to ensure
          // that the first error is the one to bubble up.
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

執(zhí)行所有的initialize方法胞枕,這邊做了異常處理,異常邏輯為:將對應(yīng)的wrapperInitData設(shè)為OBSERVED_ERROR,之后將initialize的返回值設(shè)為新的wrapperInitData魏宽,最后判斷wrapperInitData的值腐泻,如果仍為OBSERVED_ERROR表明在執(zhí)行initialize的過程中發(fā)生了異常。

closeAll

執(zhí)行所有的close队询,其中的異常處理機(jī)制和initializeAll相類似

closeAll: function (startIndex) {
    !this.isInTransaction() ? /**/
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        // Catching errors makes debugging more difficult, so we start with
        // errorThrown set to true before setting it to false after calling
        // close -- if it's still set to true in the finally block, it means
        // wrapper.close threw.
        errorThrown = true;
        if (initData !== OBSERVED_ERROR && wrapper.close) {
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          // The closer for wrapper i threw an error; close the remaining
          // wrappers but silence any exceptions from them to ensure that the
          // first error is the one to bubble up.
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }

執(zhí)行完之后將wrapperInitData清空派桩。

ReactDefaultBatchingStrategyTransaction

ReactDefaultBatchingStrategy.js中定義了一個(gè)默認(rèn)批處理策略事務(wù)。

// ReactDefaultBatchingStrategy.js

function ReactDefaultBatchingStrategyTransaction() {
  this.reinitializeTransaction();
}

_assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  }
});

通過Object.assign將Transaction的屬性添加到自定義Transaction的原型鏈中蚌斩,并重寫getTransactionWrappers方法铆惑,并且在實(shí)例化時(shí),都會(huì)調(diào)用reinitializeTransaction方法進(jìn)行初始化,這就是自定義一個(gè)Transaction的過程员魏。

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

這個(gè)Transaction被兩個(gè)wrapper包裹丑蛤。他們的initialize方法都是空函數(shù),那么關(guān)注點(diǎn)就轉(zhuǎn)移到close方法逆趋,在該Transaction方法直接完之后盏阶,首先會(huì)將ReactDefaultBatchingStrategy.isBatchingUpdates設(shè)為false晒奕,之后就調(diào)用ReactUpdates.flushBatchedUpdates刷新更新隊(duì)列闻书。

ReactReconcileTransaction

調(diào)和事務(wù)

/**
 * Currently:
 * - The order that these are listed in the transaction is critical:
 * - Suppresses events.
 * - Restores selection range.
 *
 * Future:
 * - Restore document/overflow scroll positions that were unintentionally
 *   modified via DOM insertions above the top viewport boundary.
 * - Implement/integrate with customized constraint based layout system and keep
 *   track of which dimensions must be remeasured.
 *
 * @class ReactReconcileTransaction
 */
function ReactReconcileTransaction(useCreateElement) {
  this.reinitializeTransaction();
  // Only server-side rendering really needs this option (see
  // `ReactServerRendering`), but server-side uses
  // `ReactServerRenderingTransaction` instead. This option is here so that it's
  // accessible and defaults to false when `ReactDOMComponent` and
  // `ReactDOMTextComponent` checks it in `mountComponent`.`
  this.renderToStaticMarkup = false;
  this.reactMountReady = CallbackQueue.getPooled(null);
  this.useCreateElement = useCreateElement;
}

構(gòu)造函數(shù)內(nèi)部包含了三個(gè)屬性

  • renderToStaticMarkup:只有在服務(wù)器端運(yùn)行時(shí)用到

  • reactMountReady:通過CallbackQueue.getPooled獲取,實(shí)質(zhì)上返回一個(gè)CallbackQueue實(shí)例

  • useCreateElement:值為構(gòu)造函數(shù)實(shí)例化時(shí)傳入的參數(shù)

調(diào)和事務(wù)的屬性稍微多了一點(diǎn)脑慧,通過混合Mixin注入原型鏈

var Mixin = {
  /**
   * @see Transaction
   * @abstract
   * @final
   * @return {array<object>} List of operation wrap procedures.
   *   TODO: convert to array<TransactionWrapper>
   */
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  },

  /**
   * @return {object} The queue to collect `onDOMReady` callbacks with.
   */
  getReactMountReady: function () {
    return this.reactMountReady;
  },

  /**
   * @return {object} The queue to collect React async events.
   */
  getUpdateQueue: function () {
    return ReactUpdateQueue;
  },

  /**
   * Save current transaction state -- if the return value from this method is
   * passed to `rollback`, the transaction will be reset to that state.
   */
  checkpoint: function () {
    // reactMountReady is the our only stateful wrapper
    return this.reactMountReady.checkpoint();
  },

  rollback: function (checkpoint) {
    this.reactMountReady.rollback(checkpoint);
  },

  /**
   * `PooledClass` looks for this, and will invoke this before allowing this
   * instance to be reused.
   */
  destructor: function () {
    CallbackQueue.release(this.reactMountReady);
    this.reactMountReady = null;
  }
};

這邊提到了ReactUpdateQueue魄眉,也是一個(gè)重點(diǎn)。

外部包含了三個(gè)wrapper

var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];

// 確保下拉框選擇的數(shù)據(jù)在transaction后仍被選擇
var SELECTION_RESTORATION = {
  /**
   * @return {Selection} Selection information.
   */
  initialize: ReactInputSelection.getSelectionInformation,
  /**
   * @param {Selection} sel Selection information returned from `initialize`.
   */
  close: ReactInputSelection.restoreSelection
};

// 抑制可能由于高級DOM操作(如臨時(shí)從DOM中刪除文本輸入)而意外分派的事件(blur/focus)闷袒。
var EVENT_SUPPRESSION = {
  /**
   * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
   * the reconciliation.
   */
  initialize: function () {
    var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
    ReactBrowserEventEmitter.setEnabled(false);
    return currentlyEnabled;
  },

  /**
   * @param {boolean} previouslyEnabled Enabled status of
   *   `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
   *   restores the previous value.
   */
  close: function (previouslyEnabled) {
    ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
  }
};

// 提供一個(gè)隊(duì)列坑律,來收集在 componentDidMount 和 componentDidUpdate 回調(diào)
// 在執(zhí)行事務(wù)期間,遇到這兩個(gè)生命周期囊骤,會(huì)將回調(diào)押入到隊(duì)列中晃择。在close的時(shí)候在去執(zhí)行
var ON_DOM_READY_QUEUEING = {
  /**
   * Initializes the internal `onDOMReady` queue.
   */
  initialize: function () {
    this.reactMountReady.reset();
  },

  /**
   * After DOM is flushed, invoke all registered `onDOMReady` callbacks.
   */
  close: function () {
    this.reactMountReady.notifyAll();
  }
};
_assign(ReactReconcileTransaction.prototype, Transaction, Mixin);

PooledClass.addPoolingTo(ReactReconcileTransaction);

再往下就是這兩句,第一句是將Transaction,Mixin都添加到原型鏈中也物。

第二句是調(diào)用PooledClass.addPoolingTo用到了PoolClass屬性宫屠。

PoolClass

PoolClass主要目的就是減少內(nèi)存消耗。

// PoolClass.js
var PooledClass = {
  addPoolingTo: addPoolingTo,
  oneArgumentPooler: oneArgumentPooler,
  twoArgumentPooler: twoArgumentPooler,
  threeArgumentPooler: threeArgumentPooler,
  fourArgumentPooler: fourArgumentPooler
};
// PoolClass.js
var addPoolingTo = function (CopyConstructor, pooler) {
  // Casting as any so that flow ignores the actual implementation and trusts
  // it to match the type we declared
  var NewKlass = CopyConstructor;
  NewKlass.instancePool = [];
  NewKlass.getPooled = pooler || DEFAULT_POOLER;
  if (!NewKlass.poolSize) {
    NewKlass.poolSize = DEFAULT_POOL_SIZE;
  }
  NewKlass.release = standardReleaser;
  return NewKlass;
};

通過addPoolingTo方法給傳入的構(gòu)造函數(shù)添加instancePool屬性和getPooled滑蚯,以及一個(gè)釋放池的函數(shù)NewKlass.release = standardReleaser浪蹂,默認(rèn)池大小為10。默認(rèn)的getPooled是帶一個(gè)參數(shù)的池函數(shù)告材。PoolClass提供了很多個(gè)池函數(shù)坤次,他們的區(qū)別就是參數(shù)的多少,默認(rèn)是一個(gè)參數(shù)的斥赋。

// PoolClass.js
var oneArgumentPooler = function (copyFieldsFrom) {
  var Klass = this;
  if (Klass.instancePool.length) {
    var instance = Klass.instancePool.pop();
    Klass.call(instance, copyFieldsFrom);
    return instance;
  } else {
    return new Klass(copyFieldsFrom);
  }
};

這邊可以看到如果池子里有的話缰猴,那么就直接彈出一個(gè)然后重新實(shí)例化,沒有的話則重新創(chuàng)建疤剑。使用池來儲(chǔ)存滑绒,減少內(nèi)存消耗。

所以這邊看到getPooled方法那就是返回一個(gè)實(shí)例調(diào)用者的實(shí)例骚露。

ReactUpdatesFlushTransaction

更新刷新事務(wù)蹬挤,定義在ReactUpdates.js中,在執(zhí)行ReactUpdates.flushBatchedUpdates時(shí)開啟棘幸。

// ReactUpdates.js
function ReactUpdatesFlushTransaction() {
  this.reinitializeTransaction();
  this.dirtyComponentsLength = null;
  this.callbackQueue = CallbackQueue.getPooled();
  this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
}

_assign(ReactUpdatesFlushTransaction.prototype, Transaction, {
  getTransactionWrappers: function () {
    return TRANSACTION_WRAPPERS;
  },

  destructor: function () {
    this.dirtyComponentsLength = null;
    CallbackQueue.release(this.callbackQueue);
    this.callbackQueue = null;
    ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
    this.reconcileTransaction = null;
  },

  perform: function (method, scope, a) {
    // Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
    // with this transaction's wrappers around it.
    return Transaction.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a);
  }
});

PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);

需要注意的是這邊重寫了perform方法焰扳。并且使用PoolClass.addPoolingTo添加了池屬性。

外部包裹了兩個(gè)wrapper

var NESTED_UPDATES = {
  initialize: function () {
    this.dirtyComponentsLength = dirtyComponents.length;
  },
  close: function () {
    if (this.dirtyComponentsLength !== dirtyComponents.length) {
      // Additional updates were enqueued by componentDidUpdate handlers or
      // similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
      // these new updates so that if A's componentDidUpdate calls setState on
      // B, B will update before the callback A's updater provided when calling
      // setState.
      // 額外的更新是由于 componentDidUpdate 或其他類似的操作。
      // 在 update_queue wrapper close之前 我們希望運(yùn)行這些新的更新以至于 如果 A 的componenDidUpdate 調(diào)用 B的 setState
      // 那么 B 會(huì)在 A 的updater提供的回調(diào)之前更新
      //
      // 在我們自己的UPDATE_QUEUEING包裝器關(guān)閉之前吨悍,我們希望運(yùn)行這些新更新
      // 以便如果A的componentDidUpdate在B上調(diào)用setState扫茅,那么B將在調(diào)用setState時(shí)提供回調(diào)A的updater之前更新。
      dirtyComponents.splice(0, this.dirtyComponentsLength);
      flushBatchedUpdates();
    } else {
      dirtyComponents.length = 0;
    }
  }
};

var UPDATE_QUEUEING = {
  initialize: function () {
    this.callbackQueue.reset();
  },
  close: function () {
    this.callbackQueue.notifyAll();
  }
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];

需要注意的是育瓜。在第一個(gè)wrapperNESTED_UPDATES中的initialize函數(shù)葫隙,初始化當(dāng)前實(shí)例的dirtyComponentsLength = dirtyComponents.length,之后在close的時(shí)候躏仇,發(fā)現(xiàn)他們兩長度不一樣了恋脚,說明在執(zhí)行method時(shí)引起了額外的更新。這個(gè)時(shí)候就需要刷新這些新的更新焰手。

而第二wrapperUPDATE_QUEUEING在會(huì)close的時(shí)候執(zhí)行callbackQueue.notifyAll糟描,這個(gè)方法是用來之前收集到的所有的回調(diào)。

這三個(gè)事務(wù)時(shí)目前我看到的三個(gè)书妻,后面如果有其他的我會(huì)在加以分析船响。

總結(jié)

這一篇是介紹 React 中的Transaction, 以及目前三種自定義的事務(wù)內(nèi)部的一些屬性及包裹的wrapper

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市躲履,隨后出現(xiàn)的幾起案子见间,更是在濱河造成了極大的恐慌,老刑警劉巖工猜,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件米诉,死亡現(xiàn)場離奇詭異,居然都是意外死亡域慷,警方通過查閱死者的電腦和手機(jī)荒辕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犹褒,“玉大人抵窒,你說我怎么就攤上這事〉铮” “怎么了李皇?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宙枷。 經(jīng)常有香客問我掉房,道長,這世上最難降的妖魔是什么慰丛? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任卓囚,我火速辦了婚禮,結(jié)果婚禮上诅病,老公的妹妹穿的比我還像新娘哪亿。我一直安慰自己粥烁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布蝇棉。 她就那樣靜靜地躺著讨阻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篡殷。 梳的紋絲不亂的頭發(fā)上钝吮,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音板辽,去河邊找鬼奇瘦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛戳气,可吹牛的內(nèi)容都是我干的链患。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼瓶您,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纲仍?” 一聲冷哼從身側(cè)響起呀袱,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎郑叠,沒想到半個(gè)月后夜赵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乡革,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年寇僧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沸版。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘁傀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出视粮,到底是詐尸還是另有隱情细办,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布蕾殴,位于F島的核電站笑撞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钓觉。R本人自食惡果不足惜茴肥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荡灾。 院中可真熱鬧瓤狐,春花似錦堕虹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至郁稍,卻和暖如春赦政,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耀怜。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工恢着, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人财破。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓掰派,卻偏偏與公主長得像,于是被迫代替她去往敵國和親左痢。 傳聞我的和親對象是個(gè)殘疾皇子靡羡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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