WKWebView詳解&WKWebVieW和JS交互

開發App的過程中,常常會遇到在App內部加載網頁,通常用UIWebView加載。而這個自iOS2.0開始使用的Web容器一直是開發的心病:加載速度慢,占用內存多,優化困難。如果加載網頁多,還可能因為過量占用內存而給系統kill掉。各種優化的方法效果也不那么明顯iOS8 以后,蘋果推出了新框架 WebKit,提供了替換 UIWebView 的組件 WKWebView。各種 UIWebView 的性能問題沒有了,速度更快了,占用內存少了,體驗更好了,下面列舉一些其它的優勢:
1、在性能、穩定性、功能方面有很大提升(最直觀的體現就是加載網頁是占用的內存,模擬器加載百度與開源中國網站時,WKWebView占用23M,而UIWebView占用85M);
2、允許JavaScript的Nitro庫加載并使用(UIWebView中限制);
3、支持了更多的HTML5特性;
4、高達60fps的滾動刷新率以及內置手勢;
5、將UIWebViewDelegate與UIWebView重構成了14類與3個協議(查看蘋果官方文檔);

14個類

WKBackForwardList: 之前訪問過的 web 頁面的列表,可以通過后退和前進動作來訪問到。
WKBackForwardListItem: webview 中后退列表里的某一個網頁。
WKFrameInfo: 包含一個網頁的布局信息。
WKNavigation: 包含一個網頁的加載進度信息。
WKNavigationAction: 包含可能讓網頁導航變化的信息,用于判斷是否做出導航變化。
WKNavigationResponse: 包含可能讓網頁導航變化的返回內容信息,用于判斷是否做出導航變化。
WKPreferences: 概括一個 webview 的偏好設置。
WKProcessPool: 表示一個 web 內容加載池。
WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。
WKScriptMessage: 包含網頁發出的信息。
WKUserScript: 表示可以被網頁接受的用戶腳本。
WKWebViewConfiguration: 初始化 webview 的設置。
WKWindowFeatures: 指定加載新網頁時的窗口屬性。

3個協議

WKNavigationDelegate: 提供了追蹤主窗口網頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法。
WKUIDelegate: 提供用原生控件顯示網頁的方法回調。
WKScriptMessageHandler: 提供從網頁中收消息的回調方法。

所有相關的類的API

//上文介紹過的偏好配置
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
// 導航代理 
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// 用戶交互代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;

// 頁面前進、后退列表
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

// 默認構造器
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;


//加載請求API
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

// 加載URL
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);

// 直接加載HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

// 直接加載data
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 9_0);

// 前進或者后退到某一頁面
- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;

// 頁面的標題,支持KVO的
@property (nullable, nonatomic, readonly, copy) NSString *title;

// 當前請求的URL,支持KVO的
@property (nullable, nonatomic, readonly, copy) NSURL *URL;

// 標識當前是否正在加載內容中,支持KVO的
@property (nonatomic, readonly, getter=isLoading) BOOL loading;

// 當前加載的進度,范圍為[0, 1]
@property (nonatomic, readonly) double estimatedProgress;

// 標識頁面中的所有資源是否通過安全加密連接來加載,支持KVO的
@property (nonatomic, readonly) BOOL hasOnlySecureContent;

// 當前導航的證書鏈,支持KVO
@property (nonatomic, readonly, copy) NSArray *certificateChain NS_AVAILABLE(10_11, 9_0);

// 是否可以招待goback操作,它是支持KVO的
@property (nonatomic, readonly) BOOL canGoBack;

// 是否可以執行gofarward操作,支持KVO
@property (nonatomic, readonly) BOOL canGoForward;

// 返回上一頁面,如果不能返回,則什么也不干
- (nullable WKNavigation *)goBack;

// 進入下一頁面,如果不能前進,則什么也不干
- (nullable WKNavigation *)goForward;

// 重新載入頁面
- (nullable WKNavigation *)reload;

