淺談React Native與實(shí)現(xiàn)機(jī)制

1. React Native簡(jiǎn)單介紹

目前App開(kāi)發(fā)的主流方式有三種: Native開(kāi)發(fā)祖驱,Hybird開(kāi)發(fā)以及Web開(kāi)發(fā)

原生Native開(kāi)發(fā)

主要采用Object-C/Swift方式進(jìn)行原生開(kāi)發(fā)印衔。運(yùn)行效率高,流暢,用戶(hù)體驗(yàn)好,可以做各種復(fù)雜的動(dòng)畫(huà)效果。平臺(tái)獨(dú)立性,代碼無(wú)法在其他平臺(tái)上運(yùn)行,無(wú)法做到跨平臺(tái)利凑。更新審核周期比較長(zhǎng),不利于App問(wèn)題的快速修復(fù)

Hybird開(kāi)發(fā)

以原生開(kāi)發(fā)為主。
更新頻繁,活動(dòng)頁(yè)面,運(yùn)營(yíng)頁(yè)面等采用H5方式接入嫌术。定義好原生功能與H5之間的協(xié)議,攔截特定的URL Schema進(jìn)行原生功能的調(diào)用,App調(diào)用H5提供的js方法,給H5傳值和通知H5

Web開(kāi)發(fā)

是Web App,以Web為主,通過(guò)js或者插件方式調(diào)用原生功能,如撥打電話(huà),位置服務(wù)等哀澈。
一套Web代碼可以分別在各個(gè)平臺(tái)上運(yùn)行。受限制與UIWebView,app的性能和體驗(yàn)都無(wú)法與純?cè)鷄pp相提并論度气。比較有代表性的:采用cordova和ionic進(jìn)行web app開(kāi)發(fā),通過(guò)開(kāi)發(fā)原生插件功能供Web端調(diào)用

React Native的出現(xiàn)

不同的開(kāi)發(fā)方式都在解決如下的幾個(gè)問(wèn)題

  • 使得APP的體驗(yàn)效果和原生應(yīng)用一樣好
  • 跨平臺(tái)割按,提高項(xiàng)目代碼的重用性
  • 應(yīng)對(duì)廣告或者活動(dòng)更新,能夠進(jìn)行熱替換而不用進(jìn)行APP新發(fā)布

因此Facebook在2015年發(fā)布了React Native框架磷籍,旨在幫助前端程序員解決如上的棘手問(wèn)題适荣,在發(fā)布當(dāng)初,相比于其他Hybird框架院领,React Native有如下的特點(diǎn)

  • 基于組件開(kāi)發(fā),提供代碼的復(fù)用率弛矛。
  • 各個(gè)平臺(tái)功能代碼可以進(jìn)行復(fù)用,官方數(shù)據(jù)表明:iOS和android功能代碼可以達(dá)到90%以上的復(fù)用。
  • 不用Webview,徹底擺脫了Webview的限制:交互和性能問(wèn)題栅盲。
  • 相對(duì)其它Hybrid 方案,React Native性能更好,用戶(hù)體驗(yàn)更接近原生汪诉。
  • 減少編譯時(shí)間,提高開(kāi)發(fā)效率。
  • 可以采用熱更新方式進(jìn)行app功能升級(jí)和問(wèn)題修復(fù),提高app的迭代率和開(kāi)發(fā)效率
React Native實(shí)例
  • 在JS語(yǔ)言中嵌入了HTML和CSS的元素谈秫,這種被擴(kuò)展了的JavaScript語(yǔ)言稱(chēng)為jsx
  • React Native框架中扒寄,JavaScript內(nèi)存中維護(hù)了一個(gè)Virtual DOM,JSX內(nèi)容在Virtual DOM中被轉(zhuǎn)化翻譯成真實(shí)的DOM樹(shù)拟烫,Virtual DOM與真實(shí)顯示的DOM保持一一對(duì)應(yīng)的關(guān)系
  • 當(dāng)界面發(fā)生變化時(shí)该编,得益于高效的 DOM Diff 算法,我們能夠知道 Virtual DOM 的變化硕淑,從而高效的改動(dòng) DOM课竣,避免了重新繪制 DOM
