初识小程序-视图层

视图层

框架的视图层由WXML(WeiXin Markup language)与WXSS(WeiXin Style Sheet)编写,由组件来进行展示。对于微信小程序而言,视图层就是所有.wxml文件与.wxss文件的集合。
微信小程序在逻辑层将数据进行处理后发送给视图层展现出来,同时接受视图层的事件反馈。

■.wxml文件用于描述页面的结构。
■.wxss文件用于描述页面的样式。
视图层以给定的样式展现数据并将事件反馈给逻辑层,而数据展现是以组件来进行的。组件(Component)是视图的基本组成单元,是构建.wxml文件必不可少的。
对于小程序的WXML编码开发,基本上可认为就是使用组件、结合事件系统,构建页面结构的过程。.wxml文件中所绑定的数据,均来自于对应页面的.js文件中Page方法的data对象。

WXML

WXML是框架设计的一套类似HTML的标签语言,结合基础组件、事件系统,可以构建出页面的结构,即.wxml文件。

.wxml文件第一行建议写。比如logs页面的wxml文件(logs. wxml)如下:

<!--logs.wxml-->
<view class="container log-list">
    <block wx:for="{{logs}}" wx:for-item="log">
        <text class="log-item">{{index+1}}.{{log}}</text>
    </block>
</view>

上述代码,我们通过组件控制页面内容展现,通过组件与组件实现页面数据的绑定;

WXML具有数据绑定、列表渲染、条件渲染、模板及事件绑定的能力:

1)数据绑定:
//wxml
<view>{{message}}</view>
//page.js
Page({
    data:{
        message:'hello MINA'
  }
})
上例中,在.wxml文件中绑定message变量,在.js文件的data对象中给message赋值“hello MINA ”

2)列表渲染:
//wxml
<view wx:for="{{array}}">{{item}}</view>
//page.js
Page({
    data:{
        array:[1,2,3,4,5]
  }
})

3)条件渲染:
//wxml
<view wx:if="{{view=='WEBVIEW'}}">WEBVIEW</view>
<view wx:elif="{{view=='APP'}}">APP</view>
<view wx:else="{{view=='MINA'}}">MINA</view>
//page.js
Page({
    data:{
        view:'MINA'
  }
})

4)模板:
//wxml
<template name="staffName">
  <view>FirstName:{{firstName}},LastName:{{lastName}}</view>
</template>

<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>

//Pages.js
Page({
    data:{
        staffA:{firstName:'hh',lastName:'hhh'},
        staffB:{firstName:'bb',lastName:'bbb'},
        staffC:{firstName:'cc',lastName:'ccc'}
  }
})
注:字例代码中,“...”为扩展运算符,用它来展开一个对象,如staffA对象。

5)事件绑定:
//wxml
<view bindtap="add">{{count}}</view>
//page.js
Page({
    data:{
        count:1
  },
    add:function(e){
      this.setData({
          count:this.data.count+1
      })  
  }
})

1.数据绑定

.wxml文件中的动态数据均来自对应页面的.js文件中Page的data对象。

(1)简单绑定

数据绑定使用Mustache语法(即“双大括号”语法)将变量包起来,可以作用于:

1).内容:
<!--wxml-->
<view>{{content}}</view>
//page.js
Page({
    data:{
        content:'hello world'
    }
})

2).组件属性(需要在双引号之内):
<!--wxml-->
<view id="item-{{id}}">{{content}}</view>
//page.js
Page({
    data:{
        content:'hello world',
        id:0
    }
})

3).控制属性(需要在双引号之内):
<!--wxml-->
<view wx:if="{{condition}}">{{content}}</view>
//page.js
Page({
    data:{
        content:'hello world',
        condition:true
    }
})
(2)运算

可以在{{}}内进行简单的运算,支持如下几种方式;

1).三元运算:
<!--wxml-->
<view hidden="{{flag?true:false}}">Hidden</view>