// 重新從原始URL載入
- (nullable WKNavigation *)reloadFromOrigin;

// 停止加載數據
- (void)stopLoading;

// 執行JS代碼
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;

// 標識是否支持左、右swipe手勢是否可以前進、后退
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

// 自定義user agent,如果沒有則為nil
@property (nullable, nonatomic, copy) NSString *customUserAgent NS_AVAILABLE(10_11, 9_0);

// 在iOS上默認為NO,標識不允許鏈接預覽
@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE(10_11, 9_0);

#if TARGET_OS_IPHONE
/*! @abstract The scroll view associated with the web view.
 */
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
#endif

#if !TARGET_OS_IPHONE
// 標識是否支持放大手勢,默認為NO
@property (nonatomic) BOOL allowsMagnification;

// 放大因子,默認為1
@property (nonatomic) CGFloat magnification;

// 根據設置的縮放因子來縮放頁面,并居中顯示結果在指定的點
- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;

使用

1、首先需要先引入WebKit庫

#import <WebKit/WebKit.h>

2、兩種初始化方法

// 默認初始化
- (instancetype)initWithFrame:(CGRect)frame;

// 根據對webview的相關配置,進行初始化
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

3、加載網頁

最基礎的方法和UIWebView一樣

NSURL *url = [NSURL URLWithString:@"www.lxweimin.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

一些其他的加載方法

//加載本地URL文件
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL 
               allowingReadAccessToURL:(NSURL *)readAccessURL

//加載本地HTML字符串
- (nullable WKNavigation *)loadHTMLString:(NSString *)string
                                  baseURL:(nullable NSURL *)baseURL;
//加載二進制數據
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL

4、代理方法

1、【WKNavigationDelegate協議】

該代理提供的方法,可以用來追蹤加載過程(頁面開始加載、加載完成、加載失敗)、決定是否執行跳轉。

// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

頁面跳轉的代理方法有三種,分為(收到跳轉與決定是否跳轉兩種)

// 接收到服務器跳轉請求之后調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到響應后,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在發送請求之前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
2、【WKUIDelegate協議】

WKUIDelegate從名稱能看出它是webView在user interface上的代理,共有5個可選類型的代理方法。它為webView提供了原生的彈框,而不是JavaScript里的提示框。雖然JavaScript的提示框可以做的跟原生一樣,但是對于ios開發者來說,如果要更改提示框就不方便了。提供這個代理,可以讓ios端更加靈活的修改提示框的樣式。

// 新建WKWebView
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

// 關閉WKWebView
- (void)webViewDidClose:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);

