H5.2

_______________________________________________________________________________________________

HTML5新特性之七 ——— 拖放API

  Drag&Drop: 拖动&释放,H5提供了7个拖放事件API,分为2组,可以实现类似于桌面软件中的拖放效果,无需像以前那样使用鼠标事件来代替

img和a元素默认可拖动,其它元素加一个draggable=true属性可以使其拖动

拖放的源对象(可能发生移动)可以触发的事件 ——— 3个

 dragstart: 拖动开始

 drag:  拖动中

 dragend: 拖动结束

  整个拖动过程的组成: dragstart*1 + drag*n + dragend*1

实现用鼠标拖动元素的功能:

drag事件中获取拖动事件相对于页面的坐标(e.clientX),即时修改事件源的位置(left/top)

使事件源绝对定位(通过修改top/left修改位置),并且其最近的祖先元素要相对定位

elem.ondragstart=function(e){

  startx=e.offsetX;        //拖动事件相对于飞机左上角偏移量,elem:飞机元素

  starty= e.offsetY;

};

elem.ondrag=function(e){

  var ex=e.clientX;        //获得拖动事件相对于页面(body)的坐标(文档显示区左上角)

  var ey=e.clientY;

  if(ex<=0&&ey<=0){ return; }        //防止拖动事件最后触发(0,0)坐标

  p3.style.left=(ex-startx)+"px";    //拖动后元素相对于距其最近的已定位祖先元素的偏移量 =

                                       拖动后鼠标的client - 拖动前鼠标的offset

  p3.style.top=(ey-starty)+"px";

};

拖放的目标对象(不会发生移动)可以触发的事件 ——— 4个(源对象被拖动时,目标元素可触发的事件)

 dragenter:源对象被拖动着进入目标对象

 dragover:  源对象被拖动着悬停在目标对象上方

 dragleave:源对象被拖动着离开了目标对象

 drop:  源对象在目标对象上方释放

  整个拖动过程的组成1:  dragenter*1 + dragover*n + dragleave*1

  整个拖动过程的组成2:  dragenter*1 + dragover*n + drop*1

注意: dragover事件的默认行为是必须触发dragleave,若不阻止此默认行为,drop无法触发

 在dragover事件中,阻止默认行为: e.preventDefault();

H5拖放API: 如何在拖动源对象和目标对象间传递数据

dataTransfer:  用于数据传递的"拖拉机"对象

e.dataTransfer.setData(k,v):  在拖动源对象事件中使用e.dataTransfer属性保存数据

var value = e.dataTransfer.getData(k):  在拖动目标对象事件中使用e.dataTransfer属性读取数据

第六章*****************************************************************************************

H5新特性之八 ——— Web Worker: 执行耗时JS任务

操作系统基础概念

程序: Program,可供CPU执行的代码,存储在外存中,如.exe

进程: Process/Task,把程序从外存调入内存,分配执行空间(必需数据段和可执行代码段)

      随时供CPU调用执行,进程是程序的可执行状态

线程: Thread,线程是进程内部执行代码的基本单位

问题:进程和线程间的关系

① 进程是操作系统分配内存的基本单位

② 线程是CPU执行代码的基本单位

③ 线程必须处于某个进程内部

④ 一个进程内至少一个,也可以同时存在多个线程

⑤ 每个线程也需要自己独立的内存空间,至少2MB

⑥ 一个操作系统中存在多个线程,在宏观上看是"同时执行",微观上看是"依次执行" ——— 并发执行

    宏观和微观都是"同时执行" ——— 并行

问题: Chrome浏览器中的线程模型

一个Chrome进程内,至少有6个并发线程,"并发"向web服务器发起http请求,获取资源 ——— 资源请求线程

还有1个线程负责将所有内容绘制到浏览器页面中 ——— UI渲染主线程: 不允许多线程同时绘图,防止内容布局冲突

_______________________________________________________________________________________________

若页面中需要执行一段很"耗时"的JS任务,出现的现象:

  <button>按钮1</button>

  <script src="x.js"></script>

  < button>按钮2</button>