2).算数运算:
<!--wxml-->
<view>{{a+b}}+{{c}}+d</view>
//page.js
Page({
    data:{
        a:1,
        b:2,
        c:3
    }
})
view中的内容为3+3+d

3).逻辑判断:
<view wx:if="{{length>5}}"></view>

4).字符串运算:
<!--wxml-->
<view>{{"hello"+name}}</view>
//page.js
Page({
    data:{
        name:'MINA'
    }
})

5).数据路径运算:
<view>{{object.key}}{{array[0]}}</view>
Page({
    data:{
      object:{
          key:'hello'
      },
      array:['world']
    }
})
(3)组合

也可以在Mustache内直接进行组合,构成新的对象或者数组。

1).数组
<!--wxml-->
<view wx:for="{{[zero,1,2,3,4]}}">{{item}}</view>
//page.js
Page({
    data:{
        zero:0
    }
})
最终组合成数组[0, 1, 2, 3, 4]。

2).对象
<!--wxml-->
<template is="objectCombine" data="{{for:a, bar:b}}"></template>
//page.js
Page({
    data:{
        a:1,
        b:2
    }
})
最终组合成的对象是{for: 1, bar: 2}

3).也可以用扩展运算符. ..来将一个对象展开:
<!--wxml-->
<template is="objectCombine" data="{{...obj1,...obj2,e:5}}"></template>
//page.js
Page({
    data:{
        obj1:{
            a:1,
            b:2
        },
       obj2:{
            c:3,
            d:4
        }
    }
})
最终组合成的对象是{a: 1, b: 2, c: 3, d: 4, e: 5}。

4).如果对象的key和value相同,也可以间接地表达:
<!--wxml-->
<template is="objectCombine" data="{{foo,bar}}"></template>
//page.js
Page({
    data:{
      foo:'my-foo',
      bar:'my-bar'
    }
})
最终组合成的对象是{foo: 'my-foo', bar:'my-bar'}。

5).注意:
上述方式可以随意组合,但是如果变量名相同,后边的对象会覆盖前面的对象。
<!--wxml-->
<template is="objectCombine" data="{{...obj1,...obj2,a,c:6}}"></template>
//page.js
Page({
    data:{
      obj1:{
        a:1,
        b:2
      },
      obj2:{
        b:3,
        c:4
      },
      a:5
    }
})
最终组合成的对象是{a: 5, b: 3, c: 6}。

2.条件语句

条件语句可用于.wxml中进行条件渲染,不同的条件进行不同的渲染。

(1)wx:if
我们用wx:if="{{condition}}"来判断是否需要渲染该代码块。比如:
<view wx:if="{{condition}}">True</view>

也可以用wx:elif和wx:else来添加一个else块:
<view wx:if="{{length>5}}">1</view>
<view wx:elif="{{length>2}}">2</view>
<view wx:else>3</view>
(2)block wx:if

因为wx:if是一个控制属性,需要将它添加到一个组件标签上。如果想一次性判断多个组件标签,其实可以使用一个标签将多个组件包装起来,并在其上使用wx:if控制属性:

<block wx:if="{{true}}">
    <view>view1<view>
    <view>view2</view>
<block>

注意
并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。

(3)wx:if vs hidden

因为wx:if之中的模板也可能包含数据绑定,所以当wx:if的条件值切换时,框架有一个局部渲染的过程,从而确保条件块在切换时销毁或重新渲染。
同时wx:if也是惰性的,如果在初始渲染条件为false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden就简单得多,组件始终会被渲染,只需简单地控制显示与隐藏。
一般来说,wx:if有更高的切换消耗,而hidden有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用hidden更好;如果运行时条件不大可能改变,则wx:if较好。

3.列表语句

列表语句可用于.wxml中进行列表渲染,将列表中的各项数据进行重复渲染。

(1)wx:for

在组件上使用wx:for控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组当前项的下标变量名默认为index,数组当前项的变量名默认为item。
示例如下:

<view wx:for="{{items}}">{{index}}:{{item.message}}</view>
//page.js
Page({
    data:{
      items:[{message:'foo'},{message:'go'}]
    }
})

使用wx:for-item可以指定数组当前元素的变量名。
而使用wx:for-index则可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">{{idx}}:{{itemName.message}}</view>

wx:for也可以嵌套,例如下边是一个九九乘法表:
<view wx:for="{{[1,2,3,4,5,6,7,8,9]}}" wx:for-item="I">
<view wx:for="{{[1,2,3,4,5,6,7,8,9]}}" wx:for-item="j">
<view wx:if="{{i<j}}">{{I}}*{{j}}={{i*j}}</view>
</view>
</view>
(2)block wx:for

类似于block wx:if,也可以将wx:for用在标签上,渲染一个包含多节点的结构块。
例如:

<block wx:for="{{[1,2,3]}}">
    <view>{{index}}:</view>
    <view>{{item}}</view>
</block>
(3)wx:key

如果列表中项目的位置会动态改变,或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如input标签中的输入内容,switch中的选中状态),需要使用wx:key来指定列表中项目的唯一标识符。
wx:key的值以两种形式提供:
■字符串,代表在for循环的array中item的某个property,该property的值需要是列表中唯一的字符串或数字,且不能动态改变。
■保留关键字*this代表在for循环中的item本身,这种表示需要item本身是一个唯一的字符串或者数字,例如:当数据改变触发渲染层重新渲染的时候,会校正带有key的组件,框架会确保它们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
注意:
如不提供wx:key,会报错,如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
示例代码如下:

//wx-key-demo.wxml
<switch wx:for ="{{objectArray}}" wx:key="unique" style="display:block;">{{item:id}}</switch>
<button bindtap="switch">Switch</button>
<button bindtap="addToFront">Add to the front</button>
<switch wx:for="{{numberArray}}" wx:key="*this" style="display:block;">{{item}}</switch>
<button bindtap="addNumberToFront">Add to the front</button>

//wx-key-demo.js
Page({
    data:{
      objectArray:[{id:5,unique:'unique_5'},{id:4,unique:'unique_4'},
      {id:3,unique:'unique_3'},{id:2,unique:'unique_2'},],
      numberArray:[1,2,3,4]
  },
  switch:function(e){
      const length = this.data.objectArray.length
      for(let I=0;i<length;++i){
            const x=Math.floor(Math.random()*length)
            const y=Math.floor(Math.random()*length)
            const temp = this.data.objectArray[x]  
            this.data.objectArray[x]=this.data.objectArray[y]
            this.data.objectArray[y] = temp
      }
    this.setData({
          objectArray:this.data.objectArray
    })
  },
  addToFront: function(e) { 
      const length = this.data.objectArray.length       
      this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat (this.data.objectArray)
       this.setData({ 
              objectArray: this.data.objectArray 
        })
   },
  addNumberToFront: function(e){ 
        this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this. data.numberArray)
        this.setData({
           numberArray: this.data.numberArray 
        }) 
    }
})

4.模板

WXML支持模板(template),可以在模板中定义代码片段,然后在不同的地方调用。

(1)定义模板

使用name属性,作为模板的名字。然后在<template/>内定义代码片段:

<template name="msgItem">
  <view>
    <text>{{index}}:{{msg}}</text>
    <text>time:{{time}}</text>
  </view>
</template>
(2)使用模板

使用is属性,声明需要使用的模板,然后将模板所需要的data传入,例如:

<!--wxml-->
<template is="msgItem" data="{{...item}}"/>
//page.js
Page({
    data:{
    item:{
      index:0,
      msg:'this is a template',
      time:'2018-12-12'
    }
  }
})

is属性可以使用Mustache语法,来动态决定具体需要渲染哪个模板:
<template name="odd"> 
 <view> odd </view>   
</template>    
<template name="even">      
  <view> even </view>    
</template>    