// 對應js的Alert方法
/**
 *  web界面中有彈出警告框時調用
 *
 *  @param webView           實現該代理的webview
 *  @param message           警告框中的內容
 *  @param frame             主窗口
 *  @param completionHandler 警告框消失調用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;

// 對應js的confirm方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;

// 對應js的prompt方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
3、【WKScriptMessageHandler】

這個協議中包含一個必須實現的方法,這個方法是native與web端交互的關鍵,它可以直接將接收到的JS腳本轉為OC或Swift對象。

// 從web界面中接收到一個腳本時調用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

重點之WKWebVieW和JS交互

HTML代碼

<html>
    <!--描述網頁信息-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>title</title>
        <style>
            *{
                font-size: 50px;
            }
        
            .btn{height:80px; width:80%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white}
        </style>
        
        <script>
            
        
            //OC調用JS的方法列表
            function alertMobile() {
                document.getElementById('mobile').innerHTML = '不帶參數'
            }

            function alertName(msg) {
                //有一個參數
                document.getElementById('name').innerHTML = '有一個參數 :' + msg
            }

            function alertSendMsg(num,msg) {
                //有兩個參數
                document.getElementById('msg').innerHTML = '有兩個參數:' + num + ',' + msg + '!!'
            }
        
            //JS響應方法列表
            function btnClick1() {
                window.webkit.messageHandlers.showMobile.postMessage(null)
            }

            function btnClick2() {
                window.webkit.messageHandlers.showName.postMessage('有一個參數')
            }

            function btnClick3() {
                window.webkit.messageHandlers.showSendMsg.postMessage(['兩個參數One', '兩個參數Two'])
            }

        </script>
        
        
    </head>

    <!--網頁具體內容-->
    <body>
        <br/>

        <div>
            <label>WKWebView&JS交互</label>
        </div>
        <br/>

        <div id="mobile"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick1()">不帶參數</button>
        </div>
        <br/>
        
        <div id="name"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick2()">一個參數</button>
        </div>
        <br/>
        
        <div id="msg"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick3()">兩個參數</button>
        </div>


    </body>
</html>

OC代碼

需要的頭文件

#import <WebKit/WebKit.h>

需要遵守的代理<WKScriptMessageHandler>

1、設置偏好設置,以及JS調用OC 添加處理腳本,這里的內容我寫在了viewDidLoad里面,但是需要注意的是,需要在我們結束的時候釋放WKUserContentController,不然會造成內存泄漏

- (void)viewDidLoad {
    [super viewDidLoad];
    // 設置偏好設置
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    // 默認為0
    config.preferences.minimumFontSize = 10;
    //是否支持JavaScript
    config.preferences.javaScriptEnabled = YES;
    //不通過用戶交互,是否可以打開窗口
    config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
    
    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height/2) configuration:config];
    [self.view addSubview:self.webView];

    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSURL *baseURL = [[NSBundle mainBundle] bundleURL];
    [self.webView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseURL];
    
    WKUserContentController *userCC = config.userContentController;
    //JS調用OC 添加處理腳本
    [userCC addScriptMessageHandler:self name:@"showMobile"];
    [userCC addScriptMessageHandler:self name:@"showName"];
    [userCC addScriptMessageHandler:self name:@"showSendMsg"];
    
}

2、釋放WKUserContentController代碼

-(void)removeAllScriptMsgHandle{
    WKUserContentController *controller = self.webView.configuration.userContentController;
    [controller removeScriptMessageHandlerForName:@"showMobile"];
    [controller removeScriptMessageHandlerForName:@"showName"];
    [controller removeScriptMessageHandlerForName:@"showSendMsg"];
}

在JS調用OC以后會走的代理

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    NSLog(@"%@",message.body);
    
    if ([message.name isEqualToString:@"showMobile"]) {
        [self showMsg:@"沒有參數"];
    }
    
    if ([message.name isEqualToString:@"showName"]) {
        NSString *info = [NSString stringWithFormat:@"%@",message.body];
        [self showMsg:info];
    }
    
    if ([message.name isEqualToString:@"showSendMsg"]) {
        NSArray *array = message.body;
        NSString *info = [NSString stringWithFormat:@"有兩個參數: %@, %@ !!",array.firstObject,array.lastObject];
        [self showMsg:info];
    }
}

網頁加載完成之后調用JS代碼才會執行,因為這個時候html頁面已經注入到webView中并且可以響應到對應方法。OC調用JS代碼

//不帶參數
- (IBAction)NOParameter:(id)sender {
    [self.webView evaluateJavaScript:@"alertMobile()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        //JS 返回結果
        NSLog(@"%@ %@",response,error);
    }];
}
//一個參數
- (IBAction)oneParameter:(id)sender {
    /*
     *alertName('奧特們打小怪獸')
     *alertName JS方法名
     *奧特們打小怪獸 帶的參數
     */
    [self.webView evaluateJavaScript:@"alertName('奧特們打小怪獸')" completionHandler:nil];
}
//兩個參數
- (IBAction)twoParameter:(id)sender {
    /*
     *alertSendMsg('我是參數1','我是參數2')
     *alertSendMsg JS方法名
     *我是參數1 帶的參數
     *我是參數2
     */
    [self.webView evaluateJavaScript:@"alertSendMsg('我是參數1','我是參數2')" completionHandler:nil];
}