JSX實(shí)例
var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

2. 動(dòng)態(tài)配置

在當(dāng)下移動(dòng)端App越來(lái)越人性化的趨勢(shì)下嘉赎,App的更新迭代速度很快,但是限制于App Store和安卓市場(chǎng)的應(yīng)用版本更新限制于樟,如果每次界面上有部分需要更新公条,例如廣告更換,頁(yè)面布局調(diào)整等迂曲,都需要通過(guò)發(fā)布一個(gè)新的版本來(lái)實(shí)現(xiàn)靶橱,著實(shí)對(duì)應(yīng)用商和用戶(hù)來(lái)說(shuō)都是不合理的,因此如果有一種方式可以動(dòng)態(tài)的配置移動(dòng)端界面便是極好的路捧。

很多時(shí)候关霸,我們都是利用 JSON 文件實(shí)現(xiàn)動(dòng)態(tài)配置的效果,它的核心流程如下

JSON實(shí)現(xiàn)動(dòng)態(tài)配置
  1. 通過(guò) HTTP 請(qǐng)求獲取 JSON 格式的配置文件杰扫。
  2. 配置文件中標(biāo)記了每一個(gè)元素的屬性队寇,比如位置,顏色章姓,圖片 URL 等佳遣。
  3. 解析完 JSON 后,我們調(diào)用 Objective-C 的代碼啤覆,完成 UI 控件的渲染苍日。

通過(guò)這種方法惭聂,我們實(shí)現(xiàn)了在后臺(tái)配置 app 的展示樣式窗声。從本質(zhì)上來(lái)說(shuō),移動(dòng)端和服務(wù)端約定了一套協(xié)議辜纲,但是協(xié)議內(nèi)容嚴(yán)重依賴(lài)于應(yīng)用內(nèi)要展示的內(nèi)容笨觅,不利于拓展。也就是說(shuō)耕腾,如果業(yè)務(wù)要求頻繁的增加或修改頁(yè)面见剩,這套協(xié)議很難應(yīng)付。

然而這種通過(guò)JSON通信扫俺,配置一些可選項(xiàng)的方式在很多情況下都不能夠滿(mǎn)足現(xiàn)在快速迭代開(kāi)發(fā)的App苍苞,如果想要改變一些業(yè)務(wù)邏輯或者進(jìn)行一些復(fù)雜度比較高的修改操作,則客戶(hù)端只讀取JSON配置項(xiàng)是做不到的狼纬,無(wú)法調(diào)用處理業(yè)務(wù)邏輯的方法等羹呵,不具備調(diào)試功能。

各種移動(dòng)平臺(tái)支持JavaScript

然而疗琉,基于現(xiàn)在移動(dòng)設(shè)備都支持JavaScript代碼的執(zhí)行這一條件(例如iOS上內(nèi)置了JavaScript Core來(lái)執(zhí)行JavaScript代碼)冈欢,React Native的推出發(fā)揮了這一優(yōu)點(diǎn),通過(guò)JavaScript代碼盈简,不僅僅只是傳遞簡(jiǎn)單的配置信息凑耻,更可以進(jìn)行業(yè)務(wù)邏輯的處理太示。

Learn once, write everywhere

和其他Hybird框架所宣傳的"Write once, run everywhere"不同,React Native其實(shí)不能真正意義上稱(chēng)為"跨平臺(tái)"框架香浩,因?yàn)樗谋举|(zhì)是使用了各個(gè)移動(dòng)平臺(tái)都支持JavaScript語(yǔ)言类缤,React Native幫我們做好了將JavaScript代碼轉(zhuǎn)化成Object-C或者Java語(yǔ)言,并且?guī)臀覀兲幚砗昧烁鞣N回調(diào)問(wèn)題邻吭,因此表面上我們只需要編寫(xiě)JavaScript語(yǔ)言呀非,即可在不同的平臺(tái)上展現(xiàn)應(yīng)用,這也是React Native的開(kāi)發(fā)初衷: 分別開(kāi)發(fā)安卓和iOS而不用寫(xiě)一行原生代碼

