iOS 淺析路由設(shè)計模式

自從項目用了路由設(shè)計模式润梯,每次測試告訴我有bug的時候垫卤,大哥都會微微一笑父丰,告訴她這是服務(wù)器的bug

1. 什么是路由

在Web開發(fā)過程中肝谭,經(jīng)常會遇到『路由』的概念掘宪。那么,到底什么是路由攘烛?簡單來說魏滚,路由就是URL到函數(shù)的映射。

2. router和route的區(qū)別

route 就是一條路由坟漱,它將一個URL路徑和一個函數(shù)進行映射鼠次,例如:

/users        ->  getAllUsers()
/users/count  ->  getUsersCount()

這就是兩條路由,當訪問 /users 的時候芋齿,會執(zhí)行 getAllUsers() 函數(shù)腥寇;當訪問 /users/count 的時候,會執(zhí)行 getUsersCount() 函數(shù)觅捆。

而 router 可以理解為一個容器赦役,或者說一種機制,它管理了一組 route 栅炒。簡單來說掂摔, route 只是進行了URL和函數(shù)的映射,而在當接收到一個URL之后赢赊,去路由映射表中查找相應(yīng)的函數(shù)乙漓,這個過程是由 router 來處理的。一句話概括就是 “ The router routes you to a route “释移。

上面亂七八糟一堆沒什么用簇秒,只有一句話是有用的:路由就是URL到函數(shù)的映射。例如“User/login”秀鞭,User控制器下的login方法。

首先我們來比較一下我們以前的結(jié)構(gòu)模式以及與 加入路由機制后的項目結(jié)構(gòu)扛禽,實現(xiàn)路由機制锋边,不僅需要一個映射文件,還需要一套路由管理機制编曼,那么采用路由機制豆巨,我們的項目架構(gòu)就跟原來不一樣了.

沒吃腦殘片之前.png

我們看一下它有哪些缺點:
(1)都要在當前頁面引入要跳轉(zhuǎn)頁面的class 類。這就造成了頁面的耦合性很高掐场。
(2)遇到重大bug,不能夠及時的修復問題往扔,需要等待更新發(fā)版后才能解決。
(3)推送消息或者是3D-Touch需求熊户,要求直接跳轉(zhuǎn)到內(nèi)部第10級界面萍膛,那么就需要寫一個入口跳轉(zhuǎn)到指定界面。


吃了腦殘片之后.png

二嚷堡、App路由能解決哪些問題

思考如下的問題蝗罗,平時我們開發(fā)中是如何優(yōu)雅的解決的:

1.3D-Touch功能或者點擊推送消息,要求外部跳轉(zhuǎn)到App內(nèi)部一個很深層次的一個界面。

比如微信的3D-Touch可以直接跳轉(zhuǎn)到“我的二維碼”串塑≌恿穑“我的二維碼”界面在我的里面的第三級界面∽耍或者再極端一點打瘪,產(chǎn)品需求給了更加變態(tài)的需求,要求跳轉(zhuǎn)到App內(nèi)部第十層的界面傻昙,怎么處理闺骚?

2.自家的一系列App之間如何相互跳轉(zhuǎn)?

如果自己App有幾個屋匕,相互之間還想相互跳轉(zhuǎn)葛碧,怎么處理?

3.如何解除App組件之間和App頁面之間的耦合性过吻?

隨著項目越來越復雜进泼,各個組件,各個頁面之間的跳轉(zhuǎn)邏輯關(guān)聯(lián)性越來越多纤虽,如何能優(yōu)雅的解除各個組件和頁面之間的耦合性乳绕?

4.如何能統(tǒng)一iOS和Android兩端的頁面跳轉(zhuǎn)邏輯?甚至如何能統(tǒng)一三端的請求資源的方式逼纸?

項目里面某些模塊會混合ReactNative洋措,Weex,H5界面杰刽,這些界面還會調(diào)用Native的界面菠发,以及Native的組件。那么贺嫂,如何能統(tǒng)一Web端和Native端請求資源的方式滓鸠?

5.如果使用了動態(tài)下發(fā)配置文件來配置App的跳轉(zhuǎn)邏輯,那么如果做到iOS和Android兩邊只要共用一套配置文件第喳?

6.如果App出現(xiàn)bug了糜俗,如何不用JSPatch,就能做到簡單的熱修復功能曲饱?

