WKWebview適配總結(jié)

最近因Apple的要求:App需要將UIWebview換成WKWebview,經(jīng)過(guò)一個(gè)月的開(kāi)發(fā)和測(cè)試,重要上線了! 下文重點(diǎn)記錄下遇到一些問(wèn)題.

一 UserAgent的設(shè)置
項(xiàng)目中UIWebview設(shè)置UA是全局設(shè)置的,還要防止UA被篡改,調(diào)研很多關(guān)于WK設(shè)置UA的文章,大多數(shù)都是webveiw loadRequest時(shí)設(shè)置UA,沒(méi)有達(dá)到預(yù)期,考慮到用WK設(shè)置UA風(fēng)險(xiǎn)高,一旦UA出了問(wèn)題就徹底晾涼了! UA繼續(xù)用UIWebview來(lái)設(shè)置;小心駛得萬(wàn)年船!

二 Cookie的寫入

本次改造大部分時(shí)間花在了Cookie的問(wèn)題上,WK作為多進(jìn)程組件其實(shí)是對(duì)UIWebveiw的拆分和組裝,新增了一些個(gè)人比較喜歡的功能:
1 estimatedProgress:這個(gè)屬性幫我們實(shí)現(xiàn)界面加載進(jìn)度條,告別了仿真進(jìn)度條的尷尬,WK上的一些原生特殊元素添加也變得游刃有余了.

2 title: 獲取界面title,好多界面title是異步請(qǐng)求,在UIWebview無(wú)能為力,只好拿著定時(shí)器碰運(yùn)氣,WK

3 backForwardList:在某些特定場(chǎng)景下需要返回H5界面的首頁(yè),在UIWebview中無(wú)法通過(guò)API實(shí)現(xiàn)的,可以通過(guò)JS實(shí)現(xiàn):

[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"if( window.history.length > 1 ) { window.history.go( -( window.history.length - 1 ) ) };"]];

這年頭不會(huì)點(diǎn)JavaScript寸步難行,不好意思說(shuō)開(kāi)發(fā)過(guò)hybrid App.
有喜有憂,WKWebView實(shí)例不會(huì)把Cookie存入到App標(biāo)準(zhǔn)的Cookie容器(NSHTTPCookieStorage)中,需要開(kāi)發(fā)者手動(dòng)存入,雖然在iOS11Apple在Webkit框架中新增了WKHTTPCookieStore,可以實(shí)現(xiàn)WK Cookie同步;但App需要支持iOS9以上版本,采用了head中寫入Cookie,document.cookie中注入Cookie方式實(shí)現(xiàn).
記錄下遇到的問(wèn)題

(1) 跨域情況.

1.1 服務(wù)端不設(shè)置Domian的情況
1.2 本地寫Cookie的情況

 NSMutableDictionary *cookiePropertiesZtappcliver = [NSMutableDictionary dictionary];
       [cookiePropertiesZtappcliver setObject:@"ztappcliver" forKey:NSHTTPCookieName];
       NSString *version = [UserLoginHelper sharedInstance].systemVersion;
       version = [NSString stringWithFormat:@"ios@%@",version];
       [cookiePropertiesZtappcliver setObject:SAFE_STRING(version) forKey:NSHTTPCookieValue];
        //為空字符串所有的域名都可以訪問(wèn) 不寫NSHTTPCookieDomain 域名沒(méi)法訪問(wèn).
       [cookiePropertiesZtappcliver setObject:@"" forKey:NSHTTPCookieDomain];
       [cookiePropertiesZtappcliver setObject:@"/" forKey:NSHTTPCookiePath];
       [cookieArray addObject:cookiePropertiesZtappcliver];

(2) cookie會(huì)出現(xiàn)重復(fù)的情況

登錄時(shí)存入WKHTTPCookieStore容器中cookie和服務(wù)端重新寫入cookie名稱相同時(shí)就會(huì)出現(xiàn)重復(fù)的情況,解決辦法是覆蓋WKHTTPCookieStore容器中cookie,具體在 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler中去重.
方法為:


if ([GlobalMembers IsArraySafe:cookies]) {
        for (NSHTTPCookie *cookie in cookies) {
            
           dispatch_async(dispatch_get_global_queue(0, 0), ^{
              for (NSHTTPCookie *ex_cookie in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) {
                    // 響應(yīng)cookie 如果和客戶端請(qǐng)求cookie重復(fù)  則 這個(gè)判斷是有風(fēng)險(xiǎn)的 name值相同 不代表其他屬性是相同的
                  if ([ex_cookie.name isEqualToString:cookie.name]) {
                     DLog(@"%@ cookie========",cookie.name);
                     [[NSHTTPCookieStorage sharedHTTPCookieStorage]deleteCookie:ex_cookie];
                  }
                }
           });
           
            [[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
        }

    }
WK 真的不能同步WKHTTPCookieStore中的cookie嗎Σ(⊙⊙"a ?

可以同步,通過(guò)這個(gè)例子可以看出,WKWebView 實(shí)例其實(shí)也會(huì)將 Cookie 存儲(chǔ)于 NSHTTPCookieStorage 中,但存儲(chǔ)時(shí)機(jī)有延遲,因?yàn)閃KWebView內(nèi)也有cookie的容器,而且這個(gè)同步是進(jìn)程級(jí)別的同步铛嘱,而且這個(gè)同步是單向.

(3) 刪除cookie和WK緩存清理問(wèn)題

客戶端退出登錄時(shí)會(huì)清空Cookie,連帶將WK的緩存清空,測(cè)試階段并沒(méi)有發(fā)現(xiàn)問(wèn)題,后期測(cè)試發(fā)現(xiàn)用戶首次登錄某些4g網(wǎng)絡(luò)下業(yè)務(wù)無(wú)法單點(diǎn)登錄,通過(guò)和早期支持UIWebiew版本對(duì)比得出結(jié)論:WK緩存清空導(dǎo)致的,關(guān)于WK緩存問(wèn)題,可以拜讀https://blog.csdn.net/u012413955/article/details/79783282

(4) cookie因某種原因丟失的情況

一般是重定向或者iOS9以下系統(tǒng)會(huì)出現(xiàn).

(5) iOS13上無(wú)法搖一搖的情況

在iOS13上Apple對(duì)于瀏覽器訪問(wèn)設(shè)備運(yùn)動(dòng)和方向事件在默認(rèn)情況下處于關(guān)閉狀態(tài).
前端解決方案如下:


image.png

警告:Call to requestPermission() failed, reason: Browsing context is not secure 訪問(wèn)鏈接改成https即可霎俩。

(5) evaluateJavaScript:WK調(diào)用JS方法,completionHandler返回值的id類型。

JS方法:


image.png

completionHandler:返回的值是個(gè)NSCFBoolean,切記不能當(dāng)作BOOL值強(qiáng)轉(zhuǎn)處理,因?yàn)镹SCFBoolean本身是個(gè)NSNumber對(duì)象仁堪。

image.png

(6)Webkit:JScore深入理解

1 從iOS7之后,JScore作為系統(tǒng)的Framework被蘋果提供個(gè)開(kāi)發(fā)者,Webkit作為iOS的頁(yè)面渲染及邏輯處理引擎,HTML5通過(guò)WebKit處理后就可以顯示界面,WebKit框架中主要包括:WebCore,JScore,WebKit Embedding API,WebKit Ports哮洽。
2 WebKit Embedding API:負(fù)責(zé)瀏覽器和UI交互。
3 WebKit Ports:讓WebKit方便移植到各個(gè)操作系統(tǒng)上弦聂。
4 WebCore:是WebKit中最核心的渲染引擎鸟辅。
5 JScore: 是WebKit的JS默認(rèn)內(nèi)嵌引擎。
5.1 JSCore采用基于寄存器的指令結(jié)構(gòu),寄存器指令 執(zhí)行效率更高莺葫。但是由于這樣的架構(gòu)也造成內(nèi)存開(kāi)銷更大的問(wèn)題,還存在移植性弱的問(wèn)題匪凉,因?yàn)樘摂M機(jī)中的虛擬寄存器需要去匹配到真實(shí)機(jī)器中CPU的寄存器,可能會(huì)存在真實(shí)CPU寄存器不足的問(wèn)題捺檬。
5.2 JSCore單線程機(jī)制,整個(gè)js在一條線程中執(zhí)行再层。
5.3 JSCore事件驅(qū)動(dòng)機(jī)制。
5.4 WKWebview中,也封裝了JSCore,但無(wú)法使用系統(tǒng)的JSCore Framework;下面簡(jiǎn)單學(xué)習(xí)記錄下JSCore Framework的幾個(gè)重要概念。
5.4.1 JSVM
一個(gè)JSVirtualMachine(以下簡(jiǎn)稱JSVM)實(shí)例代表了一個(gè)自包含的JS運(yùn)行環(huán)境聂受,或者是一系列JS運(yùn)行所需的資源蒿秦。該類有兩個(gè)主要的使用用途:一是支持并發(fā)的JS調(diào)用,二是管理JS和Native之間橋?qū)ο蟮膬?nèi)存.
JSCore被認(rèn)為是一個(gè)虛擬機(jī)蛋济,那JSVM又是什么渤早?實(shí)際上,JSVM就是一個(gè)抽象的JS虛擬機(jī)瘫俊,讓開(kāi)發(fā)者可以直接操作鹊杖。在App中,我們可以運(yùn)行多個(gè)JSVM來(lái)執(zhí)行不同的任務(wù)扛芽。而且每一個(gè)JSContext都從屬于一個(gè)JSVM骂蓖。但是需要注意的是每個(gè)JSVM都有自己獨(dú)立的堆空間,GC也只能處理JSVM內(nèi)部的對(duì)象川尖。所以說(shuō)登下,不同的JSVM之間是無(wú)法傳遞值的。
5.4.2 JSContext

