kerkee 之Web和iOS開發(fā)使用篇

簡單認(rèn)識:kerkee 是一個(gè)多主體共存型 Hybrid 框架洲胖,具有跨平臺、用戶體驗(yàn)好坯沪、性能高绿映、擴(kuò)展性好、靈活性強(qiáng)腐晾、易維護(hù)叉弦、規(guī)范化、集成云服務(wù)藻糖、具有Debug環(huán)境淹冰、徹底解決跨域問題。該框架從開發(fā)者角度支持三種團(tuán)隊(duì)開發(fā)模式:Web開發(fā)者 巨柒、Native開發(fā)者 樱拴、Web開發(fā)者和Native團(tuán)隊(duì)共同合作的開發(fā)團(tuán)隊(duì) 柠衍。

下面我將從 Web開發(fā)者和Native(iOS)團(tuán)隊(duì)共同合作的開發(fā)團(tuán)隊(duì) 模式來分析使用該框架。

一晶乔、相關(guān)資料:

官網(wǎng)地址:http://www.kerkee.com

Github 源碼地址:https://github.com/kercer

QQ交流群:110710084

二珍坊、簡單的使用:

[kerkee在iOS上的快速上手指南](http://blog.linzihong.com/kerkeezai-iosshang-de-kuai-su-sha ng-shou-zhi-nan/)

1、建立新項(xiàng)目 kerkeeHDDemo 在項(xiàng)目目錄中建立 Podfile 文件:

platform :ios, '7.0'

inhibit_all_warnings!
pod ‘kerkee’, ’~> 1.0.1’  

2正罢、使用 CocoaPods 來導(dǎo)入 kerkee 框架阵漏,使用終端 cd 到你的 Podfile 文件所在的目錄:

cd $PodfilePath
pod install

3、運(yùn)行 Podfile 同目錄中的 kerkeeHDDemo.xcworkspace 即可翻具;

4履怯、在項(xiàng)目中添加 html 代碼:(本人對 html 不熟悉,相信懂的人肯定懂)

kerkeeTest.html :

<!--Create by Harry on 17/06/2016-->
<!--  Copyright ? 2016年 HarryDeng. All rights reserved.-->

<html>
    <head>
        <title>kerkee測試h5界面</title>
        <meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
            <script>
                function test()
                {
                    alert("test alert...");
                    return "abcd";
                }
                </script>
            </head>
    
    <body>
        <br/>
        <br/>
        
        <input type="button" value="js數(shù)據(jù)傳遞給oc" onclick="kerkeeJSManager.jsToOc('js數(shù)據(jù)傳遞給oc'); ">
            
        <br/>
            
        <input type="button" value="js數(shù)據(jù)傳遞給oc裆泳,oc回傳給js" onclick="kerkeeJSManager.mutualJSOC('js數(shù)據(jù)傳遞給oc叹洲,oc回傳給js', function(json)
                {
                console.log(JSON.stringify(json))
                }); ">
                
        <br/>
                
        <button id="hallo" onclick="buttonClick1('js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick1')">
            js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick1 </button>
        
        <br/>
        
        <button id="hallo" onclick="buttonClick2('js數(shù)據(jù)傳遞給oc,h5的方法單獨(dú)寫出buttonClick2')">
            js數(shù)據(jù)傳遞給oc,h5的方法在js文件實(shí)現(xiàn) buttonClick2 </button>
        
        <br/>
                
    </body>

            <script>
            var kerkeeJSManager;
            function buttonClick1(s1)
            {
                kerkeeJSManager.jsToOc(s1);
            }
              
            function ocToJs(s1)
            {
                console.log(JSON.stringify(s1))
            }
            </script>
            
            <!--html頁面綁定js事件,一般放在href之后工禾,如果放在之前加載JS事件會阻塞界面-->
            <script type="text/javascript" src="kerkeeTest.js"></script>
            
</html>

上面有三種方式來處理按鈕的點(diǎn)擊事件:

i. 直接在<button> </button> 中處理

