iOS組件化實(shí)踐方案-LDBusMediator煉就

一、中小型App為什么要組件化

當(dāng)項(xiàng)目App處于起步階段辙售、各個(gè)需求模塊趨于成熟穩(wěn)定的過程中旦部,組件化也許并沒有那么迫切士八,甚至考慮組件化的架構(gòu)可能會(huì)影響開發(fā)效率和需求迭代。而當(dāng)項(xiàng)目迭代到一定時(shí)期之后缰趋,便會(huì)出現(xiàn)一些相對獨(dú)立的業(yè)務(wù)功能模塊,而團(tuán)隊(duì)的規(guī)模也會(huì)隨著項(xiàng)目迭代逐漸增長味抖,這便是中小型應(yīng)用考慮組件化的時(shí)機(jī)了仔涩。

為了更好的分工協(xié)作,團(tuán)隊(duì)會(huì)安排團(tuán)隊(duì)成員各自維護(hù)一個(gè)相對獨(dú)立的業(yè)務(wù)組件佩研。這個(gè)時(shí)候我們引入組件化方案旬薯,一是為了解除組件之間相互引用的代碼硬依賴适秩,二是為了規(guī)范組件之間的通信接口; 讓各個(gè)組件對外都提供一個(gè)黑盒服務(wù)骤公,而組件工程本身可以獨(dú)立開發(fā)測試阶捆,減少溝通和維護(hù)成本洒试,提高效率。

進(jìn)一步發(fā)展娱挨,當(dāng)團(tuán)隊(duì)涉及到轉(zhuǎn)型或者有了新的立項(xiàng)之后跷坝,一個(gè)團(tuán)隊(duì)會(huì)開始維護(hù)多個(gè)項(xiàng)目App碉碉,而多個(gè)項(xiàng)目App的需求模塊往往存在一定的交叉垢粮,而這個(gè)時(shí)候組件化給我們的幫助會(huì)更大蜡吧,我只需要將之前的多個(gè)業(yè)務(wù)組件模塊在新的主App中進(jìn)行組裝即可快速迭代出下一個(gè)全新App昔善。

二、如何開始組件化工作

2.1 組件化的架構(gòu)目標(biāo)

在詳細(xì)說如何具體開始組件化工作之前翩概,我們對于組件化的期望應(yīng)該是這樣的钥庇,一個(gè)團(tuán)隊(duì)維護(hù)一到兩個(gè)獨(dú)立App咖摹,每個(gè)獨(dú)立App除開包含一些產(chǎn)品相關(guān)的非獨(dú)立模塊集之外萤晴,還需要用一些獨(dú)立的業(yè)務(wù)組件進(jìn)行組裝。 而不管是產(chǎn)品的非獨(dú)立模塊集蕴侧、還是獨(dú)立業(yè)務(wù)組件都需要底層公共庫和基礎(chǔ)庫的支持净宵。如下圖所示:

組件化目標(biāo)圖.png

2.2 組件化第一步-剝離公共庫和產(chǎn)品基礎(chǔ)庫

在具體的項(xiàng)目開發(fā)過程中择葡,我們使用cocoapod的組件依賴管理利器已經(jīng)開始從Github上引入了一些第三方開源的基礎(chǔ)庫敏储,比如說AFNetworking、SDWebImage妥箕、SVProgressHUD畦幢、ZipArchive等宇葱。除開這些第三方開源基礎(chǔ)庫之外刊头,我們還需要做的事情就是將一些基礎(chǔ)組件從主工程剝離出來原杂,形成產(chǎn)品自己的私有基礎(chǔ)庫倉庫,為我們進(jìn)行業(yè)務(wù)獨(dú)立組件的分離做準(zhǔn)備。

這部分我將其分為兩類:一類是公共基礎(chǔ)庫被碗,用于跨產(chǎn)品使用锐朴;一類是產(chǎn)品基礎(chǔ)庫蔼囊,在某個(gè)產(chǎn)品中強(qiáng)相關(guān)依賴使用。這里以我們自己產(chǎn)品劃分為例壶谒,概述一下這兩類庫都包括哪些基礎(chǔ)組件:

公共庫包括:組件化中間件膳沽、網(wǎng)絡(luò)診斷挑社、第三方SDK管理封裝痛阻、長連接相關(guān)阱当、Patch相關(guān)斗这、網(wǎng)絡(luò)和頁面監(jiān)控相關(guān)表箭、用戶行為統(tǒng)計(jì)庫免钻、第三方分享庫、JSBridge相關(guān)凤覆、關(guān)于Device+file+crypt+http的基礎(chǔ)方法等盯桦。

產(chǎn)品基礎(chǔ)庫包括:通用的WebViewContainer組件(封裝了JSBridge)拥峦、自定義數(shù)字鍵盤卖子、表情鍵盤、自定義下拉列表玄柠、循環(huán)滾動(dòng)頁面羽利、AFNeworking封裝庫(對上層業(yè)務(wù)隱藏AF的直接引用)铐伴、以及其他自定義的UI基礎(chǔ)組件庫当宴。

2.2 組件化第二步-獨(dú)立業(yè)務(wù)模塊單獨(dú)成庫

在基礎(chǔ)庫成體系的基礎(chǔ)上,我們就可以開始按照需求定性將一些相對獨(dú)立的業(yè)務(wù)模塊獨(dú)立成庫玲献,單獨(dú)在一個(gè)工程上進(jìn)行開發(fā)捌年、測試礼预。

往往在這個(gè)階段有一個(gè)誤區(qū)托酸,千萬不能為了組件化而強(qiáng)行將一些耦合嚴(yán)重的業(yè)務(wù)模塊分出励堡。如果在拆分過程中应结,拆分模塊跟其他模塊耦合太嚴(yán)重泉唁,那就先放棄這部分模塊的獨(dú)立鹅龄,畢竟產(chǎn)品是不會(huì)單獨(dú)拿出時(shí)間給你做組件化的。

另外拆分的粒度需要大一點(diǎn)亭畜,需要在功能模塊的基礎(chǔ)上扮休,將業(yè)務(wù)獨(dú)立性考慮進(jìn)去,如果沒有就不拆贱案,等以后有了相對獨(dú)立的模塊之后再拆。

2.3 組件化第三步-對外服務(wù)接口最小化

組件化不是一蹴而就的,我們在完成第二步的時(shí)候并不要強(qiáng)行要求去掉組件之間代碼的硬依賴宝踪,只需要保證單獨(dú)拆分出來的工程可以獨(dú)立運(yùn)行和測試侨糟,并且能夠通過引用保證其他業(yè)務(wù)組件和主工程的依賴使用即可秕重。

當(dāng)?shù)诙酵瓿芍螅覀兛梢栽诖嘶A(chǔ)上總結(jié)其他組件和主工程的需求調(diào)用,根據(jù)需求總結(jié)和抽象出當(dāng)前業(yè)務(wù)組件對外服務(wù)的最小化接口以及頁面跳轉(zhuǎn)調(diào)用庐扫。經(jīng)過多次總結(jié),我們可以發(fā)現(xiàn)組件之間的通信需求無外乎三個(gè)方面:URL導(dǎo)航+服務(wù)接口調(diào)用+消息變量定義。如下圖所示:

組件通信需求.png

在這個(gè)階段,我們大多數(shù)應(yīng)用會(huì)選擇JLRoute(蘑菇街的MGJRoute方案也類似)去做URL導(dǎo)航的需求胜嗓,會(huì)通過OpenServiceImpl + Protocol的方案(將所有對外服務(wù)提供的接口都在OpenServiceImpl中實(shí)現(xiàn))去做組件間的服務(wù)調(diào)用寥粹,消息變量的聲明可以放到對外服務(wù)接口的Protocol定義中媚狰。

到了這個(gè)階段,我們的業(yè)務(wù)組件也已經(jīng)相對獨(dú)立遗锣,JLRoute能夠去掉頁面引用的頭頭文件依賴。OpenServiceImpl+Protocol也將我們最小化的對外服務(wù)接口約束到Protocol接口文件中。 如果對于項(xiàng)目組件化要求不高的話,到這一步就可以了扶叉。

三、徹底組件化-LDBusMediator煉就

3.1 組件化方案不徹底之處和JLRoute的缺陷