__weak typeof (UIWebView *)weakWebView = webView;
JSContext *Context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//weview 持有JSContext 這個(gè)會(huì)使得引用系數(shù)+1
 Context[BridgeObject] = ^(){};

在UIWebview中通過(guò)KVC獲取JSContext,然后調(diào)用JS,訪問(wèn)JS的值和函數(shù),提供JS訪問(wèn)Native的接口叮喳。

  Objective-C type  |   JavaScript type
 --------------------+---------------------
         nil         |     undefined
        NSNull       |        null
       NSString      |       string
       NSNumber      |   number, boolean
     NSDictionary    |   Object object
       NSArray       |    Array object
        NSDate       |     Date object
       NSBlock            |   Function object 
          id         |   Wrapper object 
        Class        | Constructor object

5.4.3 JSExport

(7) WK下鍵盤彈起,無(wú)法輸入問(wèn)題,UIWebview正常 如圖:

111111.gif

前端代碼


image.png

App端的原因:
通過(guò)排查,發(fā)現(xiàn)客戶端WK協(xié)議中實(shí)現(xiàn)了[self evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil]; [self evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none';" completionHandler:nil]; 這個(gè)兩個(gè)限制是為了防止用戶長(zhǎng)按彈出一些系統(tǒng)彈窗,強(qiáng)迫癥把它去掉了,沒(méi)想到影響這個(gè)鍵盤彈起輸入,按理說(shuō)長(zhǎng)按和鍵盤輸入八竿子打不著啊 ,怎么這樣呢,繼續(xù)入坑查.....

-webkit-touch-callout:當(dāng)觸摸并按住觸摸目標(biāo)時(shí)候是否彈出系統(tǒng)默認(rèn)彈窗,默認(rèn)值為inherit不彈出, 當(dāng)值為none靜止展示系統(tǒng)默認(rèn)彈窗.

document.documentElement.style.webkitUserSelect='none';:在WK中屏蔽長(zhǎng)按彈出默認(rèn)的UIMenuController 真機(jī)debug調(diào)試界面得出結(jié)論:document.documentElement.style.webkitUserSelect='none'; 影響了html的contenteditable屬性,導(dǎo)致鍵盤無(wú)法輸入.

(8) WK下鍵盤彈起,報(bào)錯(cuò)日志

當(dāng)鍵盤彈出時(shí)被芳,Xcode提示(WKWebView constrains issue when keyboard pops up),未解決.

(9) didFailNavigationdidFailProvisionalNavigation 的區(qū)別

這兩個(gè)方法容易被混淆,本人居然一直使用didFailNavigation監(jiān)聽(tīng)webveiw請(qǐng)求失敗的操作,而且上線運(yùn)行了2個(gè)版本,啊啊啊!!!

/*! @abstract Invoked when an error occurs during a committed main frame(在提交的主框架期間發(fā)生錯(cuò)誤時(shí)調(diào)用)
 navigation.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
 */
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
/*! @abstract Invoked when an error occurs while starting to load data for
 the main frame(在開(kāi)始為加載數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤時(shí)調(diào)用).
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

(9) WK error: Error Domain=NSURLErrorDomain Code=-1007 "too many HTTP redirects":

image.png

抓包分析:地址302重定向,客戶端似乎沒(méi)有發(fā)起請(qǐng)求,導(dǎo)致一直重定向地址. 302跳轉(zhuǎn)異常.
客戶端原因:
在302跳轉(zhuǎn)中,重寫請(qǐng)求head信息,導(dǎo)致跳轉(zhuǎn)下一個(gè)界面時(shí),上一個(gè)界面設(shè)置的cookie丟失了. 再次體會(huì)下大神的總結(jié).

image.png

(10) Error Domain=WKErrorDomain Code=3 "WKWebView已失效" UserInfo={NSLocalizedDescription=WKWebView已失效}

出現(xiàn)這種情況,在AppDelegate中獲取ua時(shí),代碼如下

 @objc class func wksetUserAgent(){
       
   let  webView = WKWebView.init()
      
webView?.evaluateJavaScript("window.navigator.userAgent;") { (result:Any?, error:Error?) in
       if(error != nil){
           debugPrint(error ?? "userAgent獲取失敗");
       }else{
           debugPrint(result ?? "userAgent獲取成功")
           
       }
     }
  
   }

swift類提前釋放造成的,可以將當(dāng)前類改成單利(不建議使用單利),定義一個(gè)全局局部存儲(chǔ)屬性.

//WK 設(shè)置UA
var  webView:WKWebView?
class BaseHelper: NSObject 

 @objc  func wksetUserAgent(){
        
    webView = WKWebView.init()
       
    webView?.evaluateJavaScript("window.navigator.userAgent;") { [weak self](result:Any?, error:Error?) in
        
        let strongSelf = self
        
        if(error != nil){
            debugPrint(error ?? "userAgent獲取失敗");
        }else{
            debugPrint(result ?? "userAgent獲取成功")
            
        }
        //strongSelf?.webView = nil;
      }
        
    }
    
    
    deinit {
        debugPrint("釋放了");
    }

如果是OC的話,屬性強(qiáng)引用下,在block置空下

@property(nonatomic, strong) WKWebView * webView; // 強(qiáng)引用一下

(10) WK截取整個(gè)Html界面

+(UIImage *)wk_saveWebviewImage:(WKWebView *)webview
{
  UIGraphicsBeginImageContextWithOptions(webview.bounds.size, webview.opaque, 0.0);
  //    [webview.layer renderInContext:UIGraphicsGetCurrentContext()];
  //    UIImage *senderimage = UIGraphicsGetImageFromCurrentImageContext();
  //    UIGraphicsEndImageContext();
  //
  //    return senderimage;
  
  CGFloat scale = [UIScreen mainScreen].scale;
  
  CGSize boundsSize = webview.bounds.size;
  CGFloat boundsWidth = boundsSize.width;
  CGFloat boundsHeight = boundsSize.height;
  
  CGSize contentSize = webview.scrollView.contentSize;
  CGFloat contentHeight = contentSize.height;
  //    CGFloat contentWidth = contentSize.width;
  
  CGPoint offset = webview.scrollView.contentOffset;
  
  [webview.scrollView setContentOffset:CGPointMake(0, 0)];
  
  NSMutableArray *images = [NSMutableArray array];
  while (contentHeight > 0) {
      UIGraphicsBeginImageContextWithOptions(boundsSize, NO, [UIScreen mainScreen].scale);
      [webview.layer renderInContext:UIGraphicsGetCurrentContext()];
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      [images addObject:image];
      
      CGFloat offsetY = webview.scrollView.contentOffset.y;
      [webview.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)];
      contentHeight -= boundsHeight;
  }
  
  
  [webview.scrollView setContentOffset:offset];
  
  CGSize imageSize = CGSizeMake(contentSize.width * scale,
                                contentSize.height * scale);
  UIGraphicsBeginImageContext(imageSize);
  [images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) {
      [image drawInRect:CGRectMake(0,
                                   scale * boundsHeight * idx,
                                   scale * boundsWidth,
                                   scale * boundsHeight)];
  }];
  UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  // UIImageView * snapshotView = [[UIImageView alloc]initWithFrame:CGRectMake(0,0, webview.scrollView.contentSize.width, webview.scrollView.contentSize.height)];
  // 拉伸圖片
  //snapshotView.image = [fullImage resizableImageWithCapInsets:UIEdgeInsetsZero];
  return fullImage;
}

本人還是沿用以前UIWebveiw的方法,代碼如上;功能藏得比較深,也沒(méi)有做測(cè)試,只是替換,通過(guò)幾個(gè)版本迭代,發(fā)現(xiàn)截取界面有空白;參考https://www.cnblogs.com/breezemist/p/7569798.html,文章提供的github項(xiàng)目使用偶然會(huì)也會(huì)出現(xiàn)界面空白的情況,還有一些非必須的截取bug,比如界面加載動(dòng)畫View也被截取上了,不符合要求.
最后將這個(gè)操作由前端實(shí)現(xiàn);前端繪制界面,以base64的形式傳給客戶端解析,這種做法的缺陷也很明顯,有卡頓,有時(shí)需要好幾秒;甚至有些設(shè)備會(huì)出現(xiàn)圖片空白的情況;應(yīng)該是某些舊的設(shè)備因?yàn)橘Y源開(kāi)銷問(wèn)題(繪制圖片耗時(shí)等),WKwebview崩潰了,同時(shí)觸發(fā)調(diào)用了webViewWebContentProcessDidTerminate方法

image.png

客戶端主要實(shí)現(xiàn)

  NSArray *imageArray = [base64Str componentsSeparatedByString:@","];
  screenImages = [UIImage imageWithData:[[imageArray objectAtIndex:1] base64DecodedData]];

有好的做法,歡迎交流.

(11) webview ur包含&provId=##provId##&secCode=##secCode##&sign=##sign##&sysId 這種##的地址界面展示空白,無(wú)法響應(yīng)webvew協(xié)議方法的問(wèn)題

有人說(shuō)是##被編碼引起,但客戶端只是對(duì)漢字編碼,所以應(yīng)該不是這個(gè)原因. ?????

未完待續(xù)

排查工程中UIWebView grep -r UIWebView .

JSCore參考文獻(xiàn)
https://www.cnblogs.com/meituantech/p/9528285.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市馍悟,隨后出現(xiàn)的幾起案子畔濒,更是在濱河造成了極大的恐慌,老刑警劉巖锣咒,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侵状,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡毅整,警方通過(guò)查閱死者的電腦和手機(jī)趣兄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悼嫉,“玉大人艇潭,你說(shuō)我怎么就攤上這事∠访铮” “怎么了蹋凝?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辛臊。 經(jīng)常有香客問(wèn)我仙粱,道長(zhǎng),這世上最難降的妖魔是什么彻舰? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任伐割,我火速辦了婚禮候味,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘隔心。我一直安慰自己白群,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布硬霍。 她就那樣靜靜地躺著帜慢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唯卖。 梳的紋絲不亂的頭發(fā)上粱玲,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音拜轨,去河邊找鬼抽减。 笑死,一個(gè)胖子當(dāng)著我的面吹牛橄碾,可吹牛的內(nèi)容都是我干的卵沉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼法牲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼史汗!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拒垃,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤停撞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后恶复,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體怜森,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年谤牡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姥宝。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翅萤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腊满,到底是詐尸還是另有隱情套么,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布碳蛋,位于F島的核電站胚泌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏肃弟。R本人自食惡果不足惜玷室,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一零蓉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧穷缤,春花似錦敌蜂、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至身坐,卻和暖如春秸脱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背部蛇。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工摊唇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搪花。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓遏片,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親撮竿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吮便,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355