<block wx:for="{{[1, 2, 3, 4, 5]}}">        
    <template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/>   
 </block>
(3)模板的作用域

模板拥有自己的作用域,只能使用data传入的数据。

5.引用

WXML提供两种文件引用方式:import和include。

(1)import

import可以在该文件中使用目标文件定义的template,例如:在item.wxml中定义了一个叫item的template:

 <! -- item.wxml -->   
 <template name="item">      
    <text>{{text}}</text>   
 </template>

在index.wxml中引用了item.wxml,就可以使用item模板:

<import src="item.wxml"/>    
<template is="item" data="{{text: 'forbar'}}"/>
(2)import的作用域

import有作用域的概念,即只会引用目标文件中定义的template,而不会引用目标文件嵌套import的template。
例如:C import B, B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template。如下所示:

 <! -- A.wxml -->    
<template name="A">      
<text> A template </text>    
</template>    
<! -- B.wxml -->    
<import src="a.wxml"/>    
<template name="B">      
<text> B template </text>   
</template>    
<! -- C.wxml -->    
<import src="b.wxml"/>    
<template is="A"/>   
<! -- Error! Can not use tempalte when not import A. -->    <template is="B"/>
(3)include

include可将目标文件除模板代码(<template/>)块的所有代码引入,相当于拷贝到include位置,例如:

 <! -- index.wxml -->    
<include src="header.wxml"/>    
<view> body </view>    
<include src="footer.wxml"/>    
<! -- header.wxml -->    
<view> header </view>    
<! -- footer.wxml -->    
<view> footer </view>

6.事件绑定

事件的定义如下:
■事件是视图层到逻辑层的通信方式。
■事件可以将用户的行为反馈到逻辑层进行处理。
■事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
■事件对象可以携带额外信息,如id、dataset、touches。

(1)事件的使用

小程序与用户的交互多数情况下是通过事件来进行的。
首先,在组件中绑定一个事件处理函数。如下面代码中,我们使用bindtap,当用户点击该组件view的时候会在该页面对应的Page中找到相应的事件处理函数tapName:

// 指定view组件的唯一标识tapTest;自定义属性hi,其值为MINA;绑定事件tapName    
<view id="tapTest" data-hi="MINA" bindtap="tapName"> Click me! </view>

注意
应将bindtap理解为:bind+tap,即绑定冒泡事件tap(手指触摸后离开)。
其次,要在相应的Page定义中写上相应的事件处理函数,参数是event。如下列示例代码中,定义了tapName函数,将事件信息输出到控制台上:

Page({      
  tapName: function(event) {      
      console.log(event)     
   }   
 })
(2)事件详解

微信小程序里的事件分为冒泡事件和非冒泡事件:
■冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
■非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。WXML中的冒泡事件仅有6个,列表如下:

冒泡事件 含义或触发条件
touchstart 手指触摸
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒、弹窗
touchend 手指触摸动作结束
tap 手指触摸后离开
longtap 手指触摸后超过350ms再离开

注意
除上表之外的其他组件自定义事件都是非冒泡事件,如<form/>的submit事件、<input/>的input事件、<scroll-view/>的scroll事件。

事件绑定的写法同组件的属性,以key、value的形式,如下所示:
■key以bind或catch开头,然后跟上事件的类型,如bindtap、catchtouchstart。
■value是一个字符串,需要在对应的Page中定义同名的函数。不然当触发事件的时候会报错。
注意
bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡。

例子,点击id为inner的组件view会先后触发handleTap3和handle-Tap2(因为tap事件会冒泡到id为middle的组件view,而middle view阻止了tap事件冒泡,不再向父节点传递),点击middle view会触发handleTap2,点击id为outter的组件view会触发handleTap1:

<view id="outter" bindtap="handleTap1"> outer view      
  <view id="middle" catchtap="handleTap2"> middle view  
          <view id="inner" bindtap="handleTap3"> inner view        </view>      </view>    </view>

如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。事件对象具有的属性参见表

