iOS VIPER架構(gòu)實(shí)踐(一):從MVC到MVVM到VIPER

iOS VIPER架構(gòu)實(shí)踐(一):從MVC到MVVM到VIPER

一: 簡(jiǎn)介

最近半年在寫app的時(shí)候帆谍,研究了一下各種iOS代碼架構(gòu)牵舵,最后選擇了VIPER進(jìn)行實(shí)踐,在此對(duì)實(shí)踐中遇到的各種設(shè)計(jì)問(wèn)題做一番總結(jié),并分享造出的輪子召噩。

對(duì)代碼風(fēng)格和架構(gòu)有興趣的同學(xué)父虑,肯定都已經(jīng)在很多地方見(jiàn)過(guò)各種架構(gòu)的介紹该酗。MVCMVP士嚎、MVVM呜魄、VIPER,細(xì)分程度逐漸上升莱衩。這些架構(gòu)設(shè)計(jì)都是來(lái)自MVC爵嗅,只是各自用不同的方式對(duì)MVC進(jìn)行了細(xì)分,在此只對(duì)MVC笨蚁、MVP和MVVM作精簡(jiǎn)介紹睹晒,想要詳細(xì)了解可以參考這些文章:

iOS 架構(gòu)模式–解密 MVC,MVP括细,MVVM以及VIPER架構(gòu)

淺談 MVC伪很、MVP 和 MVVM 架構(gòu)模式

二: MVC

Model-View-Controller。MVC簡(jiǎn)單地將一個(gè)模塊分為3部分:

  • View是展示給外部的界面
  • Model是Controller內(nèi)部管理的數(shù)據(jù)模型
  • Controller負(fù)責(zé)將Model的變化更新到View; Controller負(fù)責(zé)處理來(lái)自View的事件.

MVC的劃分粒度很粗勒极,因此有很多種具體實(shí)現(xiàn)是掰,各個(gè)實(shí)現(xiàn)有差異,因此并沒(méi)有一個(gè)十分明確的標(biāo)準(zhǔn)定義辱匿。

三:蘋果的MVC

蘋果的Cocoa Touch就遵照了MVC的設(shè)計(jì)键痛,一個(gè)界面分為UIViewUIViewControllerUIView負(fù)責(zé)渲染和接收觸摸事件匾七,UIViewController負(fù)責(zé)子view之間的布局絮短、組合、更新以及事件處理昨忆。

盡管蘋果已經(jīng)給我們提供了簡(jiǎn)單的MVC支持丁频,但是在實(shí)踐中我們卻常常沒(méi)有遵守MVC。原因在于Cocoa Touch中的Model部分是由我們自己負(fù)責(zé)管理的邑贴,并沒(méi)有提供原生的設(shè)計(jì)支持席里。所以有時(shí)候會(huì)出現(xiàn)這樣的情況:一個(gè)UIView為了方便,提供了一個(gè)從某個(gè)model進(jìn)行配置的方法拢驾。乍一看十分合理奖磁,但是仔細(xì)想想就會(huì)發(fā)現(xiàn),這么做已經(jīng)將View和Model耦合繁疤,不符合蘋果官方的MVC規(guī)范(The Role of View Controllers)咖为。

另外秕狰,UIViewController存在的一些問(wèn)題,導(dǎo)致了它很容易變得臃腫和耦合躁染。

首先鸣哀,UIViewController和UIView耦合得十分緊密,導(dǎo)致UIViewController經(jīng)常和某些具體的UIView耦合吞彤,幾乎無(wú)法重用我衬。而且在測(cè)試的時(shí)候,很難做到單獨(dú)測(cè)試沒(méi)有View的那部分代碼备畦,因?yàn)樵趯懙臅r(shí)候就很容易將View的邏輯入侵到各處低飒,Controller會(huì)受到View的狀態(tài)的影響,無(wú)法穩(wěn)定測(cè)試懂盐。因此,應(yīng)該盡量把和View無(wú)關(guān)的代碼放到UIViewController之外糕档。

第二莉恼,UIViewController負(fù)責(zé)了界面跳轉(zhuǎn)的操作,界面跳轉(zhuǎn)的相關(guān)配置是直接在對(duì)應(yīng)的UIViewController實(shí)例上設(shè)置的速那,這樣就很容易把源界面和目的界面耦合起來(lái)俐银,簡(jiǎn)單地把界面跳轉(zhuǎn)的部分單獨(dú)抽離為一個(gè)封裝好的跳轉(zhuǎn)方法可以一定程度上減少這部分耦合,但也不可避免地會(huì)多寫許多代碼端仰。