x.js执行过程中,按钮1可见但可能没有事件处理,按钮2不可见

产生原因: 浏览器中执行代码只使用1个线程:  UI主线程

解决办法: 创建一个新线程,让它去执行耗时的JS任务,UI主线程继续执行页面渲染和事件监听

          浏览器作为宿主环境提供了JS一个多线程运行的环境

H5中创建新的执行线程的方法:

<button>按钮1</button>

  <script> var elem = new Worker('x.js') </script>    //在网络环境下(apache/nodejs)才生效

<button>按钮2</button>

Worker线程的缺陷: 浏览器不允许Worker线程操作任何的DOM、BOM对象

原因: 浏览器只允许UI主线程操作DOM、BOM对象,若多个线程同时都可以操作DOM、BOM,页面可能混乱

解决办法: 类似jQuery操作DOM、BOM的脚本不使用Worker来加载执行

UI主线程给Worker线程发数据消息:

UI主线程:  var w2 = new Worker('x.js', options);  //构造函数

          w2.postMessage(发送的数据);

Worker线程: onmessage= function(e){ e.data }    //e.data: 接收的数据

Worker()构造函数,第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载JS脚本,否则报错。第二个参数是配置对象,可选。作用是指定 Worker 的名称,用来区分多个 Worker 线程(如{ name:'w2' })

Worker线程给UI主线程发数据消息:

UI主线程:  var w1 = new Worker('x.js');

          w1.onmessage= function(e){ e.data }    //e.data: 接收的数据

Worker线程:  postMessage(发送的数据);

主线程中的API: worker是 Worker 的实例

worker.postMessage(aMessage, transferList): 主线程往worker线程发消息,消息可以是任意类型数据,包括二进制数据,接受两个参数,aMessage 是可以传递任何类型数据的,包括对象。这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程

worker.terminate: 主线程关闭worker线程

worker.onmessage: 指定worker线程发消息时的回调,也可通过worker.addEventListener('message', cb)

worker.onerror: 指定worker线程发生错误时的回调,也可通过worker.addEventListener('error', cb)

Worker线程中全局对象为self,代表子线程自身,this指向self,其上有一些API

self.postMessage: worker线程往主线程发消息,消息可以是任意类型数据,包括二进制数据

self.close: worker线程关闭自己

self.onmessage: 指定主线程发worker线程消息时的回调,也可以self.addEventListener('message', cb)

self.onerror: 指定worker线程发生错误时的回调,也可以self.addEventListener('error', cb)

worker的使用注意点

① 同源限制: worker线程执行的脚本文件必须和主线程的脚本文件同源

② 文件限制: 为了安全,worker线程无法读取本地文件,它所加载的脚本必须来自网络,且需要与主线程的脚本同源

③ DOM操作限制: worker线程在与主线程的window不同的另一个全局上下文中运行,其中无法读取主线程所在网页的DOM对象,也不能获取 document、window等对象,但是可以获取navigator、location(只读)、XMLHttpRequest、setTimeout族等浏览器API

④ 通信限制: worker线程与主线程不在同一个上下文,不能直接通信,需要通过postMessage方法来通信

⑤ 脚本限制: worker线程不能执行alert、confirm,但可以使用XMLHttpRequest对象发出ajax请求

⑥ 虽然使用worker线程不会占用主线程,但是启动worker会比较耗费资源

⑦ 主线程中使用XMLHttpRequest在请求过程中浏览器另开了一个异步http请求线程,但是交互过程中还是要消耗主线程资源

总结: 项目中使用Worker的场景

当在UI主线程中执行会导致页面内容"卡死"时,可以使用Worker线程与UI主线程并发执行

比如: 复杂计算(如深度的递归、循环嵌套)、加密/解密、大数据统计、路径规则、预加载

_______________________________________________________________________________________________

H5新特性之九 ——— Web Storage—————key是关键字,不能作为属性名