ii. 使用 function 處理疹味;

iii. 使用 js 來處理 function。

代碼解釋:

代碼 理解分析
kerkeeJSManager js 這邊中 iOSjs 交互橋梁帜篇,在 iOS 也同樣存在 kerkeeJSManager 關(guān)鍵字
jsToOc() kerkeeJSManager 方法 ,使用 kerkeeJSManager.jsToOc() 可以調(diào)用 iOS 對應(yīng)的 - (void)jsToOc:(KCWebView)aWebView argList:(KCArgList)args 方法诫咱,實(shí)現(xiàn) js 調(diào)用 iOS 原生代碼**
ocToJs() iOS 中使用 [KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView]; 可以調(diào)用 jsocToJs() 方法笙隙,實(shí)現(xiàn)了 iOS 調(diào)用 js 代碼

kerkeeTest.js :

var kerkeeJSManager;
function buttonClick2(s1)
{
    kerkeeJSManager.jsToOc(s1);
}

5、在項(xiàng)目中添加 iOS 代碼:

HDJSToOCManager.h

#import <Foundation/Foundation.h>
#import "KCJSObject.h"
#import "KCArgList.h"

// 這個(gè)類作為 和 js 交互橋梁類
@interface HDJSToOCManager : KCJSObject

- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args;

- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args;

@end

HDJSToOCManager.m

#import "HDJSToOCManager.h"

#import <UIKit/UIKit.h>
#import "KCJSBridge.h"

@implementation HDJSToOCManager

- (NSString*)getJSObjectName{
    // 這個(gè) 和 js 中的變量要保持一致
    return @"kerkeeJSManager";
}
// js 中 可以調(diào)用 jsToOc() 來調(diào)用
- (void)jsToOc:(KCWebView*)aWebView argList:(KCArgList*)args{
    NSLog(@"JS調(diào)用OC args : %@", args);
}

- (void)mutualJSOC:(KCWebView*)aWebView argList:(KCArgList*)args{
    NSLog(@"JS調(diào)用OC坎缭,OC回調(diào)JS args : %@", args);
    
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:@"success" forKey:@"info"];
    NSString *json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:dic options:0 error:nil] encoding:NSUTF8StringEncoding];
    KCAutorelease(json);
    //回調(diào)竟痰,callbackId,kerkee.js 內(nèi)部已經(jīng)處理好
    [KCJSBridge callbackJS:aWebView callBackID:[args getObject:@"callbackId"] jsonString:json];
}

@end

這個(gè)類主要是生成和 js 對應(yīng)的變量 kerkeeJSManager 掏呼,是 iOS 這邊 iOSjs 交互橋梁

ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    
     // 將 HDJSToOCManager對象 和 js中的 kerkeeJSManager (詳見HDJSToOCManager)綁定
    [KCJSBridge registObject:[[HDJSToOCManager alloc] init]];
    
    NSString *file = [[NSBundle mainBundle] pathForResource:@"kerkeeTest" ofType:@"html"];
    NSURL *url = [NSURL URLWithString:file];
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];
    m_webView = [[KCWebView alloc] initWithFrame:self.view.bounds];
    [m_webView loadRequest:request];
    [self.view addSubview:m_webView];
    
    // 在m_webView 加載 “kerkee.js” 代碼坏快,具體代碼見 KCApiBridge.m 中的 init 方法 (這個(gè)時(shí)候需要在項(xiàng)目中添加“kerkee.js” 文件)
    m_jsBridge = [[KCJSBridge alloc] initWithWebView:m_webView delegate:self];
    
    UIButton *bb = [UIButton buttonWithType:UIButtonTypeInfoLight];
    bb.frame = CGRectMake(100, 400, 50, 50);
    [bb addTarget:self action:@selector(ocToJs) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:bb];
}

- (void)ocToJs{
    [KCJSBridge callJSFunction:@"ocToJs" withJSONObject:@{@"hhh" : @"www"} WebView:m_webView];
}