通過第二部分的講述,我們的組件化工作差不多完成了80%酪劫,但是我們依然發(fā)現(xiàn)遮咖,組件化并不夠徹底麦箍。

先來看服務(wù)調(diào)用方面,我們需要對外提供OpenServiceImpl的頭文件栗竖,外部模塊仍然保持著對業(yè)務(wù)組件的強(qiáng)依賴,OpenServiceImpl的不兼容變化必然導(dǎo)致所有調(diào)用部分的更改架专,我們期望的黑盒服務(wù)便無法實(shí)現(xiàn)。如果所有類別的服務(wù)接口都在OpenServiceImpl中實(shí)現(xiàn),OpenServiceImpl中的代碼會(huì)越來越多锡移,難以維護(hù)和管理施符。 另外Protocol文件和OpenServiceImpl的頭文件都需要對外披露柬采,如果放到組件實(shí)現(xiàn)中肩刃,兩個(gè)組件相互之間有調(diào)用,就會(huì)導(dǎo)致Podspec的相互循環(huán)依賴。

再看URL導(dǎo)航方面焊夸,在我們的項(xiàng)目中揪阶,我們在ViewController的類別中通過load方法注冊URL-Block麦乞,這樣能夠解決JLRoute的中心化注冊問題,但是JLRoute仍然存在其他一些缺陷。JLRoute去中心化的具體使用方式如下:

+ (void)load
{
    @autoreleasepool {
        [JLRoutes addRoute:@"/xxxx" handler:^BOOL(NSDictionary *parameters) {
            
            UIViewController *baseViewController = parameters[kLDRouteViewControllerKey];
            if (!baseViewController) {
                baseViewController = [UIViewController topmostViewController];
            }
            if (!baseViewController) {
                return YES;
            }
            
            XXXXViewController *viewController = [[XXXXViewController alloc] init];
            
            if ([baseViewController isKindOfClass:[UINavigationController class]]) {
                [(UINavigationController*)baseViewController pushViewController:viewController animated:YES];
            }else if (baseViewController.navigationController) {
                [baseViewController.navigationController pushViewController:viewController animated:YES];
            } else {
                UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
                [baseViewController presentViewController:navController animated:YES completion:NULL];
            }
            return YES;
        }];
    }
}

如上所用,JLRoute的缺陷如下:

  • url短鏈分布式注冊時(shí),導(dǎo)航代碼的重復(fù)拷貝邮府;
  • 無法通過URL返回一個(gè)controller實(shí)例胸竞;(TabController也就無法從獨(dú)立業(yè)務(wù)組件中不引用Controller頭文件獲取Controller實(shí)例完成設(shè)置)
  • class的load方法完成注冊马篮,太多對啟動(dòng)時(shí)Main線程有影響掷匠;
  • 同一個(gè)url短鏈的導(dǎo)航方式單一固定擎值,依賴注冊
  • 單一業(yè)務(wù)組件中可導(dǎo)航URL分散汹粤,無法統(tǒng)一查看;
  • Debug階段url傳遞參數(shù)錯(cuò)誤互捌、not found沒有提示牲阁;

3.2 LDBusMediator總體方案

針對組件化不徹底的實(shí)際問題客税,結(jié)合之前手淘分享的總線架構(gòu)以及蘑菇街的組件化分享博客目胡,我們完成了一個(gè)通用的LDBusMediator中間件幫助我們徹底完成組件化。

LDBusMediator開源Git地址:

我們先來看總體的組件化方案:所有的業(yè)務(wù)組件通過Connector連接到總線中,Connector需要遵循Connector Protocol方可接入淘邻。Connector協(xié)議規(guī)定了URL導(dǎo)航接入和服務(wù)接入的協(xié)議,Connector通過Class的Load方法將自己的實(shí)例注冊到中間件的Cache數(shù)組中岸夯,方便其他組件在調(diào)用時(shí)中間件可以通過服務(wù)發(fā)現(xiàn)的方式進(jìn)行URL導(dǎo)航和服務(wù)調(diào)用餐曼。(具體見如下的圖示)



@implementation Connector_A

#pragma mark - register connector