3. 通信機(jī)制

iOS -- JavaScript / Objective-C

我們雖然使用的是React Native框架镜盯,但還是需要依賴(lài)UIKit等框架岸裙,從而調(diào)用Objective-C代碼以在iOS平臺(tái)上執(zhí)行,JavaScript其實(shí)只是為我們提供了編寫(xiě)業(yè)務(wù)邏輯和前端界面的輔助工具速缆,React Native在iOS上能夠運(yùn)行的實(shí)質(zhì)是利用JS代碼調(diào)用OC代碼執(zhí)行降允,我們需要關(guān)注的重點(diǎn)就是JS與OC之間的通信機(jī)制,包括JS是如何去調(diào)用OC代碼艺糜,又如何實(shí)現(xiàn)回調(diào)功能剧董,這是React Native的核心功能之一。

我們都知道破停,JS作為一種腳本語(yǔ)言翅楼,是不需要像C語(yǔ)言那樣被編譯鏈接然后執(zhí)行,在執(zhí)行腳本語(yǔ)言時(shí)真慢,會(huì)在運(yùn)行時(shí)動(dòng)態(tài)地進(jìn)行詞法和語(yǔ)法的分析毅臊,然后生成一課抽象語(yǔ)法樹(shù)和對(duì)應(yīng)的字節(jié)碼,由JS解釋器等將字節(jié)碼轉(zhuǎn)化成對(duì)應(yīng)的機(jī)器碼黑界,而整個(gè)流程都是由JS引擎來(lái)加以完成管嬉。

JavaScript Core

在iOS平臺(tái)下,React Native利用了iOS提供的JavaScript Core作為JS解析器朗鸠,然而RN并沒(méi)有完全使用JS Core中提供的JS-OC互調(diào)的特性蚯撩,而是自己實(shí)現(xiàn)了一套通用的方案,以便兼容不同的版本

OC調(diào)用JS

OC向JS傳信息有現(xiàn)成接口烛占,stringByEvaluatingJavaScriptFromString方法可以直接在當(dāng)前context上執(zhí)行一段JS腳本胎挎,并且可以獲取執(zhí)行后的返回值,這個(gè)返回值就相當(dāng)于JS向OC傳遞信息忆家。

JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];

在上述例子中犹菇,JSContext 代表了當(dāng)前JS的執(zhí)行環(huán)境,evaluateScript 方法則會(huì)去執(zhí)行后面跟著的js腳本內(nèi)容弦赖,返回值會(huì)存放在 JSValue 中项栏,從而完成OC調(diào)用JS并獲取JS返回信息。

JS調(diào)用OC

React Native基于上述OC調(diào)用JS的方法蹬竖,經(jīng)過(guò)一些封裝在OC里面定義了一個(gè)模塊方法沼沈,JS可以直接調(diào)用這個(gè)模塊方法并且可以注冊(cè)回調(diào)函數(shù)流酬。

//Objective-C
@implement RCTSQLManager
- (void)query:(NSString *)queryData successCallback:(RCTResponseSenderBlOCk)responseSender
{
     RCT_EXPORT();
     NSString *ret = @"ret"
     responseSender(ret);
}
@end
//JavaScript
RCTSQLManager.query("SELECT * FROM table", function(result) {
     //result == "ret";
});

如上圖所示,在OC內(nèi)部定義了一個(gè)模塊RCTSQLManager列另,并且在模塊內(nèi)部定義了方法 -query: successCallback芽腾;我們?cè)贘S中可以直接調(diào)用RCTSQLManager的query方法并且注冊(cè)回調(diào)函數(shù)。