因此捶惜,蘋果的MVC,實(shí)際上是Model-View-ViewController荔烧。它是一個(gè)視圖驅(qū)動(dòng)的設(shè)計(jì)吱七,Controller只是為了管理View而存在的。蘋果把UIViewController和Model的關(guān)系設(shè)計(jì)交給了我們自己鹤竭。所以踊餐,如何把一個(gè)UIViewController進(jìn)行更明確的分工,就是這些架構(gòu)要做的事臀稚。

四: MVP

Model-View-Presenter用一個(gè)Presenter吝岭,把Controller中View的部分剔除,實(shí)現(xiàn)了View和Model的隔絕吧寺。各部分分工如下:

  • View負(fù)責(zé)界面展示和布局管理窜管,向Presenter暴露視圖更新和數(shù)據(jù)獲取的接口
  • Presenter負(fù)責(zé)接收來(lái)自View的事件,通過(guò)View提供的接口更新視圖稚机,并管理Model
  • Model和MVC中的一樣幕帆,提供數(shù)據(jù)模型

在iOS里,UIView和UIViewController共同組合成了MVP中的View抒钱。UIView負(fù)責(zé)元素的展示蜓肆,UIViewController負(fù)責(zé)界面布局和組合颜凯,并把事件轉(zhuǎn)發(fā)給Presenter。
因此在MVP里仗扬,業(yè)務(wù)邏輯被放到了Presenter中症概,由它負(fù)責(zé)協(xié)調(diào)View和Model。而由于View的抽離早芭,Presenter的狀態(tài)是可控的彼城,在測(cè)試時(shí)更不容易受外部影響。

在iOS中使用MVP很簡(jiǎn)單退个,在View和Presenter之間用protocol做好事件傳遞就可以募壕。缺點(diǎn)就是多了一層用于隔離的接口,會(huì)導(dǎo)致代碼數(shù)量增大语盈。

但是隨著界面越來(lái)越復(fù)雜舱馅,Presenter中的業(yè)務(wù)代碼也會(huì)越來(lái)越龐大,總有一天會(huì)遇到一個(gè)新的問(wèn)題:如何再細(xì)分Presenter刀荒。

五: MVVM

Model-View-ViewModel模式代嗤,它也和MVP一樣,目的是解決View和Model的耦合缠借。各部分分工如下:

5.1: 最普遍的MVVM

  • Model提供數(shù)據(jù)模型
  • View負(fù)責(zé)視圖展示
  • ViewModel用于描述View的狀態(tài)干毅,例如View的顏色、顯示的文字等屬性類的信息泼返,將View抽象成了一個(gè)特殊的模型硝逢,并且持有和管理Model,維護(hù)業(yè)務(wù)邏輯

在MVP中绅喉,View通過(guò)接口的方式來(lái)描述自己渠鸽,在MVVM中,則通過(guò)ViewModel來(lái)描述自己的特征霹疫。那么ViewModel如何將自己的變化更新到View上呢拱绑?MVVM經(jīng)常和數(shù)據(jù)綁定一起出現(xiàn),在UIViewController中丽蝎,將View和ViewModel的屬性用類似KVO的方式進(jìn)行綁定猎拨,這樣ViewModel的變化就能立即傳輸?shù)絍iew上。

5.2: 數(shù)據(jù)綁定

利用ReactiveCocoa和RxSwift這些函數(shù)式響應(yīng)編程框架實(shí)現(xiàn)數(shù)據(jù)綁定屠阻,可以用很少的代碼完成復(fù)雜的業(yè)務(wù)邏輯红省,熟練時(shí)能夠提升開(kāi)發(fā)速度。但是數(shù)據(jù)綁定的缺點(diǎn)也很明顯:調(diào)試?yán)щy国觉,數(shù)據(jù)來(lái)源難以回溯吧恃,在線上出bug的時(shí)候就很難追蹤了,所以從這方面來(lái)說(shuō)又降低了維護(hù)的效率麻诀。