作用: 在浏览器中存储当前用户专有的数据,比如: 访问历史、内容定制、样式定制

注意: 存储Number类型时,取出来会变成String类型

Web项目中项目数据的存储位置:

服务器端存储: 业务数据(如商品、用户、订单、帖子、评论、储户等)

客户端存储:   客户端专有数据(历史记录、内容定制、样式偏好等)

① Cookie技术:  浏览器兼容性好,大小不能超过4KB,操作复杂,使用较普遍

② Flash存储:   依赖于Flash播放器,不推荐使用

③ IndexedDB技术: 可以存取大量数据,还不是技术标准

④ H5 Web Storage技术:  大小不能超过8MB,操作简单,H5新特性

Session:会话,从浏览器打开某个网站的某个页面开始,中间可能打开多个页面,不断的发出请求、接收响应,直至关闭浏览器 ——— 整个过程称为浏览器和Web服务器之间的一次"会话"

H5的Web Storage技术中,浏览器为用户提供了2个对象:

①window.sessionStorage: 存储在浏览器内存中的类数组对象,会话级数据存储,会话结束,数据删除

作用:  在同一个会话中所有页面共享数据,如登录用户名

                                              //访问对象的属性: 对象.属性名 或对象["属性名"]

sessionStorage['key']= value        //保存一个数据

var v = sessionStorage['key']           //读取一个数据

sessionStorage.setItem('key',value)  //保存一个数据

var v = sessionStorage.getItem('key')   //读取一个数据

sessionStorage.removeItem('key')     //删除一个数据

sessionStorage.clear()                  //清除所有的数据

var count = sessionStorage.length       //查看保存的数据个数

var key =sessionStorage.key(i)     //获取第i个key,第i个属性名

②window.localStorage:跨会话级数据存储

本质就是一个存储在浏览器硬盘文件中的类数组对象,专用于永久存储当前网站在当前浏览器中的专用数据,即使关闭浏览器/重启电脑数据也不会删除

作用: 在当前客户端所对应的所有会话中共享数据

localStorage['key'] = value         //保存一个数据

var v = localStorage['key']          //读取一个数据

localStorage.setItem('key',value)   //保存一个数据

var v = localStorage.getItem('key')  //读取一个数据

localStorage.removeItem('key')      //删除一个数据

localStorage.clear()                 //清除所有的数据

var count = localStorage.length      //查看保存的数据个数

var key =localStorage.key(i)       //获取第i个key,即第i个属性名

localStorage['key']=JSON.stringify(obj);  //把对象/数组转成json字符串,存进localStorage

var obj=JSON.parse(localStorage.getItem('key'));  //将存进去的json字符串转成对象/数组

                                           //此时,对象或数组中的Number不会变为String

if(localStorage['key']){    //在iPhone/iPad上有时设置setItem()时会出现诡异的QUOTA_EXCEEDED_ERR错误,这时在setItem之前,先removeItem()就好了

  localStorage.removeItem('key');

}

localStorage.setItem('key',value);

window.onstorage事件:  当 localStorage 中数据发生改变时会自动触发

注意: 该事件只能用于监听 localStorage 中数据的改变,而不能监听sessionStorage中数据的改变

for(var i=0;i<localStorage.length;i++){    //遍历客户端 内存/硬盘 中存储的数据

  var key = localStorage.key(i);           //获取第i个key

  var value = localStorage[key];           //获取第i个value

}

第七章*****************************************************************************************

H5新特性之十 ——— Web Socket

Socket: 插座、套接字,源自于90+年代C语言,所有的网络通讯底层都是基于套接字编程,套接字用于"接收数据&发送数据"

HTTP协议特点: 属于"请求 — 响应"模型的协议,必须客户端先发请求,服务器才会给出响应。一个请求,只能得到一个响应。在有些应用场景,此模型有缺陷: 实时走势、聊天室 ——— 即使客户端不发送请求,服务器只要数据有更新也应该立即给客户端

解决办法:长轮询(Long Polling)/心跳请求: 定时器 + XHR