模塊配置表
  1. 取出所有可被調(diào)用的模塊页衙,每個(gè)可被調(diào)用模塊類(lèi)都實(shí)現(xiàn)了RCTBridgeModule接口摊滔,可以通過(guò)runtime接口objc_getClassListobjc_copyClassList取出項(xiàng)目里所有類(lèi),然后逐個(gè)判斷是否實(shí)現(xiàn)了RCTBridgeModule接口店乐,就可以找到所有模塊類(lèi)艰躺。

  2. 取出模塊中所有可被調(diào)用的方法,模塊方法里有句代碼:RCT_EXPORT()眨八,模塊里的方法加上這個(gè)宏就可以實(shí)現(xiàn)暴露給JS腺兴,無(wú)需其他規(guī)則,這個(gè)宏的作用是用編譯屬性__attribute__給二進(jìn)制文件新建一個(gè)section廉侧,屬于__DATA數(shù)據(jù)段页响,名字為RCTExport,并在這個(gè)段里加入當(dāng)前方法名段誊。編譯器在編譯時(shí)會(huì)找到__attribute__進(jìn)行處理闰蚕,為生成的可執(zhí)行文件加入相應(yīng)的內(nèi)容。

#define RCT_EXPORT(JS_name) __attribute__((used, section("__DATA,RCTExport" \
))) static const char *__rct_export_entry__[] = { __func__, #JS_name }
  1. 在讀取完所有可被調(diào)用模塊和可被調(diào)用方法后连舍,OC告訴JS有哪些模塊没陡,哪些方法是可以被JS調(diào)用的,在這里的實(shí)現(xiàn)機(jī)制是OC生成一份模塊配置表然后OC端和JS端分別持有這一份配置表烟瞧,表的內(nèi)容大致如下诗鸭,可以看到每個(gè)模塊都有對(duì)應(yīng)的編號(hào)染簇,每個(gè)方法也有對(duì)應(yīng)的編號(hào)参滴,在JS調(diào)用OC時(shí),通過(guò)傳遞對(duì)應(yīng)的ModuleID和MethodID即可匹配OC模塊及方法锻弓。
{
    "remoteModuleConfig": {
        "RCTSQLManager": {
            "methods": {
                "query": {
                    "type": "remote",
                    "methodID": 0
                }
            },
            "moduleID": 4
        },
        ...
     },
}
React Native初始化分析

每個(gè)應(yīng)用有一個(gè)唯一的 rootWindow,每一個(gè)UIWindow有一個(gè)唯一的rootView,在React Native 中,對(duì)應(yīng)的就是RCTRootView,它持有一個(gè)RCTBridge,RCTBridge的職能是通訊橋,負(fù)責(zé)各個(gè)模塊之間和js之間的通訊, RCTBatchedBridge繼承RCTBridge,它有一個(gè)唯一的但是可變的currentBridge,實(shí)際上RCTBridge是唯一的, RCTBatchedBridge是唯一的,通訊時(shí),實(shí)際上RCTBatchedBridge承擔(dān)一個(gè)適配的職責(zé)砾赔。

因此,實(shí)際上在創(chuàng)建一個(gè)RootView之前青灼,React Native都會(huì)預(yù)先創(chuàng)建好一個(gè)RCTBridge暴心,而RCTBridgesetUp方法主要是為了初始化BatchedBridgeBatchedBridge主要是用來(lái)批量讀取JavaScript對(duì)Objective-C的調(diào)用杂拨,BatchedBridge內(nèi)部還依賴(lài)一個(gè)JSCExecutor专普,用于執(zhí)行JS代碼,下面我們簡(jiǎn)單地了解一下BatchedBridge初始化過(guò)程中都做了哪些工作弹沽。

1. 讀取 JavaScript 源碼

這個(gè)過(guò)程將應(yīng)用的js代碼加載到內(nèi)存檀夹,供接下來(lái)在OC中調(diào)用執(zhí)行JS代碼

2. 初始化模塊信息

這一步主要是發(fā)現(xiàn)所有需要暴露給JavaScript的模塊及模塊中需要暴露的方法筋粗,每一個(gè)需要暴露的模塊都會(huì)被加上 RCT_EXPORT_MODULE的宏,宏的內(nèi)容如下:

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

這個(gè)類(lèi)在執(zhí)行l(wèi)oad方法時(shí)會(huì)調(diào)用RCTRegisterModule方法炸渡,將自身注冊(cè)到RCTModuleClasses

void RCTRegisterModule(Class moduleClass)
{
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    RCTModuleClasses = [NSMutableArray new];
  });

  [RCTModuleClasses addObject:moduleClass];
}