/**
 * 每個(gè)組件的實(shí)現(xiàn)必須自己通過load完成掛載养渴;
 * load只需要在掛載connector的時(shí)候完成當(dāng)前connecotor的初始化宇立,掛載量津函、掛載消耗、掛載所耗內(nèi)存都在可控范圍內(nèi)活逆;
 */
+(void)load{
    @autoreleasepool{
        [LDBusMediator registerConnector:[self sharedConnector]];
    }
}
@end

3.3 LDBusMediator-URL導(dǎo)航方案

URL導(dǎo)航的總線中間件方案很簡單,只需要在Connector中實(shí)現(xiàn)URL導(dǎo)航接入的接口即可侠驯,如圖所示:

LDBusMediator-URL導(dǎo)航.png

具體使用如下:

@protocol LDBusConnectorPrt <NSObject>

-(BOOL)canOpenURL:(nonnull NSURL *)URL;

- (nullable UIViewController *)connectToOpenURL:(nonnull NSURL *)URL params:(nullable NSDictionary *)params;

@end

@implementation Connector_A

#pragma mark - LDBusConnectorPrt 

/**
 * (1)當(dāng)調(diào)用方需要通過判斷URL是否可導(dǎo)航顯示界面的時(shí)候权谁,告訴調(diào)用方該組件實(shí)現(xiàn)是否可導(dǎo)航URL;可導(dǎo)航奋救,返回YES,否則返回NO轴猎;
 * (2)這個(gè)方法跟connectToOpenURL:params配套實(shí)現(xiàn)矛渴;如果不實(shí)現(xiàn),則調(diào)用方無法判斷某個(gè)URL是否可導(dǎo)航峦椰;
 */
-(BOOL)canOpenURL:(nonnull NSURL *)URL{
    if ([URL.host isEqualToString:@"ADetail"]) {
        return YES;
    }

    return NO;
}
@end
/**
 * (1)通過connector向busMediator掛載可導(dǎo)航的URL冒签,具體解析URL的host還是path走趋,由connector自行決定夺荒;
 * (2)如果URL在本業(yè)務(wù)組件可導(dǎo)航,則從params獲取參數(shù)五辽,實(shí)例化對應(yīng)的viewController進(jìn)行返回悔橄;如果參數(shù)錯(cuò)誤凛忿,則返回一個(gè)錯(cuò)誤提示的[UIViewController paramsError]; 如果不需要中間件進(jìn)行present展示壕吹,則返回一個(gè)[UIViewController notURLController],表示當(dāng)前可處理;如果無法處理,返回nil,交由其他組件處理肯适;
 * (3)需要在connector中對參數(shù)進(jìn)行驗(yàn)證撩嚼,不同的參數(shù)調(diào)用生成不同的ViewController實(shí)例;也可以通過參數(shù)決定是否自行展示源请,如果自行展示左权,則用戶定義的展示方式無效;
 * (4)如果掛接的url較多,這里的代碼比較長捺癞,可以將處理方法分發(fā)到當(dāng)前connector的category中豹绪;
 */
