thymeleaf

Thymeleaf学习笔记

SpringThymeleaf

Web开发离不开动态页面的开发,很早以前企业主要使用JSP技术来开发网页,随着技术的升级更替,目前来说最主流的方案是: Thymeleaf, Thymeleaf 是一个模板框架,它可以支持多种格式的内容动态渲染非常强大,它天然和HTML是相融合的,所以对于前端工程师来说它也是易于理解的。Thymeleaf机制:数据+模板+引擎

初始化Thymeleaf

添加依赖


<dependency>

  <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

代码举例:


@Controller

public class SongListControl {

  @Autowired

  private SongListService songListService;

  @RequestMapping("/songlist")

  public String index(@RequestParam("id")String id,Model model){

    SongList songList = songListService.get(id);

    //传递歌单对象到模板当中

    //第一个 songList 是模板中使用的变量名

    // 第二个 songList 是当前的对象实例

    model.addAttribute("songList",songList);

    return "songList";

  }

}

注意:

springboot默认访问的是templates中的内容,也就是说return后面的地址接的是templates中的文件路径.想要访问static中的文件路径,就必须要在return后面接redirect重定向。

Thymeleaf变量

由于Thymeleaf是完全兼容HTML的,所以为了不破坏HTML结构,Thymeleaf 采用了自定义HTML属性的方式来生成动态内容。th:text语法的作用就是会动态替换掉作用HTML标签的内部内容。

  • 代码举例:

<span th:text="${msg}">Hello</span>

  • 渲染结果

<span>你好</span>

一般情况下,模板的变量都是会存放在模板上下文中,所以我们如果想要调用变量,就需要先设置变量到模板上下文中去

扩展:

上下文这个术语在编程里用的比较多,一 般泛指在某个具体的实例里,比如说模板上下文, 指的是这个模板运行的实例。

对象变量

注意:
如果对象包含对象,还是可以用.一直点出来的, 前提是这个对象是POJO类哦

  • Thymeleaf循环语句

<ul th:each="song : ${songs}">

  <li th:text="${song.name}">歌曲名称</li>

</ul>

打印列表的索引值

<ul th:each="song,it: ${songs}">

  <li>

    <span th:text="${it.count}"></span>

    <span th:text="${song.name}"></span>

  </li>

</ul>

这个it是作为可选参数,如果定义了就可以通过这个it对象来获取更多关于统计的需求,具体参数如

Thymeleaf表达式

1..${}

字符串处理


<span th:text="'00:00/'+${totalTime}"></span>

静态内容用单引号''包裹起来,和上下文变量之间用+连接。

字符串拼接优化

Thymeleaf做字符串拼接还做了优化工作,我们可以使用上面的代码你还可以这样|围住字符串,这样就不需要在文字后面附加'...'+'...'


<span th:text="|00:00/${totalTime}|"></span>

数据转化

Thymeleaf默认集成了大量的工具类可以方便的进行数据转化,一我们使用最多的是dates


@RequestMapping("/demo")

  public String index(Model model){

    Date dateVar = new Date();

    model.addAttribute("dateVar",dateVar);

    return "demo";

  }

前台显示:

//显示年月日


<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd')}"></p>

<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日')}"></p>

//显示年月日时分秒


<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>

<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>

在这里如果后台的日期类型是LocalDate/LocalDateTime,那么就把#date换成#temporals

注意:需要在pom.xml文件里添加依赖


<dependency>

  <groupId>org.thymeleaf.extras</groupId>

  <artifactId>thymeleaf-extras-java8time</artifactId>

  <version>3.0.4.RELEASE</version>

</dependency>

这个库会自动添加一个新的工具类temporals

工具类的运用和变量不同,变量使用的是${变量名} ,工具类使用的是#{工具类}。

后台代码:


@RequestMapping("/demo")

  public String index(Model model){

    LocalDateTime dateVar = LocalDateTime.now();

    model.addAttribute("dateVar",dateVar);

    return "demo";

  }

前台代码:

//显示年月日


<p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd')}"></p>

<p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日')}"></p>

````html

//显示年月日时分秒

````html

<p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>

<p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>

dates/temporals工具类说明

dates和temporals支持的方法是一样的, 只是支持的类型不同,dates支持的是Date类, temporals支持的是LocalDate 和LocalDateTime。

java.util.Date类和LocalDateTime类功能是一样的,不同的是LocalDateTime是Java8才出现的,一老的应用还是用Date类的。

strings工具类