因此我們可以從RCTModuleClasses中獲取出所有模塊信息娜亿,每一條模塊信息都被存儲(chǔ)與RCTModuleData對(duì)象中

for (Class moduleClass in RCTGetModuleClasses()) {
    RCTModuleData *moduleData = [[RCTModuleData alloc]initWithModuleClass:moduleClass                                                                      bridge:self];
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
}
3. 初始化 JavaScript 代碼的執(zhí)行器,即 RCTJSCExecutor 對(duì)象

在這一步操作中蚌堵,通過(guò)addSynchronousHookWithName這一方法向JavaScript的添加了若干的Block對(duì)象作為全局變量买决,以供第5步過(guò)程中在執(zhí)行JavaScript源碼時(shí)處理這些Block對(duì)象

4. 生成模塊列表并寫(xiě)入 JavaScript 端

這一步操作是將OC端生成的模塊列表信息注入到JavaScript端中,以便雙方都持有一份模塊列表信息吼畏。

- (NSString *)moduleConfig{
    NSMutableArray<NSArray *> *config = [NSMutableArray new];
    for (RCTModuleData *moduleData in _moduleDataByID) {
      [config addObject:@[moduleData.name]];
    }
}

可以看到督赤,Objective-C將config信息存儲(chǔ)到了JavaScript的全局變量中,名稱(chēng)為__fbBatchedBridgeConfig

5. 執(zhí)行 JavaScript 源碼

在所有的初始化都完成后泻蚊,只需要運(yùn)行js代碼即可够挂,運(yùn)行過(guò)程中也會(huì)執(zhí)行第3步過(guò)程添加進(jìn)全局變量的Block對(duì)象

方法調(diào)用流程
  1. JS調(diào)用模塊方法。

  2. 在JS端有一個(gè)JS Bridge專(zhuān)門(mén)負(fù)責(zé)處理JS與OC交互部分藕夫,同理在OC端也有一個(gè)OC Bridge孽糖,JS Bridge將調(diào)用的模塊方法記錄并轉(zhuǎn)化成相應(yīng)的ModuleName,MethodName和args毅贮。

  3. 然后在MessageQueue中將調(diào)用模塊方法時(shí)的回調(diào)函數(shù)注冊(cè)一個(gè)CallBack ID办悟,將ID和回調(diào)函數(shù)存儲(chǔ)在一個(gè)成員變量的列表中,并將第2步中的ModuleName和MethodName根據(jù)模塊配置表信息轉(zhuǎn)成對(duì)應(yīng)的ID滩褥。

  4. JS將moduleID病蛉,methodID和args以及CallBackID傳遞給OC Bridge,這個(gè)過(guò)程實(shí)質(zhì)上是基于事件處理的瑰煎,因?yàn)樵谝苿?dòng)平臺(tái)上如果有代碼的執(zhí)行必定是某個(gè)事件觸發(fā)的铺然,比如滑動(dòng)屏幕等等,事件觸發(fā)后OC主動(dòng)調(diào)用JS代碼酒甸,JS處理業(yè)務(wù)邏輯過(guò)程并將需要調(diào)用OC的部分存儲(chǔ)到MessageQueue中魄健,再去通知OC執(zhí)行。

  5. OC接收到消息插勤,通過(guò)模塊配置表拿到對(duì)應(yīng)的模塊和方法沽瘦,在OC Bridge端,對(duì)每一個(gè)可被調(diào)用的模塊方法都會(huì)有一個(gè)RCTModuleMethod對(duì)象與之對(duì)應(yīng)农尖。

  6. RCTModuleMethod對(duì)傳進(jìn)來(lái)的參數(shù)進(jìn)行處理析恋,包括類(lèi)型轉(zhuǎn)化以及創(chuàng)建一個(gè)Block對(duì)象以供回調(diào),會(huì)將JS端傳過(guò)來(lái)的CallBackID以及回調(diào)的值存儲(chǔ)進(jìn)Block對(duì)象中

  7. 執(zhí)行OC端代碼

  8. 執(zhí)行第6步中生成的Block方法

  9. block里帶著CallbackID和block傳過(guò)來(lái)的參數(shù)去調(diào)JS里MessageQueue的方法invokeCallbackAndReturnFlushedQueue盛卡。

  10. MessageQueue根據(jù)CallBackId找到對(duì)應(yīng)的回調(diào)函數(shù)

  11. 根據(jù)OC傳來(lái)的回調(diào)值助隧,執(zhí)行回調(diào)函數(shù)