事件对象的属性 类型 说明
type String 事件类型
timeStamp Integer 事件生成的时间戳
target Object 触发事件组件的一些属性值集合
currentTarget Object 当前组件的一些属性值集合
touches Array 触摸事件,当前停留在屏幕中触摸点信息的数组
changedTouches Array 触摸事件,当前变化的触摸点信息的数组
detail Object 额外的信息

其中:
■type指通用事件类型。
■timeStamp是该页面打开到触发事件所经过的毫秒数。
■target触发事件的源组件,是一个对象,具有以下3个属性

源组件对象属性 说明
id 事件组件的id
tagName 事件组件的类型
dataset 事件组件上由data-开头的自定义属性组成的集合

■currentTarget事件绑定的当前组件。与target类似,是一个对象,同样具有上表三个属性。(组件<canvas />中的触摸事件为特殊事件,不可冒泡,所以无currentTarget。)
说明:
1)target和currentTarget可以参考上例中,点击inner view时,handleTap3收到的事件对象target和currentTarget都是inner,而handleTap2收到的事件对象target就是inner, currentTarget就是middle。
2)dataset在组件中可以定义数据,这些数据将会通过事件传递给App Service。dataset书写方式以data-开头,多个单词由连字符“-”连接,不能有大写(大写会自动转成小写),如data-element-type,最终在event.target.dataset中会将连字符转成驼峰形式:elementType。
示例代码如下:

// bindviewtap.wxml    
<view  data-alpha-beta="1"  data-alphaBeta="2"  bindtap="bindViewTap">    DataSet Test </view>    
// bindviewtap.js    
Page({      
    bindViewTap:function(event){   
       event.target.dataset.alphaBeta == 1      //- 会转为驼峰写法 
         event.target.dataset.alphabeta == 2      //大写会转为小写 
   }    
})

■touches是一个触摸点的数组。每个元素为一个Touch(触摸点)对象,具有以下属性:
■changedTouches数据格式同touches。表示有变化的触摸点,如从无变有(touch-start)、位置变化(touchmove)、从有变无(touchend、touchcancel)。

WXSS