- (nullable UIViewController *)connectToOpenURL:(nonnull NSURL *)URL params:(nullable NSDictionary *)params{
    //處理scheme://ADetail的方式
    // tip: url較少的時(shí)候可以通過if-else去處理,如果url較多烁落,可以自己維護(hù)一個(gè)url和ViewController的map,加快遍歷查找讨跟,生成viewController;
    if ([URL.host isEqualToString:@"ADetail"]) {
        DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
        if (params[@"key"] != nil) {
            viewController.valueLabel.text = params[@"key"];
        } else if(params[@"image"]) {
            id imageObj = params[@"image"];
            if (imageObj && [imageObj isKindOfClass:[UIImage class]]) {
                viewController.valueLabel.text = @"this is image";
                viewController.imageView.image = params[@"image"];
                [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
                return [UIViewController notURLController];
            } else {
                viewController.valueLabel.text = @"no image";
                viewController.imageView.image = [UIImage imageNamed:@"noImage"];
                [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
                return [UIViewController notURLController];
            }
        } else {
            // nothing to do
        }
        return viewController;
    }


    else {
        // nothing to to
    }

    return nil;
}

通過LDBusMediator的URL導(dǎo)航方案,有效的解決了前文提出的JLRoute的缺陷:

  1. url短鏈分布式注冊時(shí),導(dǎo)航代碼的重復(fù)拷貝
  • LDBusNavigator+PresentMode:將通用的導(dǎo)航方式即成到LDBusNavigator中,而無需每個(gè)URL注冊時(shí)重復(fù)拷貝休溶。
  1. 無法通過URL返回一個(gè)controller實(shí)例;(TabController)
  • *URL-Block —> URL-ViewController實(shí)例:將之前JLRoute的url-block方式改成了url-ViewController方式熏挎,即可滿足。
  1. class的load方法完成注冊磅轻,太多對啟動(dòng)時(shí)Main線程有影響;
  • 服務(wù)發(fā)現(xiàn)的方式,只在load時(shí)注冊Connector實(shí)例:中間件只對每個(gè)業(yè)務(wù)組件的connector實(shí)例進(jìn)行注冊僚饭,相比URL注冊量大量減少load使用乌妒。
  1. 同一個(gè)url短鏈的導(dǎo)航方式單一固定丧枪,依賴注冊
  • 調(diào)用時(shí)指定Present、Push疫衩、Share方式:之前JLRoute只能在注冊時(shí)候決定導(dǎo)航方式,通過LDBusMediator如何導(dǎo)航顯示由調(diào)用方?jīng)Q定艇抠,默認(rèn)是Push歹苦;Share方式是指pop到導(dǎo)航層次中已經(jīng)存在的viewController處屉凯。
  1. 單一業(yè)務(wù)組件中可導(dǎo)航URL分散节榜,無法統(tǒng)一查看;
  • 單一組件的connector中集中管理所有可導(dǎo)航URL
  1. Debug階段url傳遞參數(shù)錯(cuò)誤啸蜜、not found沒有提示蜂林;
  • Debug階段的錯(cuò)誤Controller提示、包括參數(shù)錯(cuò)誤芝加、notFound敢辩、notSupportController:如果參數(shù)錯(cuò)誤、notfound無法生成一個(gè)viewController實(shí)例帐姻,中間件在debug階段會(huì)提示。如果URL不支持返回一個(gè)Controller纱皆,同樣會(huì)給與提示。

3.4 LDBusMediator-服務(wù)調(diào)用方案

為了更好的通過中間件支撐組件間的服務(wù)調(diào)用方案,我們在組件實(shí)現(xiàn)和中間件之間增加了一層協(xié)議接口層呻拌。 每個(gè)業(yè)務(wù)組件將自己對外提供的服務(wù)接口抽象到一個(gè)統(tǒng)一的業(yè)務(wù)組件協(xié)議集合中乌助。 業(yè)務(wù)組件的實(shí)現(xiàn)依賴自己的對外服務(wù)接口集并進(jìn)行接口的實(shí)現(xiàn)炕泳。

每個(gè)業(yè)務(wù)組件中的協(xié)議部分有兩種:一種是服務(wù)協(xié)議,其他組件可以通過Mediator拿到對外開放的服務(wù)實(shí)例調(diào)用服務(wù)接口;一種是Model協(xié)議,服務(wù)協(xié)議中的接口可以給其他組件一個(gè)協(xié)議化對象,其他組件也可以組裝一個(gè)協(xié)議化對象通過參數(shù)傳入。

為了方便業(yè)務(wù)組件實(shí)現(xiàn)和協(xié)議集合的版本對應(yīng),需要保證協(xié)議集合的大版本(如x.y)和業(yè)務(wù)組件的大版本(如x.y.z)中的x保持一致疗绣;協(xié)議集合中一般沒有補(bǔ)丁版本的迭代,當(dāng)其他業(yè)務(wù)組件調(diào)用需要增加接口進(jìn)行兼容版本升級(y+1),減少或者修改接口則需要協(xié)議集合和業(yè)務(wù)組件中的x同時(shí)+1(x+1)立轧; 如果自身業(yè)務(wù)組件升級不能影響對外協(xié)議接口的調(diào)用格粪,升級版本主要為補(bǔ)丁版本迭代(z+1)或 兼容版本升級(y+1);

組件協(xié)議集合 單獨(dú)通過一個(gè)Git地址進(jìn)行管理氛改,單獨(dú)配置podspec帐萎,單獨(dú)通過協(xié)議的版本倉庫進(jìn)行管理;所有的協(xié)議集合的git統(tǒng)一放到Git的一個(gè)組中進(jìn)行管理胜卤。

具體方案如下:

LDBusMediator-服務(wù)調(diào)用.png
@protocol LDBusConnectorPrt <NSObject>
/**
 * 業(yè)務(wù)模塊掛接中間件疆导,注冊自己提供的service,實(shí)現(xiàn)服務(wù)接口的調(diào)用葛躏;
 * 
 * 通過protocol協(xié)議找到組件中對應(yīng)的服務(wù)實(shí)現(xiàn)澈段,生成一個(gè)服務(wù)單例悠菜;
 * 傳遞給調(diào)用者進(jìn)行protocol接口中屬性和方法的調(diào)用;
 */
- (nullable id)connectToHandleProtocol:(nonnull Protocol *)servicePrt;  

@end


@implementation Connector_A
/**
 * (1)通過connector向BusMediator掛接可處理的Protocol败富,根據(jù)Protocol獲取當(dāng)前組件中可處理protocol的服務(wù)實(shí)例悔醋;
 *  (2)具體服務(wù)協(xié)議的實(shí)現(xiàn)可放到其他類實(shí)現(xiàn)文件中,只需要在當(dāng)前connetor中引用囤耳,返回一個(gè)服務(wù)實(shí)例即可篙顺;
 *  (3)如果不能處理,返回一個(gè)nil充择;
 */
- (nullable id)connectToHandleProtocol:(nonnull Protocol *)servicePrt{
    if (servicePrt == @protocol(ModuleAXXXServicePrt)) {
        return [[self class] sharedConnector];
    }
    return nil;
}
@end

LDBusMediator中間件的服務(wù)調(diào)用方案的優(yōu)勢:

  1. 通過中間件支撐德玫,不暴露任何實(shí)現(xiàn)文件的頭文件;
  • 組件對外提供的服務(wù)通過最小化抽象的“協(xié)議接口集”披露椎麦;
  • 組件的實(shí)現(xiàn)Pod不暴露任何頭文件宰僧;
  1. 每個(gè)業(yè)務(wù)組件提供黑盒服務(wù)
  • 調(diào)用者不用關(guān)心具體實(shí)現(xiàn)細(xì)節(jié);
  • 業(yè)務(wù)組件的實(shí)現(xiàn)升級观挎、或者更換(包括整個(gè)業(yè)務(wù)組件更換)不影響調(diào)用者的調(diào)用修改琴儿;
  1. 為業(yè)務(wù)組件Framework化、自動(dòng)化構(gòu)建奠定基礎(chǔ)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘁捷,一起剝皮案震驚了整個(gè)濱河市造成,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雄嚣,老刑警劉巖晒屎,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缓升,居然都是意外死亡鼓鲁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門港谊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骇吭,“玉大人,你說我怎么就攤上這事歧寺≡镎” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵成福,是天一觀的道長碾局。 經(jīng)常有香客問我,道長奴艾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任内斯,我火速辦了婚禮蕴潦,結(jié)果婚禮上像啼,老公的妹妹穿的比我還像新娘。我一直安慰自己潭苞,他們只是感情好忽冻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著此疹,像睡著了一般僧诚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝗碎,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天湖笨,我揣著相機(jī)與錄音,去河邊找鬼蹦骑。 笑死慈省,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眠菇。 我是一名探鬼主播边败,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捎废!你這毒婦竟也來了笑窜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤登疗,失蹤者是張志新(化名)和其女友劉穎排截,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谜叹,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匾寝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荷腊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艳悔。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖女仰,靈堂內(nèi)的尸體忽然破棺而出猜年,到底是詐尸還是另有隱情,我是刑警寧澤疾忍,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布乔外,位于F島的核電站,受9級特大地震影響一罩,放射性物質(zhì)發(fā)生泄漏杨幼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望差购。 院中可真熱鬧四瘫,春花似錦、人聲如沸欲逃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稳析。三九已至洗做,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彰居,已是汗流浹背诚纸。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裕菠,地道東北人咬清。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像奴潘,于是被迫代替她去往敵國和親旧烧。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

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