整個(gè)流程就是這樣,簡(jiǎn)單概括下滑沧,差不多就是:JS函數(shù)調(diào)用轉(zhuǎn)ModuleID/MethodID -> callback轉(zhuǎn)CallbackID -> OC根據(jù)ID拿到方法 -> 處理參數(shù) -> 調(diào)用OC方法 -> 回調(diào)CallbackID -> JS通過(guò)CallbackID拿到callback執(zhí)行

4. React Native優(yōu)缺點(diǎn)

優(yōu)點(diǎn)
  • 能夠利用 JavaScript 動(dòng)態(tài)更新的特性并村,快速迭代漏健。
  • 相比于原生平臺(tái),開(kāi)發(fā)速度更快橘霎,相比于 Hybrid 框架蔫浆,性能更好。
缺點(diǎn)
  • 不能實(shí)現(xiàn)真正意義上的跨平臺(tái)姐叁,開(kāi)發(fā)者仍然需要為iOS和Android提供兩套實(shí)現(xiàn)機(jī)制
  • 不能直接取代Native Code開(kāi)發(fā)瓦盛,很大程度上還加重了開(kāi)發(fā)者的學(xué)習(xí)成本
  • 語(yǔ)言互轉(zhuǎn)存在著固定的時(shí)間和空間開(kāi)銷(xiāo)

參考資料

  1. React Native從入門(mén)到原理
  2. React Native通信原理
  3. React Native通信機(jī)制詳解
  4. React Native開(kāi)發(fā)簡(jiǎn)介
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市外潜,隨后出現(xiàn)的幾起案子原环,更是在濱河造成了極大的恐慌,老刑警劉巖处窥,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱吗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡滔驾,警方通過(guò)查閱死者的電腦和手機(jī)谒麦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哆致,“玉大人绕德,你說(shuō)我怎么就攤上這事√В” “怎么了耻蛇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)胞此。 經(jīng)常有香客問(wèn)我臣咖,道長(zhǎng),這世上最難降的妖魔是什么漱牵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任夺蛇,我火速辦了婚禮政鼠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拟枚。我一直安慰自己印荔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布死宣。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪儒溉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,850評(píng)論 1 290
  • 那天发钝,我揣著相機(jī)與錄音顿涣,去河邊找鬼波闹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涛碑,可吹牛的內(nèi)容都是我干的精堕。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蒲障,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歹篓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起揉阎,我...
    開(kāi)封第一講書(shū)人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庄撮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毙籽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洞斯,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年坑赡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烙如。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅否,死狀恐怖厅翔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搀突,我是刑警寧澤刀闷,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站仰迁,受9級(jí)特大地震影響甸昏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜徐许,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一施蜜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雌隅,春花似錦翻默、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至检盼,卻和暖如春肯污,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工蹦渣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哄芜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓柬唯,卻偏偏與公主長(zhǎng)得像认臊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锄奢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

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