#pragma mark --
#pragma mark KCWebViewProgressDelegate
-(void)webView:(KCWebView*)webView identifierForInitialRequest:(NSURLRequest*)initialRequest{
    
}

#pragma mark - UIWebView Delegate
- (void)webViewDidFinishLoad:(UIWebView *)aWebView{
    NSString *scrollHeight = [aWebView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
    NSLog(@"scrollHeight: %@", scrollHeight);
    NSLog(@"webview.contentSize.height %f", aWebView.scrollView.contentSize.height);
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
    
}

- (BOOL)webView:(UIWebView *)aWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    return YES;
}

上面的代碼并不多,其核心代碼之一在 KCApiBridge 中憎夷,可以看到莽鸿, KCApiBridge 在初始化的時(shí)候找到 kerkee.js 并且使用 - (nullable NSString )stringByEvaluatingJavaScriptFromString:(NSString )script;iOSUIWebView 注入 kerkee.js 代碼。這份 js 才是真正的**將 iOS 的橋梁 HDJSToOCManager 實(shí)體和 js變量 kerkeeJSManager 的關(guān)鍵代碼拾给。在 這里 可以看到作者在demo中的 kerkee.js 源碼祥得。因?yàn)槲覍?js 也是入門階段,所以選擇在源碼上改動之后蒋得,代碼如下:

;(function(window){
    if (window.WebViewJSBridge)
        return;
    window.WebViewJSBridge = {
  
  };

    console.log("--- kerkee init begin---");
    var browser={
        versions:function(){
            var u = navigator.userAgent, app = navigator.appVersion;
            return {
                trident: u.indexOf('Trident') > -1, //IE
                presto: u.indexOf('Presto') > -1, //opera
                webKit: u.indexOf('AppleWebKit') > -1, //apple&google kernel
                gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//firfox
                mobile: !!u.match(/AppleWebKit.*Mobile.*/), //is Mobile
                ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //is ios
                android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android
                iPhone: u.indexOf('iPhone') > -1 , //iPhone or QQHD
                iPad: u.indexOf('iPad') > -1, //iPad
                iPod: u.indexOf('iPod') > -1, //iPod
                webApp: u.indexOf('Safari') == -1, //is webapp,no header and footer
                weixin: u.indexOf('MicroMessenger') > -1, //is wechat
                qq: u.match(/\sQQ/i) == " qq" //is qq
            };
        }(),
        language:(navigator.browserLanguage || navigator.language).toLowerCase()
    }

    var global = this || window;
                               
    var ApiBridge ={
        msgQueue : [],
        callbackCache : [],
        callbackId : 0,
        processingMsg : false,
        isReady : false,
        isNotifyReady : false
    };

    ApiBridge.create = function()
    {                  
        ApiBridge.bridgeIframe = document.createElement("iframe");
        ApiBridge.bridgeIframe.style.display = 'none';
        document.documentElement.appendChild(ApiBridge.bridgeIframe);
    };

    ApiBridge.prepareProcessingMessages = function()
    {
        ApiBridge.processingMsg = true;
    };

    ApiBridge.fetchMessages = function()
    {
        if (ApiBridge.msgQueue.length > 0)
        {
            var messages = JSON.stringify(ApiBridge.msgQueue);
            ApiBridge.msgQueue.length = 0;
            return messages;
        }
        ApiBridge.processingMsg = false;
        return null;
    };

    ApiBridge.callNative = function(clz, method, args, callback)
    {
        var msgJson = {};
        msgJson.clz = clz;
        msgJson.method = method;
        if (args != undefined)
            msgJson.args = args;

        if (callback)
        {
            var callbackId = ApiBridge.getCallbackId();
            ApiBridge.callbackCache[callbackId] = callback;
            if (msgJson.args)
            {
                msgJson.args.callbackId = callbackId.toString();
            }
            else
            {
                msgJson.args =
                {
                    "callbackId" : callbackId.toString()
                };
            }
        }

        if (browser.versions.ios)
        {
            if (ApiBridge.bridgeIframe == undefined)
            {
                ApiBridge.create();
            }
            // var msgJson = {"clz": clz, "method": method, "args": args};
            ApiBridge.msgQueue.push(msgJson);
            if (!ApiBridge.processingMsg)
                ApiBridge.bridgeIframe.src = "kcnative://go";
        }
        else if (browser.versions.android)
        {
            // android
            return prompt(JSON.stringify(msgJson));
        }

    };

    ApiBridge.log = function(msg)
    {
        ApiBridge.callNative("ApiBridge", "JSLog",
        {
            "msg" : msg
        });
    }

    ApiBridge.getCallbackId = function()
    {
        return ApiBridge.callbackId++;
    }

    ApiBridge.onCallback = function(callbackId, obj)
    {
        if (ApiBridge.callbackCache[callbackId])
        {
            ApiBridge.callbackCache[callbackId](obj);
            // ApiBridge.callbackCache[callbackId] = undefined;
            // //如果是注冊事件的話级及,不能undefined;
        }
    }

    ApiBridge.onBridgeInitComplete = function(callback)
    {
        ApiBridge.callNative("ApiBridge", "onBridgeInitComplete", {}, callback);
    }

    ApiBridge.onNativeInitComplete = function(callback)
    {
        ApiBridge.isReady = true;
        console.log("--- kerkee onNativeInitComplete end ---");

        if (callback)
        {
            callback();
            ApiBridge.isNotifyReady = true;
            console.log("--- device ready go--- ");
        }
    }

    ApiBridge.compile = function(aIdentity, aJS)
    {
        var value;
        var error;
        try
        {
            value = eval(aJS);
        }
        catch (e)
        {
            var err = {};
            err.name = e.name;
            err.message = e.message;
            err.number = e.number & 0xFFFF;
            error = err;
        }

        ApiBridge.callNative("ApiBridge", "compile",
        {
            "identity" : aIdentity,
            "returnValue" : value,
            "error" : error
        });
    }

    var _Configs =
    {
        isOpenJSLog:false,
        sysLog:{},
        isOpenNativeXHR:true,
        sysXHR:{}
    };
    _Configs.sysLog = global.console.log;
    _Configs.sysXHR = global.XMLHttpRequest;


    var kerkee = {};

    
    /*****************************************
     * JS和iOS 交互接口
     *****************************************/
    kerkee.jsToOc = function(s1)
       {
       ApiBridge.callNative("kerkeeJSManager", "jsToOc",
            {
                "s1" : s1
            });
       };
       
       kerkee.mutualJSOC = function(aString, callback)
       {
       ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
            {
                "aString" : aString
            }, callback);
       }
       
    global.kerkeeJSManager = kerkee;
                               

    kerkee.openJSLog = function()
    {
        _Configs.isOpenJSLog = true;
        global.console.log = ApiBridge.log;
    }
    kerkee.closeJSLog = function()
    {
        _Configs.isOpenJSLog = false;
        global.console.log = _Configs.sysLog;
    }

    kerkee.onDeviceReady = function(handler)
    {
        ApiBridge.onDeviceReady = handler;

        if (ApiBridge.isReady && !ApiBridge.isNotifyReady && handler)
        {
            console.log("-- device ready --");
            handler();
            ApiBridge.isNotifyReady = true;
        }
    };

    kerkee.invoke = function(clz, method, args, callback)
    {
        if (callback)
        {
            ApiBridge.callNative(clz, method, args, callback);
        }
        else
        {
            ApiBridge.callNative(clz, method, args);
        }
    };

    kerkee.onSetImage = function(srcSuffix, desUri)
    {
        // console.log("--- kerkee onSetImage ---");
        var obj = document.querySelectorAll('img[src$="' + srcSuffix + '"]');
        for (var i = 0; i < obj.length; ++i)
        {
            obj[i].src = desUri;
        }
    };


    /*****************************************
     * XMLHttpRequest實(shí)現(xiàn)
     *****************************************/
    var _XMLHttpRequest = function()
    {
        this.id = _XMLHttpRequest.globalId++;
        _XMLHttpRequest.cache[this.id] = this;

        this.status = 0;
        this.statusText = '';
        this.readyState = 0;
        this.responseText = '';
        this.headers = {};
        this.onreadystatechange = undefined;

        ApiBridge.callNative('XMLHttpRequest', 'create',
        {
            "id" : this.id
        });
    }

    _XMLHttpRequest.globalId = 0;
    _XMLHttpRequest.cache = [];
    _XMLHttpRequest.setProperties = function(jsonObj)
    {
        var id = jsonObj.id;
        if (_XMLHttpRequest.cache[id])
        {
            var obj = _XMLHttpRequest.cache[id];

            if (jsonObj.hasOwnProperty('status'))
            {
                obj.status = jsonObj.status;
            }
            if (jsonObj.hasOwnProperty('statusText'))
            {
                obj.statusText = jsonObj.statusText;
            }
            if (jsonObj.hasOwnProperty('readyState'))
            {
                obj.readyState = jsonObj.readyState;
            }
            if (jsonObj.hasOwnProperty('responseText'))
            {
                obj.responseText = jsonObj.responseText;
            }
            if (jsonObj.hasOwnProperty('headers'))
            {
                obj.headers = jsonObj.headers;
            }

            if (_XMLHttpRequest.cache[id].onreadystatechange)
            {
                _XMLHttpRequest.cache[id].onreadystatechange();
            }
        }
    }

    _XMLHttpRequest.prototype.open = function(method, url, async)
    {
        ApiBridge.callNative('XMLHttpRequest', 'open',
        {
            "id" : this.id,
            "method" : method,
            "url" : url,
            "scheme" : window.location.protocol,
            "host" : window.location.hostname,
            "port" : window.location.port,
            "href" : window.location.href,
            "referer" : document.referrer != "" ? document.referrer : undefined,
            "useragent" : navigator.userAgent,
            "cookie" : document.cookie != "" ? document.cookie : undefined,
            "async" : async,
            "timeout" : this.timeout
        });
    }

    _XMLHttpRequest.prototype.send = function(data)
    {
        if (data != null)
        {
            ApiBridge.callNative('XMLHttpRequest', 'send',
            {
                "id" : this.id,
                "data" : data
            });
        }
        else
        {
            ApiBridge.callNative('XMLHttpRequest', 'send',
            {
                "id" : this.id
            });
        }
    }
    _XMLHttpRequest.prototype.overrideMimeType = function(mimetype)
    {
        ApiBridge.callNative('XMLHttpRequest', 'overrideMimeType',
        {
            "id" : this.id,
            "mimetype" : mimetype
        });
    }
    _XMLHttpRequest.prototype.abort = function()
    {
        ApiBridge.callNative('XMLHttpRequest', 'abort',
        {
            "id" : this.id
        });
    }
    _XMLHttpRequest.prototype.setRequestHeader = function(headerName,
            headerValue)
    {
        ApiBridge.callNative('XMLHttpRequest', 'setRequestHeader',
        {
            "id" : this.id,
            "headerName" : headerName,
            "headerValue" : headerValue
        });
    }
    _XMLHttpRequest.prototype.getAllResponseHeaders = function()
    {
        var strHeaders = '';
        for ( var name in this.headers)
        {
            strHeaders += (name + ": " + this.headers[name] + "\r\n");
        }
        return strHeaders;
    }
    _XMLHttpRequest.prototype.getResponseHeader = function(headerName)
    {
        var strHeaders;
        var upperCaseHeaderName = headerName.toUpperCase();
        for ( var name in this.headers)
        {
            if (upperCaseHeaderName == name.toUpperCase())
                strHeaders = this.headers[name]
        }
        return strHeaders;
    }
    _XMLHttpRequest.deleteObject = function(id)
    {
        if (_XMLHttpRequest.cache[id])
        {
            _XMLHttpRequest.cache[id] = undefined;
        }
    }

    global.ApiBridge = ApiBridge;
    global.kerkee = kerkee;
    global.XMLHttpRequest = _XMLHttpRequest;

    kerkee.register = function(_window)
    {
        _window.ApiBridge = window.ApiBridge;
        _window.kerkee = window.kerkee;
        _window.console.log = window.console.log;
        _window.XMLHttpRequest = window.XMLHttpRequest;
        _window.open = window.open;
    };


    ApiBridge.onBridgeInitComplete(function(aConfigs)
    {
        if (aConfigs)
        {
            if (aConfigs.hasOwnProperty('isOpenJSLog'))
            {
                _Configs.isOpenJSLog = aConfigs.isOpenJSLog;
            }
            if (aConfigs.hasOwnProperty('isOpenNativeXHR'))
            {
                _Configs.isOpenNativeXHR = aConfigs.isOpenNativeXHR;
            }
        }

        if (_Configs.isOpenJSLog)
        {
            global.console.log = ApiBridge.log;
        }
        ApiBridge.onNativeInitComplete(ApiBridge.onDeviceReady);

    });

})(window);