其實(shí)數(shù)據(jù)綁定只是一種為了減少膠水代碼的技術(shù)實(shí)現(xiàn)方式痕寓,MVVM的設(shè)計(jì)并沒(méi)有要求必須要使用數(shù)據(jù)綁定傲醉,你也完全可以使用protocol的方式來(lái)將ViewModel的變化傳遞給View,讓數(shù)據(jù)流向更清晰呻率。MVVM的關(guān)鍵是將View進(jìn)行了抽象硬毕,從而實(shí)現(xiàn)View和Model的解耦。

5.3: ViewModel的職責(zé)

但是除了數(shù)據(jù)綁定礼仗,MVVM還有另一個(gè)問(wèn)題吐咳。把業(yè)務(wù)邏輯放到ViewModel中,雖然能夠?yàn)閁IViewController減負(fù)元践,但是只是把問(wèn)題轉(zhuǎn)移了韭脊,最終ViewModel還是會(huì)變成另一個(gè)Massive ViewModel

而且當(dāng)ViewModel維護(hù)Model和業(yè)務(wù)邏輯時(shí),可復(fù)用性就會(huì)大大降低单旁。例如把同一個(gè)登錄界面復(fù)用到另一個(gè)app中時(shí)沪羔,login model中的屬性名或者類型很可能會(huì)改變,從而數(shù)據(jù)處理的方式也會(huì)改變慎恒,導(dǎo)致ViewModel無(wú)法重用任内。而當(dāng)View由多個(gè)子View組成時(shí),ViewModel里也會(huì)引入多個(gè)子ViewModel融柬,這就又導(dǎo)致了View的實(shí)現(xiàn)影響了ViewModel的實(shí)現(xiàn)。奇怪的是趋距,國(guó)內(nèi)iOS圈對(duì)這個(gè)問(wèn)題的探討十分稀少粒氧。

ViewModel到底是什么?從它的命名和最初的設(shè)計(jì)來(lái)看节腐,它只是View的抽象外盯,目的是方便和Model進(jìn)行數(shù)據(jù)轉(zhuǎn)換。其實(shí)在微軟的WPF和前端里翼雀,MVVM的業(yè)務(wù)邏輯大部分是放在Model層的饱苟,相關(guān)的討論可以參考:

MVVM: ViewModel and Business Logic Connection

Where does business logic sit in MVVM?

The Problems with MVVM on iOS

而針對(duì)這個(gè)問(wèn)題,有人又提出了一個(gè)MVVMP架構(gòu)(Model-View-ViewModel-Presenter)狼渊,把業(yè)務(wù)邏輯放到了Presenter里箱熬。Presenter的引入讓ViewModel專注于View的抽象,和Model分離開(kāi)來(lái)狈邑,只負(fù)責(zé)管理View相關(guān)的狀態(tài)城须、傳遞View的事件,因此ViewModel中的代碼可以得到很好的復(fù)用米苹。而Presenter負(fù)責(zé)大部分業(yè)務(wù)邏輯糕伐,如果模塊需要重用,則把業(yè)務(wù)邏輯中的數(shù)據(jù)操作邏輯(domain logic)單獨(dú)分離出來(lái)作為重用代碼蘸嘶,其他的無(wú)法重用的應(yīng)用邏輯(application logic)則依舊放在Presenter里良瞧。

和MVP相比陪汽,MVVM用了一種更優(yōu)雅的方式來(lái)抽象View。但它和MVP其實(shí)是類似的褥蚯,只做了View和Model的解耦挚冤,仍然沒(méi)有對(duì)Controller進(jìn)行進(jìn)一步的細(xì)分。

那么如何對(duì)Controller進(jìn)行進(jìn)一步的職責(zé)細(xì)分呢遵岩?答案就是VIPER你辣。

六: VIPER

VIPER的全稱是View-Interactor-Presenter-Entity-Router。示意圖如下:

[圖片上傳失敗...(image-ab9aa7-1573097998703)]

相比之前的MVX架構(gòu)尘执,VIPER多出了兩個(gè)東西:Interactor(交互器)和Router(路由)舍哄。

各部分職責(zé)如下:

View

  • 提供完整的視圖,負(fù)責(zé)視圖的組合誊锭、布局表悬、更新
  • 向Presenter提供更新視圖的接口
  • 將View相關(guān)的事件發(fā)送給Presenter

Presenter

  • 接收并處理來(lái)自View的事件
  • 向Interactor請(qǐng)求調(diào)用業(yè)務(wù)邏輯
  • 向Interactor提供View中的數(shù)據(jù)
  • 接收并處理來(lái)自Interactor的數(shù)據(jù)回調(diào)事件
  • 通知View進(jìn)行更新操作
  • 通過(guò)Router跳轉(zhuǎn)到其他View