该办法并不完美,请求过于频繁服务器压力太大;不够频繁,客户端数据延迟较大,时效性差

Web Socket协议: 属于于"广播 — 收听"模型的协议,只要客户端连接到服务器就不再断开(永久连接),双方建立"全双工通信通道"(双方向),一方可以不停的给对方发消息,同时对方也可以发送/不发送消息,可以解决"实时走势、聊天室"应用中的问题

Web Socket协议的应用分为客户端和服务器端程序

基于WS协议的服务器端程序: Java、PHP、Node.js都可以编写

  监听指定端口,接收客户请求,向对方发消息,并接受消息

基于WS协议的客户端程序:   Java、PHP、HTML5都可以编写

  主动发起连接请求,保持永久连接,向对方发消息,并接收消息

使用Node.js编写一个WS协议的服务器端应用

① 下载第三方ws协议模块:  npm  i  ws

② 仿照ws模块自带的说明文档 README,编写WS服务器端程序

创建WebSocket服务器程序

const ws = require("ws");                  //加载相应模块 ws

var server = new ws.Server( {port:9001} );//创建WebSocket服务器,指定端口

server.on("connection",(socket)=>{        //接收客户端连接请求

  console.log("WS服务器接收到一个连接");

  var counter = 0;

  var timer = setInterval(()=>{

    counter++;

   socket.send("I am Server - "+counter);    //每隔一定时间,ws服务器向客户端发送一次消息

  },1000);

  socket.on("message",(msg)=>{//WS服务器接收客户端发来的消息

    console.log("服务器接收消息"+msg);

  });

  socket.on("close",()=>{//WS服务器接收到客户端发来的断开连接请求

    console.log("客户端断开连接");

    clearInterval(timer);                       //不再继续发送消息

  });

});

使用HTML5编写一个WS协议的客户端应用

var socket = null;         //创建变量保存socket对象

socket =new WebSocket('ws://127.0.0.1:9001');    //连接WS服务器

socket.onmessage= function(e){ e.data }; //.onmessage:从服务器接收消息,e.data: 接收的数据

socket.send( msg );        //向服务器发送消息

socket.close();            //断开与服务器的连接

Web SQL***************************************************************************************

Web Storage、Web SQL不能跨域名存储数据,不同域名下存储的数据互不影响

Web Storage对于存储较少量的数据有用,但无法存储更大量的结构化数据,可以使用Web SQL或IndexedDB

Web SQL Database API 未包含在 HTML5 规范之中,它是一个独立的规范,它引入了一套使用 SQL 操作客户端数据库的API,这些 API 有同步的,也有异步的,同步版本的 API 只在工作线程(Worker Threads)上有用,由于并不是所有的浏览器都支持工作线程,一般情况下,都会使用异步 API

它的核心方法有三个: openDatabase、transaction 和 executeSql,这些 API 已经被广泛的实现在了不同的浏览器里,尤其是手机端浏览器。W3C 官方在2011年11月声明已经不再维护Web SQL Database 规范

浏览器对Web SQL Database的支持情况

IEFirefoxChromeSafariOperaIOS SafariAndroid

6.0(×)7.0(×)13.0(√)3.2(√)11.0(√)3.2(√)2.1(√)

10.0(×)11.0(×)

12.0(×)

规范中定义的三个核心方法:

openDatabase: 使用现有的数据库或新建的数据库创建一个数据库对象

              需要连接数据库后才能在 F12→Application→Web SQL中看到数据库

transaction:  控制一个事务,以及基于这种情况执行提交或回滚

executeSql:   用于执行实际的SQL查询

var db = openDatabase('mydb'数据库名称, '1.0'版本号, 'Test DB'描述文本, 2*1024*1024数据库大小, [创建回调]);                   //第五个参数'创建回调'会在创建数据库后被调用