WXSS是一套样式语言,用于描述WXML的组件样式。它将决定WXML的组件应该怎么显示。
官方文档表明,WXSS的选择器目前支持(“.class”、“#id”、“element”、“element, element”、“::after”、“::before”),而且本地资源无法通过WXSS获取,所以WXSS中的样式都是用的网络图片,或者base64。这样,对于某些前端开发者而言,会有所局限。

与CSS相比,WXSS扩展的特性有:
■尺寸单位。
■样式导入。

1.尺寸单位

WXSS新增了针对移动端屏幕的两种尺寸单位:rpx与rem。
rpx(responsive pixel):可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在iPhone6上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

设备 rpx换算px(屏幕宽度/750) px换算rpx(750/屏幕宽度)
iPhone5 1rpx=0.42px 1px=2.34rpx
iPhone6s 1rpx=0.5px 1px=2rpx
iPhone6sPlus 1rpx=0.552px 1px=1.81rpx

rem(root em):规定屏幕宽度为20rem;1rem =(750/20)rpx。
因此建议,开发微信小程序时设计师可以用iPhone6作为视觉稿的标准。

2.导入样式

可以使用@import语句来导入外联样式表。@import后跟需要导入的外联样式表的相对路径,并用;表示语句结束。
示例代码如下:

/** common.wxss **/   
 .small-p {      padding:5px;    }   
 /** app.wxss **/   
@import "common.wxss";   
 .middle-p {      padding:15px;    }

3.内联样式

内联样式指的是框架组件上支持使用style、class属性来控制组件的样式:
■style:静态的样式统一写到class中。style接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进style中,以免影响渲染速度:
<view style="color:{{color}}; " />
■class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上“. ”,比如,“.normal-view”样式类的使用:
<view class="normal_view" />

4.选择器

WXSS目前支持的选择器有:

选择器 样例 样例描述
.class .intro 选择所有拥有class="intro"组件
#id #firstname 选择所有拥有id="firstname"组件
element view 选择所有view组件
element,element view,checkbox 选择所有文档的view组件和所有的checkbox组件
::after view::after 在view组件后面插入内容
::before view::before 在view组件前边插入内容

5.全局样式和局部样式

定义在app.wxss中的样式为全局样式,作用于每一个页面。在page的.wxss文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖app.wxss中相同的选择器。

6.WXSS与CSS开发的差异

(1)选择器的差异

WXSS提供的选择器目前官网提供得很少,下面也是通过图表来对比下WXSS与CSS选择器的差异


wxss.png
(2)适配

WXSS刚开始时并不能适配各种设备,虽然支持rem,但是并不能改变HTML的属性,这使得HTML 5中的rem适配方案失效。最终微信团队推出了rpx(responsive pixel)这个新的计量单位,它规定屏幕宽度为750rpx,从而可以依据屏幕宽度进行自适应。rpx的实现原理跟rem很相似,而且最终也是换算成rem。
rpx计量最大的优势在于750设计稿不需要进行任何转换即可适配。750设计稿量是多少就是多少,如在iPhone6上,屏幕宽度为375px,共有750个物理像素,则750rpx =375px = 750物理像素,1rpx = 0.5px = 1物理像素。但是目前的方案还存在一定的问题,那就是非750的设计稿则需要进行一次换算,如640的设计稿就需要进行一次换算在640设计稿中的1rpx = 640/750rpx,而在WXSS中并不支持算术运算符,所以小程序的视觉设计稿尽量使用750来给出。

(3)样式级联

如"element element",微信团队回复说“级联会破坏掉组件的结构,级联最终会取消”,因此推荐使用BEM,即Block(块)、Element(元素)、Modifier(修饰符),是由Yandex团队提出的一种CSSClass命名方法。后续会提供另外的一种层级关系来解决依赖层级的情况。虽然现在还能使用级联的写法,但是最终可能会废弃,所以建议大家尽量不要使用级联。

框架组件

组件是视图层的基本组成单元,除自带某些功能外,也具有微信风格的样式。一个组件通常包括“开始标签”和“结束标签”,组件由属性来定义与修饰,放在“开始标签”中。组件的内容则包含在两个标签之内;

注意:
所有的组件与属性都需使用小写字符。
所有组件都有的共同属性:

属性名 类型 描述 注解
id String 组件的唯一标示 保持整个页面唯一
class String 组件的样式类 在对应的wxss中定义的样式类
style String 组件的内联样式 可以动态设置的内联样式
hidden Boolean 组件是否显示 所有组件默认显示
data-* Any 自定义属性 组件上触发事件时,会发送给事件处理函数
bind/catch EventHandler 组件的事件 事件绑定

同时每一个组件也可以有自定义的属性(称为“特殊独有属性”),用于对该组件的功能或样式进行修饰。但属性只支持下面这七种数据类型:

类型 描述 注解
Boolean 布尔值 组件写上该属性,不管该属性等于什么,其值为true,该组件上没写该属性,属性值才为false
Number 数字 1,2,5
String 字符串 "string"
Array 数组 [1,"string"]
Object 对象 [key:value]
EventHandler 事件处理函数名 "handlerName"是page中定义的事件处理函数
Any 任意属性 ...

微信小程序为开发者提供了九大类组件:

组件类型 组件用途 包含组件
视图容器组件 控制视图样式 view\scroll-view\swiper
基础内容组件 图标,文本与进度条 icon\text\progress
表单组件 构建表单 button\form\input\checkbox\radio\picker\picker-view\slider\switch\label\textarea
互动操作组件 操作反馈 action-sheet\modal\toast\loading
页面导航组件 页面链接 navigator
媒体组件 多媒体控制 audio\image\video
地图组件 地图 map
画布组件 画图 canvas
客服会话组件 客服会话服务 contact-button
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容