Router

  • 提供View之間的跳轉(zhuǎn)功能,減少了模塊間的耦合
  • 初始化VIPER的各個(gè)模塊

Interactor

  • 維護(hù)主要的業(yè)務(wù)邏輯功能丧靡,向Presenter提供現(xiàn)有的業(yè)務(wù)用例
  • 維護(hù)蟆沫、獲取、更新Entity
  • 當(dāng)有業(yè)務(wù)相關(guān)的事件發(fā)生時(shí)温治,處理事件饭庞,并通知Presenter

Entity

  • 和Model一樣的數(shù)據(jù)模型

和MVX的區(qū)別
VIPER把MVC中的Controller進(jìn)一步拆分成了Presenter、Router和Interactor熬荆。和MVP中負(fù)責(zé)業(yè)務(wù)邏輯的Presenter不同舟山,VIPER的Presenter的主要工作是在View和Interactor之間傳遞事件,并管理一些View的展示邏輯卤恳,主要的業(yè)務(wù)邏輯實(shí)現(xiàn)代碼都放在了Interactor里累盗。Interactor的設(shè)計(jì)里提出了"用例"的概念,也就是把每一個(gè)會(huì)出現(xiàn)的業(yè)務(wù)流程封裝好突琳,這樣可測(cè)試性會(huì)大大提高若债。而Router則進(jìn)一步解決了不同模塊之間的耦合。所以拆融,VIPER和上面幾個(gè)MVX相比蠢琳,多總結(jié)出了幾個(gè)需要維護(hù)的東西:

  • View事件管理
  • 數(shù)據(jù)事件管理
  • 事件和業(yè)務(wù)的轉(zhuǎn)化
  • 總結(jié)每個(gè)業(yè)務(wù)用例
  • 模塊內(nèi)分層隔離
  • 模塊間通信

而這里面,還可以進(jìn)一步細(xì)分一些職責(zé)冠息。VIPER實(shí)際上已經(jīng)把Controller的概念淡化了挪凑,這拆分出來(lái)的幾個(gè)部分,都有很明確的單一職責(zé)逛艰,有些部分之間是完全隔絕的躏碳,在開(kāi)發(fā)時(shí)就應(yīng)該清晰地區(qū)分它們各自的職責(zé),而不是將它們視為一個(gè)Controller。

優(yōu)點(diǎn)
VIPER的特色就是職責(zé)明確菇绵,粒度細(xì)肄渗,隔離關(guān)系明確,這樣能帶來(lái)很多優(yōu)點(diǎn):

  • 可測(cè)試性好咬最。UI測(cè)試和業(yè)務(wù)邏輯測(cè)試可以各自單獨(dú)進(jìn)行翎嫡。
  • 易于迭代。各部分遵循單一職責(zé)永乌,可以很明確地知道新的代碼應(yīng)該放在哪里惑申。
  • 隔離程度高,耦合程度低翅雏。一個(gè)模塊的代碼不容易影響到另一個(gè)模塊圈驼。
  • 易于團(tuán)隊(duì)合作。各部分分工明確望几,團(tuán)隊(duì)合作時(shí)易于統(tǒng)一代碼風(fēng)格绩脆,可以快速接手別人的代碼

缺點(diǎn)

  • 一個(gè)模塊內(nèi)的類數(shù)量增大,代碼量增大橄抹,在層與層之間需要花更多時(shí)間設(shè)計(jì)接口靴迫。
    使用代碼模板來(lái)自動(dòng)生成文件和模板代碼可以減少很多重復(fù)勞動(dòng),而花費(fèi)時(shí)間設(shè)計(jì)和編寫接口是減少耦合的路上不可避免的楼誓,你也可以使用數(shù)據(jù)綁定這樣的技術(shù)來(lái)減少一些傳遞的層次玉锌。

  • 模塊的初始化較為復(fù)雜,打開(kāi)一個(gè)新的界面需要生成View疟羹、Presenter芬沉、Interactor,并且設(shè)置互相之間的依賴關(guān)系阁猜。而iOS中缺少這種設(shè)置復(fù)雜初始化的原生方式。