比如App上線突然遇到了緊急bug悠抹,能否把頁面動態(tài)降級成H5,ReactNative扩淀,Weex楔敌?或者是直接換成一個本地的錯誤界面?

7.如何在每個組件間調(diào)用和頁面跳轉(zhuǎn)時都進行埋點統(tǒng)計引矩?每個跳轉(zhuǎn)的地方都手寫代碼埋點梁丘?利用Runtime AOP 侵浸?

8.如何在每個組件間調(diào)用的過程中,加入調(diào)用的邏輯檢查氛谜,令牌機制掏觉,配合灰度進行風控邏輯?

9.如何在App任何界面都可以調(diào)用同一個界面或者同一個組件值漫?只能在AppDelegate里面注冊單例來實現(xiàn)澳腹?

比如App出現(xiàn)問題了,用戶可能在任何界面杨何,如何隨時隨地的讓用戶強制登出酱塔?或者強制都跳轉(zhuǎn)到同一個本地的error界面?或者跳轉(zhuǎn)到相應(yīng)的H5危虱,ReactNative羊娃,Weex界面?如何讓用戶在任何界面埃跷,隨時隨地的彈出一個View 蕊玷?

以上這些問題其實都可以通過在App端設(shè)計一個路由來解決。那么我們怎么設(shè)計一個路由呢弥雹?