- (void)showMsg:(NSString *)msg {
    [[[UIAlertView alloc] initWithTitle:nil message:msg delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
}

解決POST請求傳參不管用問題

當我們用UIWebView POST請求傳參時一般是這樣寫的

// 創建WebView
    UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 設置訪問的URL
    NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
    // 根據URL創建請求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 設置請求方法為POST
    [request setHTTPMethod:@"POST"];
    // 設置請求參數
    [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
    // WebView加載請求
    [webView loadRequest:request];
    // 將WebView添加到視圖
    [self.view addSubview:webView];

但是當我們用WKWebView這樣加載的時候并沒有什么卵用。

解決方法

參考文章:
http://stackoverflow.com/questions/26253133/cant-set-headers-on-my-wkwebview-post-request
http://www.lxweimin.com/p/403853b63537

1、將一個包含JavaScript的POST請求的HTML代碼放到工程目錄中
2、加載這個包含JavaScript的POST請求的代碼到WKWebView
3、加載完成之后,用Native調用JavaScript的POST方法并傳入參數來完成請求

1、創建包含JavaScript的POST請求的HTML代碼
<html>
 <head>
     <script>
         //調用格式: post('URL', {"key": "value"});
         function post(path, params) {
             var method = "post";
             var form = document.createElement("form");
             form.setAttribute("method", method);
             form.setAttribute("action", path);

             for(var key in params) {
                 if(params.hasOwnProperty(key)) {
                     var hiddenField = document.createElement("input");
                     hiddenField.setAttribute("type", "hidden");
                     hiddenField.setAttribute("name", key);
                     hiddenField.setAttribute("value", params[key]);

                     form.appendChild(hiddenField);
                 }
             }
             document.body.appendChild(form);
             form.submit();
         }
     </script>
 </head>
 <body>
 </body>
</html>

將這段代碼拷貝下來,然后粘貼到文本編輯器中,名字可以隨意起,比方說保存為:JSPOST.html,然后拷貝到工程目錄中,記得選擇對應的Target和勾選Copy items if needed(默認應該是勾選的)。這時候,就可以用這段JavaScript代碼來發送帶參數的POST請求了。

2、將對應的JavaScript代碼通過加載本地網頁的形式加載到WKWebView
 // JS發送POST的Flag,為真的時候會調用JS的POST方法(僅當第一次的時候加載本地JS)
 self.needLoadJSPOST = YES;
 // 創建WKWebView
 self.webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
 //設置代理
 self.webView.navigationDelegate = self;
 // 獲取JS所在的路徑
 NSString *path = [[NSBundle mainBundle] pathForResource:@"JSPOST" ofType:@"html"];
 // 獲得html內容
 NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
 // 加載js
 [self.webView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
 // 將WKWebView添加到當前View
 [self.view addSubview:self.webView];
3、Native調用JavaScript腳本并傳入參數來完成POST請求

這里需要用到WKWebView和JS的交互

 // 加載完成的代理方法
 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
     // 判斷是否需要加載(僅在第一次加載)
     if (self.needLoadJSPOST) {
         // 調用使用JS發送POST請求的方法
         [self postRequestWithJS];
         // 將Flag置為NO(后面就不需要加載了)
         self.needLoadJSPOST = NO;
     }
 }

 // 調用JS發送POST請求
 - (void)postRequestWithJS {
     // 發送POST的參數
     NSString *postData = @"\"username\":\"aaa\",\"password\":\"123\"";
     // 請求的頁面地址
     NSString *urlStr = @"http://www.postexample.com";
     // 拼裝成調用JavaScript的字符串
     NSString *jscript = [NSString stringWithFormat:@"post('%@', {%@});", urlStr, postData];

     // NSLog(@"Javascript: %@", jscript);
     // 調用JS代碼
     [self.webView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {

     }];
 }
點贊吧,伙計們!!!

github地址:https://github.com/jiangjunhui1993/KWWebView-JS-

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容