Thymeleaf里的其他工具类

各内置对象包含哪些方法可以点此看文档,也可以点这里看官方文档。

Thymeleaf内联表达式

其实就是th:text的另一种写法,直接把变量写在HTML中,格式:[[变量]]


<span>Hello [[${msg}]]</span>

<p>[[ ${#dates.format(dateVar, 'yyyy-MM-dd')} ]]</p>

<p>[[${#dates.format(dateVar, 'yyyy年MM月dd日')}]]</p>

<p>[[${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}]]</p>

<p>[[${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}]]</p>

注意:

内联表达式[[]]只能替代th:tex不是替代所有的th:标签。

2.*{}

选择表达式(星号表达式)。选择表达式与变量表达式有一个重要的区别:选择表达式计算的是选定的对象,而不是整个环境变量映射。也就是:只要是没有选择的对象,选择表达式与变量表达式的语法是完全一样的。那什么是选择的对象呢?是一个:th:object对象属性绑定的对象。

例如:


<div th:object=" ${session.user}" >

<p>Name: <span th: text=" *{firstName}" >Sebastian</span>. </p>

<p>Surname: <span th: text=" *{lastName}" >Pepper</span>. </p>

<p>Nationality: <span th: text=" *{nationality}" >Saturn</span>. </p>

</div>

上例中,选择表达式选择的是th:object对象属性绑定的session. user对象中的属性。

3.#{}

消息表达式(井号表达式,资源表达式)。通常与th:text属性一起使用,指明声明了th:text的标签的文本是#{}中的key所对应的value,而标签内的文本将不会显示。

例如:

新建/WEB-INF/templates/home.html,段落


<p th: text=" #{home. welcome}" >This text will not be show! </p>

新建/WEB-INF/templates/home.properties,home.welcome:

home.welcome=this messages is from home.properties!

测试结果:

从测试结果可以看出,消息表达式通常用于显示页面静态文本,将静态文本维护在properties文件中也方便维护,做国际化等。

4.@{}

超链接url表达式。

例如:


<script th:src="@{/resources/js/jquery/jquery.json-2.4.min.js}"/>

5.#maps

工具对象表达式。常用于日期、集合、数组对象的访问。这些工具对象就像是java对象,可以访问对应java对象的方法来进行各种操作。用法在上面数据转化已经举例

  • dates

  • calendars

  • numbers

  • strings

  • objects

  • bools

  • arrays

  • lists

  • sets

Thymeleaf条件语句

th:if条件判断除了判断boolean值外,Thymeleaf 还认为如下表达式为true:

• 值非空

• 值是非0数字

• 值是字符串,但是不是false, off or no

• 值不是boolean值,数字,character或字符串

Strings逻辑判断

isEmpty

contains

  • 检查字符串变量是否包含片段

${#strings.contains(name,'abc')}

  • strings判断语法的其他几个常用方法

  • strings的字符串操作函数

除了字符串判断语句外,#strings 还支持字符串的数据处理,比如

Thymeleaf表单

Springboot做了自动处理,通过post方式提交表单的时候,需要有一个实体类,去接收表单传递的数据内容,在对象的属性读取中,Thymeleaf 提供了两种方式:1、直接通过${userInfo.username} ,这种实体bean + 属性的方式;2、通过选择表达式*{username}的这种方式。表单input中的name属性值要和实体类的属性保持一致

Spring Validation

在实际的工作中对于数据的保存是离不开数据验证的,比如说name必须要输入,isbn 必须要输入等等校验规则,Spring 对于数据验证支持的也非常好,我们可以借助Spring Validation来处理表单数据的验证。

Validation注解

JSR 380定义了-些注解用于做数据校验,这些注解可以直接设置在Bean的属性上

|注解|说明|备注|

|------|----------|-----------|

|@NotNull |不允许为null对象||

|@AssertTrue|是否为true||

|@Size|约定字符串的长度||

|@Min|字符串的最小长度||

|@Max|字符串的最大长度||

|@Email|是否是邮箱格式||

|@NotEmpty|不允许为null或者为空,可以用于判断字符串、集合,比如Map、数组、List||

|@NotBlank|不允许为null和空格||

注意:

大多数情况下,建议使用NotEmpty替代NotNull、NotBlank


package com.bookstore.model;

import javax.validation.constraints.*;

public class User {

    @NotEmpty(message = "名称不能为 null")

    private String name;

    @Min(value = 18, message = "你的年龄必须大于等于18岁")

    @Max(value = 150, message = "你的年龄必须小于等于150岁")

    private int age;

    @NotEmpty(message = "邮箱必须输入")

    @Email(message = "邮箱不正确")

    private String email;

    // standard setters and getters 

}

校验的注解是可以累加的,如上面的@Min和@Max,系统会按顺序执行校验,任何一条校验触发就会抛出校验错误到上下文中。


<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8" />

    <title>添加用户</title>

    <style>

        .error {

            color: red;

        }

    </style>

</head>

<body>

    <h2>添加用户</h2>

    <form action="/user/save" th:object="${user}" method="POST">

        <div th:classappend="${#fields.hasErrors('name')} ? 'error' : ''">

        <label>用户名称:</label>

        <input type="text" th:field="*{name}">

        <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>

    </div>

    <div th:classappend="${#fields.hasErrors('age')} ? 'error' : ''">

        <label>年龄:</label>

        <input type="text" th:field="*{age}">

        <p th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></p>

    </div>

    <div th:classappend="${#fields.hasErrors('email')} ? 'error' : ''">

        <label>邮箱:</label>

        <input type="text" th:field="*{email}">

        <p th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>

    </div>

    <div th:classappend="${#fields.hasErrors('mobile')} ? 'error' : ''">

        <label>手机号码:</label>

        <input type="text" th:field="*{mobile}">

        <p th:if="${#fields.hasErrors('mobile')}" th:errors="*{mobile}"></p>

    </div>

    <div>

        <button type="submit">保存</button>

    </div>

</form>

</body>

</html>

说明:

1.control类中的方法药先new一个实例对象传到页面去,如下代码中的addUser方法。

2.form表单里的th:object="${user}"用于替换对象,使用了这个就不需要每次都编写user. xxx,可以直接操作XXX

3.${#fields.hasErrors('key')} 这个语法是专门为验证场景提供的,这里的 key就是对象的属性名称,比如User对象的name、age、 email 等。

4.<p></p>标签里的th:errors,用于显示错误信息。

5.<div></div>里的th:classappend支持动态管理样式

6.<input></input>里的th:field可以显示上一次输入的内容。


package com.bookstore.control;

import com.bookstore.model.User;

import com.bookstore.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.validation.BindingResult;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import javax.validation.Valid;

import java.util.List;

@Controller

public class UserControl {

    @Autowired

    private UserService userService;

    @GetMapping("/user/add.html")

    public String addUser(Model model) {

        User user = new User();

        model.addAttribute("user", user);

        return "user/addUser";

    }

    @PostMapping("/user/save")

    public String saveUser(@Valid User user, BindingResult errors) {

        if (errors.hasErrors()) {

            // 如果校验不通过,返回用户编辑页面

            return "user/addUser";

        }

        userService.saveUser(user);

        // 校验通过,返回成功页面

        return "redirect:/user/list.html";//跳转到@GetMapping("/user/list.html")

    }

    @GetMapping("/user/list.html")

    public String list(Model model) {

        List<User> users = userService.getUsers();

        model.addAttribute("users", users);

        return "user/list";

    }

}

说明:

redirect:这个用于跳转到某-一个页面网址,如果是同一个域名,你可以省略域名,直接写path, 比如这里的/user/list. html你也可以跳转到某个网站,比如return "redirect: https: //www. baidu. com";大体上所有的增加、查询行为都是这样的流程。

Thymeleaf布局(Layout)

大多数的网站都有导航、底部等公共的东西,在一个网站里访问页面总是会显示相同的导航、底部之类的内容,layout解决的是模板复用的问题,比如常见的网站是下面这样的

我们推荐使用th:include + th:replace 方案来完成布局的开发

代码实现:

layout.html


<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>

    <title>布局</title>

    <style>

        .header {background-color: #f5f5f5;padding: 20px;}

        .header a {padding: 0 20px;}

        .container {padding: 20px;margin:20px auto;}

        .footer {height: 40px;background-color: #f5f5f5;border-top: 1px solid #ddd;padding: 20px;}

    </style>

</head>

<body>

<header class="header">

    <div>

        <a href="/book/list.html">图书管理</a>

        <a href="/user/list.html">用户管理</a>

    </div>

</header>

<div class="container" th:include="::content">页面正文内容</div>

<footer class="footer">

    <div>

        <p style="float: left">&copy; heerh.com 2017</p>

        <p style="float: right">

            Powered by god666hrh

        </p>

    </div>

</footer>

</body>

user/list.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org"

      th:replace="layout">

<div th:fragment="content">

    <h2>用户列表</h2>

    <div>

        <a href="/user/add.html">添加用户</a>

    </div>

    <table>

        <thead>

        <tr>

            <th>

                用户名称

            </th>

            <th>

                用户年龄

            </th>

            <th>

                用户邮箱

            </th>

        </tr>

        </thead>

        <tbody>

        <tr th:each="user : ${users}">

            <td th:text="${user.name}"></td>

            <td th:text="${user.age}"></td>

            <td th:text="${user.email}"></td>

        </tr>

        </tbody>

    </table>

</div>

</html>

总结:

1.在layout页面需要渲染的标签加th:include="::content"

2.在子页面的<html></html>标签里加th:replace="layout",在渲染内容的标签里加th:fragment="content",

目的是为了和公共页面(layout)里的标签内容对应上。

小技巧:

由于超链接有默认的打开链接行为,所以对于想要执行onclick 的时候,我们一般设置href="javascript:;"

使用thymeleaf进行数字循环

需求:使用thymeleaf模版 根据一个数字在页面循环生成固定的标签

解决方案:需要用到thymeleaf 的#numbers.sequence()函数

示例:


<span th:each="i:${#numbers.sequence(1, 5)}">

   <span th:text="${i}"></span>

</span>

效果如下:


1 2 3 4 5  

ThymeLeaf注释

1.在html里是代码,在解析时是注释

注:常用在保持静态html代码的完整性上

举例:


<table>

<tr th:each="x : ${xs}">

...

</tr>

<!--/*-->

<tr>

...

</tr>

<tr>

...

</tr>

<!--*/-->

</table>

2.在html里是注释,在解析时是代码

举例:


<span>hello!</span>

<!--/*/

<div th:text="${...}">

...

</div>

/*/-->

<span>goodbye!</span>

Thymeleaf调用对象的成员变量值

Thymeleaf调用对象的成员变量值、Map值、List值、属性的方法 、ctx对象、param、session和application

一、概述

本文会对thymeleaf以下功能进行举例说明:

• 调用对象的成员变量的属性

• 调用Map的对象的属性

• 调用List的对象的属性

• 调用属性的方法

• 使用ctx对象

• param:获取request的请求参数

• session:获取session的属性值

• application:获取application的属性值

二、实例说明

1.调用对象的成员变量的属性

  • 演示如下功能

<!-- 获取family的成员变量father的属性 -->

${family.father.name} --> <input type="text" name="userName" th:value="${family.father.name}" />

  • 输出:

=================== 调用对象的成员变量的属性 ===============================

${family.father.name} --> father

2.调用Map的对象的属性

  • 演示如下功能

通过map的key从hashMap获取对象的属性name值: 可以使用”.”或者使用”[]”获取对象值


<!-- 通过map的key从hashMap获取对象的属性name值: 可以使用"."或者使用"[]"获取对象值 -->

${hashMap.hashMapKey.name} --> <input type="text" name="userName" th:value="${hashMap.hashMapKey.name}" /> <br />

<!-- 等价于这条语句:-->

${hashMap['hashMapKey'].name} --> <input type="text" name="userName" th:value="${hashMap['hashMapKey'].name}" /> <br />

  • 输出:

=================== 调用Map的对象的属性 ===============================

${hashMap.hashMapKey.name} --> hashMap_name

等价于这条语句: 

${hashMap['hashMapKey'].name} --> hashMap_name

3. 调用List的对象的属性

  • 演示如下功能

通过[0]获取List的第一个对象的属性name值


<!-- 通过[0]获取List的第一个对象的属性name值 -->

${family.childList[0].name} --> <input type="text" name="userName" th:value="${family.childList[0].name}" /> <br />

<br />

  • 输出:

=================== 调用List的对象的属性 ===============================

${family.childList[0].name} --> son_1

4. 调用属性的方法

  • 演示如下功能

调用属性的方法


<!-- 调用属性的方法 -->

${family.father.name.toUpperCase()} --> <input type="text" name="userName" th:value="${family.father.name.toUpperCase()}" /> <br />

  • 输出: “–>”的左边是语法,右边是对应的输出

=================== 调用属性的方法 ===============================

${family.father.name.toUpperCase()} --> FATHER

5. 使用ctx对象

  • 演示如下功能

    • ctx.locale: 获取#ctx的locale值

    • ctx.variables: 获取#ctx的变量中的一个的值

    • ctx.variables: 变量中值的一个的值,等价于#ctx.variables

    • ctx.httpServletRequest:请求request

    • ctx.httpServletResponse:返回response

    • ctx.httpSession:返回session

    • ctx.servletContext:返回servletContext

在非web的环境里,ctx只是org.thymeleaf.context.IContext的一个实例,而在web的环境里org.thymeleaf.context.IWebContext的一个实例,此接口同时是org.thymeleaf.context.IContext的子类。IWebContext比IContext多了httpServletRequest,httpServletResponse,httpSession,servletContext等属性,详细可以见源代码。


<!-- #ctx是org.thymeleaf.context.IContext -->

${#ctx.locale} --> <input type="text" name="userName" th:value="${#ctx.locale}" /> <br />

<!-- #ctx获取ctx的变量中的一个的值  -->

${#ctx.variables.hashMap} --> <input th:value="${#ctx.variables.hashMap}" ></input> <br />

<!-- vars变量中值的一个的值,等价于#ctx.variables -->

${#ctx.variables.hashMap} --> <input th:value="${#vars.hashMap}" ></input> <br />

<!-- 以下#ctx是org.thymeleaf.context.IWebContext的一个实例,他也是IContext的子类 -->

<!-- #request -->

${#ctx.httpServletRequest} --> <input th:value="${#ctx.httpServletRequest}" ></input> <br />

<!-- #response -->

${#ctx.httpServletResponse} --> <input th:value="${#ctx.httpServletResponse}" ></input> <br />

<!-- #session -->

${#ctx.httpSession} --> <input th:value="${#ctx.httpSession}" ></input> <br />

<!-- #servletContext -->

${#ctx.servletContext} --> <input th:value="${#ctx.servletContext}" ></input> <br />

  • 结果:

=================== 使用ctx对象: Base objects context object ===============================

${#ctx.locale} --> zh_CN

${#ctx.variables.hashMap} -->{hashMapKey=com.hry.spring.support.User@1888a92c}

${#ctx.variables.hashMap} -->{hashMapKey=com.hry.spring.support.User@1888a92c}

${#ctx.httpServletRequest} --> org.apache.catalina.connector.RequestFacade@7fdddbce

${#ctx.httpServletResponse} --> org.apache.catalina.connector.ResponseFacade@6e632047

${#ctx.httpSession} --> 

${#ctx.servletContext} --> org.apache.catalina.core.ApplicationContextFacade@4149c8a4

6.param:获取request的请求参数

  • 演示如下功能

param是org.thymeleaf.context.WebRequestParamsVariablesMap的子,封装了请求参数,下面演示param.size(),param.containsKey('id'),param.get('id')[0]的用法。


<!-- param是org.thymeleaf.context.WebRequestParamsVariablesMap的子类 -->

<!-- 如果 :http://localhost:8080/expressions/complex?id=1,此时有有一个参数输出1 -->

${param.size()} --> <input th:value="${param.size()}" ></input> <br />

${param.containsKey('id')} --> <input th:value="${param.containsKey('id')}" ></input> <br />

${param.get('id')[0]} --> <input th:value="${param.get('id')[0]}" ></input> <br />

等价于:<br/>

${#ctx.httpServletRequest.getParameter('id')} --> <input th:value="${#ctx.httpServletRequest.getParameter('id')}" ></input> <br />

  • 结果:

================= param:获取request的请求参数 ===============================

${param.size()} --> 1

${param.containsKey('id')} --> true

${param.get('id')[0]} --> 112

  • 等价于:

${#ctx.httpServletRequest.getParameter('id')} --> 112

7. session:获取session的属性值

演示如下功能

session是org.thymeleaf.context.WebSessionVariablesMap的子类


================= session:获取session的属性值  ===============================<br/>

<!-- session是org.thymeleaf.context.WebSessionVariablesMap的子类 -->

${session.size()} --> <input th:value="${session.size()}" ></input> <br />

  • 结果

================= session:获取session的属性值 ===============================

${session.size()} --> 0

8. application:获取application的属性值

  • 演示如下功能

application是org.thymeleaf.context.WebServletContextVariablesMap的子类


================= application:获取application的属性值  ========================<br/>

<!-- application是org.thymeleaf.context.WebServletContextVariablesMap的子类 -->

${application.size()} --> <input th:value="${application.size()}" ></input> <br />

  • 结果:

================= application:获取application的属性值 ========================

${application.size()} --> 9

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

推荐阅读更多精彩内容