+(void) processActionURL:(NSString*)actionURL andWithAction:(NSString*)action
{
       NSDictionary * ACTION_MAP = @{
      /*瀏覽器*/    @"browser":@"HSWebViewController",
      /*資訊詳情*/  @"news_detail":@"HSNewsDetailController",
      /*帖子詳情*/  @"topic_detail":@"HSTopicDatailTableViewController",
      /*章節(jié)列表*/  @"material_detail":@"HSMaterialDetailTableViewController",
      /*資料管理*/  @"material_manage":@"MaterialManage",
      /*題目展示*/  @"question_detail":@"HSMaterialQuestionController",
      /*文章詳情*/  @"article_detail":@"",
      /*問卷調(diào)查*/  @"survey_detail":@"",
      /*套餐詳情*/  @"meal_detail":@"",
      /*課程詳情*/  @"course_detail":@"",
      /*購買頁面*/  @"purchase":@"HSPayViewController",
      /*列表頁面*/  @"search_list":@"HSComonSearchTBV",
      /*用戶幫助頁面*/ @"guide":@"HSHelpCenterViewController",
      /*二維碼掃描*/ @"qrcode":@"HSScanViewController",
      /*領(lǐng)取課程*/  @"playcode":@"HSPlayCodeViewController",
      /*我的課程*/  @"my_course":@"",
      /*個人設(shè)置*/  @"profile_setting":@"HSPensonalSettingController",
      /*系統(tǒng)設(shè)置*/  @"system_setting":@"HSSettingController",
      /*用戶信息*/  @"user_info":@"HSUserInfoVC"
                 };
    NSDictionary * dict = [HSNetUrlProcess queryDictWithUrlString:actionURL];
    if ([action isEqualToString:@"browser"]) { // 0 跳轉(zhuǎn)到瀏覽器
        HSWebViewController * webView = [[HSWebViewController alloc]init];
        webView.urlStr = dict[HSOBJ_URL];
        webView.hidesBottomBarWhenPushed = YES;
        [[HSTool getCurrentVC].navigationController pushViewController:webView animated:YES];
    }
    else if([action isEqualToString:@"news_detail"])//1
    {
        HSNewsDetailController *newsVC=[[HSNewsDetailController alloc]init];
        newsVC.param=dict;
        [[HSTool getCurrentVC].navigationController pushViewController:newsVC animated:YES];
    }
    else if([action isEqualToString:@"topic_detail"])//2
    {
        HSTopicDatailTableViewController * topicDetail = [[HSTopicDatailTableViewController alloc]init];
        topicDetail.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:topicDetail animated:YES];
    }
    else if([action isEqualToString:@"material_detail"])//3
    {
        HSMaterialDetailTableViewController *materialDeialVC=[[HSMaterialDetailTableViewController alloc]init];
        materialDeialVC.param=dict;
        [[HSTool getCurrentVC].navigationController pushViewController:materialDeialVC animated:YES];
    }
    else if([action isEqualToString:@"material_manage"])//4
    {
        MaterialManage * manage = [[MaterialManage alloc]init];
        manage.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:manage animated:YES];
    }
    else if([action isEqualToString:@"system_setting"])//5
    {
        HVsystemSetingTableController * setting = [[HVsystemSetingTableController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:setting animated:YES];
    }
    else if([action isEqualToString:@"profile_setting"])//6
    {
        HSPensonalSettingController * personSet = [[HSPensonalSettingController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:personSet animated:YES];
    }
    else if([action isEqualToString:@"user_info"])//7
    {
        HSUserInfoVC * personSet = [[HSUserInfoVC alloc]init];
        personSet.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:personSet animated:YES];
    }
    else if([action isEqualToString:@"guide"])//8
    {
        HSHelpCenterViewController * help = [[HSHelpCenterViewController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:help animated:YES];
    }
    else if([action isEqualToString:@"qrcode"])//9
    {
        if (![FLLoginDataModel currentUser].isLogin) {
            HSLoginController * log = [[HSLoginController alloc]init];
            log.hidesBottomBarWhenPushed = YES;
            [[HSTool getCurrentVC].navigationController pushViewController:log animated:YES];
            return;
        }
        NSString *mediaType = AVMediaTypeVideo;
        
        AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
        if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied){
            
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請在設(shè)置中打開APP相機權(quán)限" message:nil preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *action  =[UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:nil];
            [alert addAction:action];
            [[HSTool getCurrentVC] presentViewController:alert animated:YES completion:nil];
            return;
        }
        
        HSScanViewController * scan = [[HSScanViewController alloc]init];
        HSNavigationViewController *nav =[[HSNavigationViewController alloc]initWithRootViewController:scan];
        [scan.navigationController.navigationBar setHidden:YES];
        [[HSTool getCurrentVC] presentViewController:nav animated:YES completion:nil];
    }
    else if([action isEqualToString:@"search_list"])//10
    {
        HSComonSearchTBV * searchtbv = [[HSComonSearchTBV alloc]init];
        searchtbv.param =  dict;
        if (title.length>0) {
            searchtbv.title = title;
        }
        [[HSTool getCurrentVC].navigationController pushViewController:searchtbv animated:YES];
    }
    else if([action isEqualToString:@"playcode"])//11
    {
        HSPlayCodeViewController * playcode = [[HSPlayCodeViewController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:playcode animated:YES];
    }
    else if([action isEqualToString:@"question_detail"])//12
    {
        HSMaterialQuestionController * questionVC = [[HSMaterialQuestionController alloc]init];
       
        questionVC.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:questionVC animated:YES];
    }
    else if([action isEqualToString:@"purchase"]) //購買界面 13
    {
        HSPayViewController * pay = [[HSPayViewController alloc]init];
        pay.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:pay animated:YES];
    }
    else if([action isEqualToString:@"article_detail"])//14
    {
        HSNewsContentVC * article = [[HSNewsContentVC alloc]init];
        article.paramDict = dict;
        article.isArticleDetail = YES;
        if (title.length>0) {
            article.title = title;
        }
        [[HSTool getCurrentVC].navigationController pushViewController:article animated:YES];

    }
    else if([action isEqualToString:@"survey_detail"])//15
    {
        
    }
    else if([action isEqualToString:@"meal_detail"]||[action isEqualToString:@"pack_detail"])//16
    {
        HSMealDetailViewController *detailVC=[[HSMealDetailViewController alloc]init];
        detailVC.param=dict;
      
        [[HSTool getCurrentVC].navigationController pushViewController:detailVC animated:YES];
    }
    else if([action isEqualToString:@"course_detail"])//17
    {

        HSCourseDetailViewController *detailVC=[[HSCourseDetailViewController alloc]init];
        detailVC.param=dict;
        if ([[HSTool getCurrentVC] isKindOfClass:[HSCourseDetailViewController class]]||[[HSTool getCurrentVC] isKindOfClass:[HSVideoDetailViewController class]]) {
            detailVC.isPushToSelfVC = YES;
        }
        [[HSTool getCurrentVC].navigationController pushViewController:detailVC animated:YES];
        
    }
    else if([action isEqualToString:@"my_course"])//18
    {
        HSMyCourseViewController *mycourse = [[HSMyCourseViewController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:mycourse animated:YES];
    }
    else if([action isEqualToString:@"teacher_detail"])
    {
        HVTeacherDetailVC * teacherDetail = [[HVTeacherDetailVC alloc]init];
        teacherDetail.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:teacherDetail animated:YES];

    }
    else if([action isEqualToString:@"suggest"])
    {
        FeedBackVC * feedBack = [[FeedBackVC alloc]init];
        feedBack.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:feedBack animated:YES];
    }
    else if([action isEqualToString:@"video_detail"])
    {
        HSVideoDetailViewController *videoDetail= [[HSVideoDetailViewController alloc]init];
        videoDetail.param  = dict;
        [[HSTool getCurrentVC].navigationController  pushViewController:videoDetail animated:YES];
    }
    else if([action isEqualToString:@"answer_detail"])
    {//"answer/view?answer_id=d8c83aa9e8707475c545a8966ae052ea&token=357487b027fda52852792171a18ac2f4"
        HSAnalysisViewController *analysisVC = [[HSAnalysisViewController alloc]init];
        analysisVC.param = dict;
        [[HSTool getCurrentVC].navigationController  pushViewController:analysisVC animated:YES];
    }
    else if([action isEqualToString:@"qq"])
    {
        [HSTool jumpToQQWithQQnumber:dict[HSOBJ_URL]];
    }
    else if([action isEqualToString:@"phone"])
    {
        [HSTool jumpToPhoneWithPhonenumber:dict[HSOBJ_URL]];
    }
    else if([action isEqualToString:@"webview"])
    {
        HSWebViewController * webView = [[HSWebViewController alloc]init];
        webView.urlStr = dict[HSOBJ_URL];
       
        webView.hidesBottomBarWhenPushed = YES;
         webView.shouldHideNavBar = YES;
        [[HSTool getCurrentVC].navigationController pushViewController:webView animated:YES];
    }
    else if([action isEqualToString:@"login"])
    {
        HSLoginController * login = [[HSLoginController alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:login animated:YES];
    }
    else if([action isEqualToString:@"order_detail"])
    {
        OrderDetailController * detail = [[OrderDetailController alloc]init];
        detail.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:detail animated:YES];
    }
    else if([action isEqualToString:@"web_login"])
    {

        SwipLoginVC * swip = [[SwipLoginVC alloc]init];
        swip.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:swip animated:YES];
    }
    else if([action isEqualToString:@"aboutus"])
    {
         [[HSTool getCurrentVC].navigationController pushViewController:[[HvaboutVC alloc]init] animated:YES];
    }
    else if([action isEqualToString:@"message_detail"]){
        
        HSMessageDetailViewController * detailVC = [[HSMessageDetailViewController alloc]init];
        detailVC.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:detailVC animated:YES];
    }
    else if([action isEqualToString:@"score"]){
        IntegralDetailVC *detailVC = [[IntegralDetailVC alloc]init];
        [[HSTool getCurrentVC].navigationController pushViewController:detailVC animated:YES];
    }
    else if([action isEqualToString:@"close"]){
        [[HSTool getCurrentVC].navigationController popViewControllerAnimated:YES];
    }
    else if([action isEqualToString:@"goods_detail"]){
        HSGoodsDetailViewController * detailVC = [[HSGoodsDetailViewController alloc]init];
        detailVC.param = dict;
        [[HSTool getCurrentVC].navigationController pushViewController:detailVC animated:YES];
    }
    else if([action isEqualToString:@"self"]){
        [AFNetworkTool HVDataCache:dict NetBlock:^(NSDictionary *json) {
            
        } ErrorCode:^(int errorCode) {
            
        } Fail:^{
            
        } Setting:nil];
    }
    else if([action isEqualToString:@"update_store"])
    {
        
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:actionURL]];
        //   [HSCoverView showMessage:@"未指定跳轉(zhuǎn)內(nèi)容" finishBlock:nil];
    }