db.transaction(function(tx){        //插入数据

  tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');    //创建1个名为LOGS的表

  tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "菜鸟教程")');   //插入数据

  tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "www.runoob.com")');

  tx.executeSql('INSERT INTO LOGS (id, log) VALUES (?, ?)', [e_id, e_log]);    //动态插入数据

                           //e_id、e_log是外部变量,executeSql会映射数组参数中的每个条目给'?'

});

db.transaction(function(tx){        //查询数据

  tx.executeSql('SELECT * FROM LOGS', [], function(tx,results){

    console.log(results);           //result是查询到的表的数据

  }, null);

});

db.transaction(function(tx){        //删除记录

  tx.executeSql('DELETE FROM LOGS WHERE id=1');

  tx.executeSql('DELETE FROM LOGS WHERE id=?', [id]);    //动态删除指定的数据

});

db.transaction(function(tx){        //更新记录

  tx.executeSql('UPDATE LOGS SET log=\'www.w3cschool.cc\' WHERE id=2');

  tx.executeSql('UPDATE LOGS SET log=\'www.w3cschool.cc\' WHERE id=?', [id]);    //删除动态的id

});

IndexedDB************************************************************************************

单页应用基本需要 web service 装载数据,数据通过控制器注入DOM中,大多数前端框架都这么做

可以缓存静态网页和资源文件,尤其是浏览的数据,在没有网络的时候,可以从当地数据库缓存中进行读取

IndexedDB 是被废弃的 Web SQL 数据库的代替品。一个键值对的 NoSQL 且支持超大储存(上浮20-50%硬盘空间)的数据库。支持数据类型 number、string、JSON、blob 等

特点:

① 键值对存储,主键独一无二,不能重复

② 异步,不会锁死浏览器,而localStorage是同步的,会拖慢网页速度

③ 支持事务,只要有一步失败,整个事务就会取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据库的情况(数据记录的读写和删除,都需要通过事务完成,事务对象提供error、abort、complete三个事件,用来监听操作结果)

④ 同源限制,受同源限制,每个数据库对应创建它的域名,网页只能访问自身域名下的数据库,不能访问跨域的数据库

⑤ 存储空间大,一般不少于250MB

⑥ 支持二进制存储,不仅可以存储字符串,还可以存储二进制数据

Indexed Database也可简称为 IndexedDB(以前被称作 WebSimpleDB),是一个Web客户端存储结构化数据的规范,在 2009 年由 Oracle 提出。如果说 Web SQL Databae 在客户端实现了传统的SQL数据库操作,那么 Indexed Database 更类似于 NoSQL 的形式来操作数据库,其中最重要的是 Indexed Database 不使用 SQL 作为查询语言。其数据存储可以不需要固定的表格模式,也经常会避免使用SQL的 JOIN 操作,并且一般具有水平可扩展性。目前 W3C 官方也把焦点投到对 Indexed Database 规范的制定当中来,而Microsoft和 Mozilla是对这个规范重要的两个推动者,Firefox 4 以上已经部分实现了 Indexed DB API,并且 IE 10 中也将实现 Indexed DB API。由于在手机等移动设备的浏览器中都没有实现 Indexed DB API,所以其还有一定的局限性

IEFirefoxChromeSafariOperaIOS SafariAndroid

6.0(×)7.0(√)13.0(√)3.2(×)11.0(×)3.2(×)2.1(×)

9.0(×)5.1(×)11.6(×)5.0(×)4.0(×)

10.0(√)16.0(√)10(√)15(√)10(√)

var request = window.indexedDB.open(databaseName数据库名, version版本);    //打开数据库

返回IDBRequest对象,通过3种事件(error、success、upgradeneeded)处理打开数据库的操作结果:

数据库打开报错:

request.onerror = function(err){ console.log(err); };

数据库打开成功

request.onsuccess = function(event){ console.log(request.result); };

如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded

request.onupgradeneeded = function(event){ console.log(event.target.result); }

var db = null;

新建对象仓库(新建表)

request.onupgradeneeded = function(event){

  db = event.target.result;

  var objectStore = null;

  if(!db.objectStoreNames.contains('person')){

    objectStore = db.createObjectStore('person', { keyPath: 'id' });

  }

}