總結(jié)
有人可能會(huì)覺(jué)得蹋艺,一個(gè)界面模塊真的有必要使用這么復(fù)雜的架構(gòu)嗎剃袍?這樣是不是過(guò)度設(shè)計(jì)?
我反對(duì)這種觀點(diǎn)捎谨。不要被VIPER的組織圖嚇到民效,VIPER并不復(fù)雜,它是將原來(lái)MVC中的Controller中的各種任務(wù)進(jìn)行了清晰的分解涛救,在寫代碼時(shí)畏邢,你會(huì)很清楚你正在做什么。事實(shí)上检吆,它比使用了數(shù)據(jù)綁定技術(shù)的MVVM更加簡(jiǎn)單舒萎,就是因?yàn)樗氊?zé)明確。從MVC轉(zhuǎn)到VIPER的過(guò)程同樣是很清晰的蹭沛,它甚至把重構(gòu)的思路都體現(xiàn)出來(lái)了臂寝。而MVVM則留下了許多尚未明確的責(zé)任章鲤,導(dǎo)致不同的人會(huì)在某些地方有不同的實(shí)現(xiàn)。即便你還在使用MVC咆贬,你也應(yīng)該在Controller中分離出VIPER總結(jié)出的那些專項(xiàng)職責(zé)败徊,既然如此,為何不徹底地明確這些職責(zé)掏缎,把它們分散到不同的文件中呢皱蹦?一旦開(kāi)始這樣的工作,你就已經(jīng)向VIPER靠攏了眷蜈。
有人可能會(huì)覺(jué)得沪哺,VIPER適合大型app,中小型app沒(méi)必要過(guò)早使用端蛆。
我反對(duì)這種觀點(diǎn)凤粗。VIPER是單個(gè)界面模塊內(nèi)的架構(gòu)設(shè)計(jì),并不是整個(gè)app架構(gòu)層面的設(shè)計(jì)今豆,和app的整體架構(gòu)沒(méi)有多大的關(guān)系嫌拣,也不存在過(guò)早使用VIPER的情況。所以呆躲,嚴(yán)格來(lái)說(shuō)异逐,是復(fù)雜界面更適合VIPER,而不是大型app更適合VIPER插掂。
至此灰瞻,我的結(jié)論就是,快點(diǎn)擁抱VIPER的懷抱吧辅甥。酝润。

開(kāi)始實(shí)踐
VIPER是2013年首次在iOS平臺(tái)上提出的設(shè)計(jì),十分年輕璃弄,因此缺少大量參與者要销,以總結(jié)出更多最佳實(shí)踐。下一篇文章將會(huì)從VIPER的源頭開(kāi)始夏块,比較現(xiàn)有的各種VIPER實(shí)現(xiàn)疏咐,總結(jié)出一個(gè)我認(rèn)為較好的實(shí)施方案。
地址:iOS VIPER架構(gòu)實(shí)踐(二):VIPER詳解與實(shí)現(xiàn)脐供。里面有VIPER的具體Demo和代碼模板浑塞。

參考資料
iOS 架構(gòu)模式–解密 MVC,MVP政己,MVVM以及VIPER架構(gòu)
淺談 MVC酌壕、MVP 和 MVVM 架構(gòu)模式

轉(zhuǎn)載:iOS VIPER架構(gòu)實(shí)踐(一):從MVC到MVVM到VIPER

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仅孩,更是在濱河造成了極大的恐慌托猩,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辽慕,死亡現(xiàn)場(chǎng)離奇詭異京腥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)溅蛉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門公浪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人船侧,你說(shuō)我怎么就攤上這事欠气。” “怎么了镜撩?”我有些...
    開(kāi)封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵预柒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我袁梗,道長(zhǎng)宜鸯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任遮怜,我火速辦了婚禮淋袖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锯梁。我一直安慰自己即碗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布陌凳。 她就那樣靜靜地躺著剥懒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪合敦。 梳的紋絲不亂的頭發(fā)上蕊肥,一...
    開(kāi)封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音蛤肌,去河邊找鬼。 笑死批狱,一個(gè)胖子當(dāng)著我的面吹牛裸准,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赔硫,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼炒俱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起权悟,我...
    開(kāi)封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤砸王,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后峦阁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谦铃,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年榔昔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驹闰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撒会,死狀恐怖嘹朗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诵肛,我是刑警寧澤屹培,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站怔檩,受9級(jí)特大地震影響褪秀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜珠洗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一溜歪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧许蓖,春花似錦柳洋、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至米酬,卻和暖如春沛豌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赃额。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工加派, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跳芳。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓芍锦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親飞盆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娄琉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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