無論是路由還是中間人或者是組件化垃帅,其實感覺思想上都差不多,現(xiàn)在舉個例子還說明一下上面的代碼:例如界面上有一個按鈕剪勿,服務(wù)器就會給你一個action和action_url 贸诚,action表示你要跳轉(zhuǎn)的頁面,url表示下一個頁面網(wǎng)絡(luò)請求的地址厕吉,項目中幾乎所有的點擊事件都由服務(wù)器控制酱固,服務(wù)器也會傳給你相應(yīng)的action和url。
這樣的封裝头朱,也為與H5交互提供了便捷媒怯,

 [_bridge registerHandler:@"WebViewAction" handler:^(id data, WVJBResponseCallback responseCallback) {

 NSDictionary *dic  = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:(NSUTF8StringEncoding)] options:NSJSONReadingAllowFragments error:nil];
           
 [HSTool processActionURL:[dic stirngWithSaftKey:@"action_url"] andWithAction:[dic stirngWithSaftKey:@"action"]];        
 }];

還記得以前每個H5頁面都要單獨一個controller,然后跟著后臺小哥一頓對接口髓窜,想一堆的標識符,現(xiàn)在只需要一個webVC欺殿,只需要兩行代碼就能實現(xiàn)寄纵,而且支持所有原生支持的事件,感覺寫的這么方便我好想要失業(yè)了脖苏。程拭。。棍潘。恃鞋。崖媚。

