如何用React创建一个真实项目

假设我们要创建一个应用,如何用react的思维进行创建呢?本例是react官网的例子,主要是进行更详细的叙述和细节补充。

注:本文默认您已经清楚react的基本用法了,不清楚请点击React官网学习哦。

我们要创建一个什么样的应用?

假设我们要创建一个商品列表的应用。
界面如下(一般界面我们可以从设计师处拿到)


而我们从后端获取的数据如下:

[
  {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
  {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
  {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
  {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
  {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
  {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];

Step 1: Break The UI Into A Component Hierarchy

(把UI分成层级组件)

首先你可以根据UI图形为每个component划分区域(一个矩形之类),如果设计稿做得好,很可能每个图层的名称就是我们的组件名称。

如何进行分层呢?原则和我们创建一个函数和对象是一致的,就是“单一职责原则”。一个组件理论上应该只做一件事。如果内能还能在划分,那么它应该继续细化。

由于您经常向用户显示JSON数据模型,您会发现,如果您的模型构建正确,您的UI(以及您的组件结构)将很好地映射。 这是因为UI和数据模型倾向于遵循相同的信息架构,这意味着将UI分成组件的工作往往是微不足道的。 只需将其分解成代表您的数据模型的一部分。

基于以上原则,我们把应用分成以下组件。

  • FilterableProductTable (橘色): contains the entirety of the example
  • SearchBar (蓝色): receives all user input
  • ProductTable (绿色): displays and filters the data collection based on user input
  • ProductCategoryRow (蓝绿色): displays a heading for each category
  • ProductRow (红色): displays a row for each product

所以我们的组件层级结果如下

  • FilterableProductTable
  • SearchBar
  • ProductTable
    • -- ProductCategoryRow
    • -- ProductRow

根据我们的结构创建初级代码

代码如下

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
import  './myCss.css'
class FilterableProductTable extends Component {

    render() {
        return (<div>
            <SearchBar/>
            <ProductTable/>
        </div>)
    }
}

class SearchBar extends Component {

    render() {
        return (<h2>SearchBar</h2>)
    }
}

class ProductTable extends Component {


    render() {
        return (<div>
            <h2>
            ProductTable
            </h2>
            <div>
                <ProductCategoryRow/>
                <ProductRow/>
            </div>
        </div>)
    }
}

class ProductCategoryRow extends Component {


    render() {
        return (<div>ProductCategoryRow</div>)
    }
}

class ProductRow extends Component {


    render() {
        return (<div>ProductRow</div>)
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

我们预览看看结果怎么样。


Step 2: Build A Static Version in React(用创建一个静态版本)

虽然上面的基础版本看起有点丑,但是确定了我们的组件和层级关系,接下来我们不考虑其交互关系,先创建一个静态版本。
因为创建这样的版本可以可以根据各个模块分别创建,而不需要考虑那么多的数据传递方式。

首先需要确定的是,既然是静态版本,我们还不需要state,因为使用state意味着我们有数据的交互,而我们只是创建静态版本。
而我们为什么要用props呢?因为props是父组件传递给子组件的方式,既然我们确定了层级关系,在静态版本我们也需要确定子组件的数据来源。

至于是自上而下(top-down),还是自下而上(bottom-up),取决于我们的结构,一般小应用我们用自上而下是比较快的,而层级比较多而复杂的组件,我们采取自下而上是比较理想的。

FilterableProductTable的修改

对于这个最大的上层组件,我们确定的是关系如下:

<div>
    <SearchBar/>
    <ProductTable/>
</div>

但既然我们<ProductTable/>需要展示数据,我们的来源就需要从顶层这里获取。
因此我们的组件修改如下:
(只是添加了products的引用,简单吧...)

class FilterableProductTable extends Component {
    render() {
        return (<div>
            <SearchBar/>
            <ProductTable products={PRODUCTS}/>
        </div>)
    }
}

SearchBar的修改

想想我们的SearchBar长什么样?


一个input框和一个checkbox,然后是一段文字

所以很容易就创建以下结构。

ps:我们先不考虑数据交互,先做静态版本,所以这里先不与
products交互。

class SearchBar extends Component {
    render() {
        return (
            <div>
                <input type="text" placeholder="Search"/>
                <p>
                    <input type="checkbox"/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}

这是的p标签只是用以区分input和checkbox,或者说换行用的,可以自行替换.

ProductTable的修改


同样,我们根据UI来看html应该怎么写。

首先是一个table,里面包着两个heade(Name & Price),
Sporting GoodsElectronics呢?
它们也是每一个类别的表头,但是横跨两行。所以我们可以轻松写出这样的结构。

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>Price</th>
    </tr>
    </thead>
    <tbody>
        <tr>
            <th cols={2}>{category}</th>
        </tr>
        <tr>
            <td>{name}</td>
            <td>{price}</td>
        </tr>
    </tbody>
</table>

这里,Name和Price是固定的,而后面的内容是根据后台数据变化的,所以可以把这部分设置为一个变量,如下:

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>Price</th>
    </tr>
    </thead>
    <tbody>{rows}</tbody>
</table>

这里我们的rows要怎么写呢?
考虑一下,我们首先需要一个数组,包含所有的table内容,每个类别有一个表头和内容,
所以应该是这样的

rows=[<Category1/>,<Row1/>,<Row2/>,
<Category2/>,<Row3/>,</Row4/>]

而我们的后台数据是这样的

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];

这么形成rows形式的结构呢?就是对一个数组判断,如果有一个category(比如Sporting Goods),就push这个表头,然后push后面的内容.
如果下一个对象的category还是一样的,我们就忽略这个category,但是继续push其他内容。直到我们遇到下一个category(比如Electronics),我们才push这个表头。

const rows = [];
let lastCategory = null;//作为是否是新category的判断

//首先我们需要从父元素获取这个products的对象列表,然后对每个元素进行遍历(category有新的就push表头,否则就push内容)
this.props.products.forEach((product) => {
    //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
    if (product.category !== lastCategory) {
        rows.push(
            <ProductCategoryRow
                category={product.category}
                key={product.category}/>
        );
        //这里添加cataegory时因为子元素需要获取每个表头的名字,key就不多说了,不清楚可以看官网了解key的作用
    }
    rows.push(
        <ProductRow
            product={product}
            key={product.name}/>
    );
    //这里添加product是因为子元素需要获取每个product的内容,包括价格,名称等等
    lastCategory = product.category;
});

所以我们创建了一个单项数据流的结构,自上而下,只有一个render()方法,当我们需要确认UI变化的原因,可以很容易地追溯到。

综上,我们的ProductTable代码如下:

class ProductTable extends React.Component {
    render() {
        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {
            //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

ProductCategoryRow的修改

这里的html结构我们上面已经讨论过了。
由于需要category的字段,我们直接从父元素获取(this.props.category)

class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (
        <tr>
            <th cols={2}>{category}</th>
        </tr>
        )
    }
}

ProductRow的修改

结构之前已经讨论过,而我们需要对是否stocked进行标记,所以采取
三元表示符。(stocked为false的颜色为红)

 const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

完整的代码

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    render() {
        return (<div>
            <SearchBar/>
            <ProductTable products={PRODUCTS}/>
        </div>)
    }
}

class SearchBar extends Component {
    render() {
        return (
            <div>
                <input type="text" placeholder="Search"/>
                <p>
                    <input type="checkbox"/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}

class ProductTable extends React.Component {
    render() {
        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {
            //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

class ProductCategoryRow extends Component {

    render() {
        var category = this.props.category;
        return (<tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

预览一下:

Step 3: Identify The Minimal (but complete) Representation Of UI State

要使UI产生交互效果,在REACT中我们是通过state来进行的。

考虑一下我们的的各种数据有:

  • 后端提供的products列表。
  • input标签的输入框产生的内容
  • checkbox的结果
  • ProductTable过滤后的结果

考虑是不是state数据有以下原则

  • 是否可以从父组件通过props传递过来?
    • If so, it probably isn’t state.
  • 是否是一个常量?
    • If so, it probably isn’t state.
  • 是否可以根据其他state或者props计算出来?
    • If so, it isn’t state.

接下来逐项分析:

  • products列表的内容可以从父元素的props获取,所以不是state。
  • 搜索框和checkbox的内容似乎是state,因为它们无法从其他元素获取或者计算得出。
  • 最后,过滤的列表也不是state,因为它可以通过搜索框和checkbox的值进行筛选得出。

最终,我们只有两个state需要设置。

  1. 搜索框里用户输入的值
  2. checkbox的值

Step 4:Identify Where Your State Should Live

好的,所以我们确定了最小的应用状态是什么。 接下来,我们需要确定哪个组件应该改变或拥有这种状态。

记住:React是一个单向数据流的层级结构,而我们或许一开始并不清楚需要把state设置在哪里。
这通常是对入门者最困扰的地方,但是我们可以根据下面的步骤进行分析。

对每个state进行分析:

  • Identify every component that renders something based on that state.
  • 找出每个需要根据state进行渲染的组件
  • Find a common owner component (a single component above all the components that need the state in the hierarchy).
  • 找出一个共通组件(一般是层级组件的上层)
  • Either the common owner or another component higher up in the hierarchy should own the state.
  • 一个共通组件或者另外的更高阶层的组件应该拥有这些state
  • If you can’t find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component.
  • 如果没有找到一个组件适合去存放这些state,我们可以创建一个。

我们根据上面的原则进行分析

  • ProductTable需要根据与用户在搜索框的输入和checkbox的勾选进行过滤数据,把过滤后的数据展示在上面。

  • 共通的组件便是FilterableProductTable.(ProductTable和SearchBar的共通父级)

Cool, so we’ve decided that our state lives in FilterableProductTable

① 在FilterableProductTable 建立初始statethis.state = {filterText: '', inStockOnly: false}
② 在<ProductTable/> 和 <SearchBar/>传递props filterText 和 inStockOnly=state对应的值,使子组件获取到该值。

<div>
<SearchBar filterText={this.state.filterText}inStockOnly={this.state.inStockOnly}/>

<ProductTable products={PRODUCTS}
filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
</div>

③ 用这些props去过滤ProductTable的内容,并且把SeachBar对应的props设为这些state。

class SearchBar extends Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}/>
                <p>
                    <input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}
class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

完整的代码

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    constructor(props) {
        super(props)
        this.state = {
            filterText: '',
            inStockOnly: false
        };
    }
    render() {
        return (<div>
            <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
            <ProductTable products={PRODUCTS}
                          filterText={this.state.filterText}
                          inStockOnly={this.state.inStockOnly}/>
        </div>)
    }
}

class SearchBar extends Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}/>
                <p>
                    <input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}


class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}


class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (<tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

Step 5: Add Inverse Data Flow

到目前为止,我们已经构建了一个应用程序,可以在层次结构中用function的props或者state进行正确地渲染。 现在是时候来进行数据交互了。

React这样的数据结构使我们能明确地知道各个组件之间的数据流动,但相对一般的双向数据流,我们需要多一步(通过回调函数)来改变state的值。

如果你尝试在上一步建立的app中进行输入或者改变checkbox的状态,你会发现是无效的。原因是我们设置了input的值永远是由FilterableProductTable的state进行传递的。

让我们确认一下发生了什么。我们想要确定无论何时,用户改变表单,我们就根据输入的内容(包括input和checkbox)改变我们的state。
FilterableProductTable 会传递回调函数,只要我们的state需要变更,它便会触发。

我们可以通过onChange作为这个回调函数,一旦用户的表单发生改变,我们就用setState()来改变state。

完整代码

相对上一步,我们就多了两个函数,onChange的时候调用这两个函数进行改变state,注意要绑定this哦😯...

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    constructor(props) {
        super(props)
        this.state = {
            filterText: '',
            inStockOnly: false
        };
        this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
        this.handleInStockChange = this.handleInStockChange.bind(this);
    }
    handleFilterTextChange(filterText){
        this.setState({
            filterText: filterText
        });
    }

    handleInStockChange(inStockOnly){
        this.setState({
            inStockOnly: inStockOnly
        })
    }
    render() {
        return (<div>
            <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}
                       onFilterTextChange={this.handleFilterTextChange}
                       onInStockChange={this.handleInStockChange}/>
            <ProductTable products={PRODUCTS}
                          filterText={this.state.filterText}
                          inStockOnly={this.state.inStockOnly}/>
        </div>)
    }
}

class SearchBar extends Component {
    constructor(props) {
        super(props);
        this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
        this.handleInStockChange = this.handleInStockChange.bind(this);
    }

    handleFilterTextChange(e) {
        this.props.onFilterTextChange(e.target.value);
    }

    handleInStockChange(e) {
        this.props.onInStockChange(e.target.checked);
    }

    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}  onChange={this.handleFilterTextChange}/>
                <p>
                    <input type="checkbox" checked={inStockOnly} onChange={this.handleInStockChange}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}


class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}


class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (
        <tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

此时就可以进行筛选和搜索了。



以上,转载请说明来源,蟹蟹......

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容