如果單純的只是 jsiOS 的交互额衙,只需要關(guān)注 下面 的代碼即可:(當(dāng)然你也可以自己設(shè)置多個(gè) kerkeeJSManager 橋梁及 更多的 交互方法)

/*****************************************
 * JS和iOS 交互接口
 *****************************************/
kerkee.jsToOc = function(s1)
   {
   ApiBridge.callNative("kerkeeJSManager", "jsToOc",
        {
            "s1" : s1
        });
   };
   
   kerkee.mutualJSOC = function(aString, callback)
   {
   ApiBridge.callNative("kerkeeJSManager", "mutualJSOC",
        {
            "aString" : aString
        }, callback);
   }
   
global.kerkeeJSManager = kerkee;

6饮焦、編譯運(yùn)行:

Xcode 編譯運(yùn)行效果

7怕吴、demo下載地址:GitHub地址

三、總結(jié):

這里只是簡單的介紹了 iOS 使用 kerkee 框架來加載 html 實(shí)現(xiàn) jsiOS 交互 县踢,如果只是單純的為了簡單的交互转绷,可以看我的另外一篇博客 :JS和OC相互調(diào)用 ,這里介紹了幾個(gè)更加輕量級的框架實(shí)現(xiàn) jsiOS 交互殿雪。但是 jsiOS 交互 功能在 kerkee 框架的一小部分暇咆,更多高性能、支持跨平臺丙曙、擴(kuò)展性好爸业、易維護(hù)等等優(yōu)秀的特性,我會慢慢閱讀源碼來說明亏镰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扯旷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子索抓,更是在濱河造成了極大的恐慌钧忽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逼肯,死亡現(xiàn)場離奇詭異耸黑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)篮幢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門大刊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人三椿,你說我怎么就攤上這事缺菌。” “怎么了搜锰?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵伴郁,是天一觀的道長。 經(jīng)常有香客問我蛋叼,道長焊傅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任鸦列,我火速辦了婚禮租冠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘薯嗤。我一直安慰自己顽爹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布骆姐。 她就那樣靜靜地躺著镜粤,像睡著了一般捏题。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肉渴,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天公荧,我揣著相機(jī)與錄音,去河邊找鬼同规。 笑死循狰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的券勺。 我是一名探鬼主播绪钥,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼关炼!你這毒婦竟也來了程腹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤儒拂,失蹤者是張志新(化名)和其女友劉穎寸潦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體社痛,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡见转,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒜哀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片池户。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凡怎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赊抖,我是刑警寧澤统倒,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站氛雪,受9級特大地震影響房匆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜报亩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一浴鸿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧弦追,春花似錦岳链、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽约急。三九已至,卻和暖如春苗分,著一層夾襖步出監(jiān)牢的瞬間厌蔽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工摔癣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奴饮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓择浊,卻偏偏與公主長得像戴卜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子近她,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

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