如法炮制的在UI顯示上我們同樣寫好了二十多種type的UI樣式,每種樣式對應(yīng)一個cell恤浪,一個controller可以顯示二十幾種樣式畅哑,完全可以通過后端來配置一個自己想要的頁面,從二十幾種中選出你要展示的樣式水由,你可以用type1也可以用type2這個controller有很高的復用率荠呐。

曾經(jīng)想過既然所有的事件跳轉(zhuǎn)都交給Tool去處理慰毅,那可不可以同樣實現(xiàn)一個view制作這樣的一個工具類褥符,其實也可以览闰,不過高度和布局實現(xiàn)比較麻煩哲嘲。

但是需求來了也沒辦法呵哨,后來我直接把支持type解析的controller引几,用child controller丹允,addsubview的方式直接把這個controller當成了一個萬能的view航瞭,但是由于controller是個tableview彤恶,而你卻非要把它當成一個view钞钙,所以高度是一個很麻煩的問題,后來用KVO解決了粤剧,感覺很尷尬歇竟,雖然能滿足需求,但是總感覺哪里怪怪的抵恋。

總結(jié):

相對來說這樣的設(shè)計還是很方便的焕议,低耦合,而且支持小小的熱更新弧关,樣式不定盅安,事件不定,都由服務(wù)器端來控制世囊,用起來很方便别瞭,最重要的是,我發(fā)現(xiàn)我?guī)缀鯖]什么bug株憾,有bug基本上全是后臺大兄弟的蝙寨,哈哈

相關(guān):與路由設(shè)計模式配套的視圖處理方式

相關(guān):

http://www.cnblogs.com/oc-bowen/p/6489070.html
https://casatwy.com/iOS-Modulization.html
http://www.cocoachina.com/ios/20170315/18893.html
http://blog.csdn.net/tianyitianyi1/article/details/60070763

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嗤瞎,隨后出現(xiàn)的幾起案子墙歪,更是在濱河造成了極大的恐慌,老刑警劉巖贝奇,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虹菲,死亡現(xiàn)場離奇詭異,居然都是意外死亡掉瞳,警方通過查閱死者的電腦和手機毕源,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門浪漠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霎褐,你說我怎么就攤上這事址愿。” “怎么了瘩欺?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵必盖,是天一觀的道長。 經(jīng)常有香客問我俱饿,道長歌粥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任拍埠,我火速辦了婚禮失驶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枣购。我一直安慰自己嬉探,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布棉圈。 她就那樣靜靜地躺著涩堤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪分瘾。 梳的紋絲不亂的頭發(fā)上胎围,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音德召,去河邊找鬼白魂。 笑死,一個胖子當著我的面吹牛上岗,可吹牛的內(nèi)容都是我干的福荸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肴掷,長吁一口氣:“原來是場噩夢啊……” “哼敬锐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呆瞻,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤滞造,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栋烤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡挺狰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年明郭,在試婚紗的時候發(fā)現(xiàn)自己被綠了买窟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡薯定,死狀恐怖始绍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情话侄,我是刑警寧澤亏推,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站年堆,受9級特大地震影響吞杭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜变丧,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一芽狗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痒蓬,春花似錦童擎、人聲如沸攻晒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碴萧,卻和暖如春乙嘀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背破喻。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留婴噩,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓羽德,卻偏偏與公主長得像几莽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宅静,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

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