新建索引

request.onupgradeneeded = function(event){

  db = event.target.result;

  var objectStore = db.createObjectStore('person', { keyPath: 'id' });

  objectStore.createIndex('name索引名', 'name索引所在属性', { unique: false }配置对象(该属性是否包含重复的值));

  objectStore.createIndex('email', 'email', { unique: true });

}

新增数据: 向对象仓库写入数据记录,需要通过事务完成

function add(){

  var request = db.transaction(['person'], 'readwrite')

  .objectStore('person')

  .add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });

  request.onsuccess = function (event){ console.log('数据写入成功'); };

  request.onerror = function (event){ console.log('数据写入失败'); }

}

读取数据: 通过事务完成

function read(){

  var transaction = db.transaction(['person']);

  var objectStore = transaction.objectStore('person');

  var request = objectStore.get(1);

  request.onerror = function(event){ console.log('事务失败'); };

  request.onsuccess = function(event){

    if(request.result){

      console.log('Name: ' + request.result.name);

      console.log('Age: ' + request.result.age);

      console.log('Email: ' + request.result.email);

    }else{

      console.log('未获得数据记录');

    }

  };

}

遍历数据表格的所有记录: 使用指针对象 IDBCursor

function readAll(){

  var objectStore = db.transaction('person').objectStore('person');

  objectStore.openCursor().onsuccess = function(event){

    var cursor = event.target.result;

    if(cursor){

      console.log('Id: ' + cursor.key);

      console.log('Name: ' + cursor.value.name);

      console.log('Age: ' + cursor.value.age);

      console.log('Email: ' + cursor.value.email);

      cursor.continue();

    }else{

      console.log('没有更多数据了!');

    }

  };

}

更新数据: 使用 IDBObject.put() 方法

function update(){

  var request = db.transaction(['person'], 'readwrite')

  .objectStore('person').put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });

 request.onsuccess = function(event){ console.log('数据更新成功'); };

  request.onerror = function(event){ console.log('数据更新失败'); }

}

删除记录: IDBObjectStore.delete()方法

function remove(){

  var request = db.transaction(['person'], 'readwrite')

  .objectStore('person').delete(1);

  request.onsuccess = function(event){ console.log('数据删除成功'); };

}

新建表格的时候,对name字段建立了索引,就可以从name找到对应的数据记录

var transaction = db.transaction(['person'], 'readonly');

var store = transaction.objectStore('person');

var index = store.index('name');

var request = index.get('李四');

request.onsuccess = function(e){

  var result = e.target.result;

  if(result){

    // ...

  }else{

    // ...

  }

}

文件的读取和下载*******************************************************************************

文件的读取:

在html5中,DOM给文件中添加了一个files集合,在选取文件中,files中包含一个File对象,每个对象都有下列4个属性:

① name: 本地文件的文件名

② size: 文件的大小

③ type: 字符串,文件的MIME类型

④ lastModifiedDate: 字符串,文件上一次被修改的时间

同时,使用FileReader对象,web应用程序可以异步的读取存储在用户计算机上的文件(或者原始数据缓冲)内容,可以使用File对象或者Blob对象来指定所要处理的文件或数据

FileReader提供了以下4个方法:

① readAsText(file, encoding): 以纯文本形式读取文件,将读取到的文本保存在 result 属性中。第二个参数用于指定编码类型,是可选的

② readAsDataURL(file): 读取文件并将文件以数据 URI 的形式保存在 result 属性中

③ readAsBinaryString(file): 读取文件并将一个字符串保存在 result 属性中,字符串中的每个字符表示一字节

④ readAsArrayBuffer(file): 读取文件并将一个包含文件内容的 ArrayBuffer 保存在 result 属性中

文件的下载:

<a href="./file/log.text" download="log.text">下载文件</a>

属性:

① download: 用于指定下载的文件名

② href:     要下载的文件的链接(需为本地链接)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容