11React 组件化

11React 组件化

资源:

组件化

快速开始

https://www.html.cn/create-react-app/docs/getting-started/

npx create-react-app reactDemo

cd reactDemo

yarn start

组件化优点

  1. 增强代码重用性,提高开发效率;

  2. 简化调试步骤,提升整个项目的可维护性;

  3. 便于协同开发;

  4. 注意点:降低耦合性。

组件跨层级通信 - Context

image.png

在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不是显式地通过组件数的逐层传递 props。

React 中使用 Context 实现祖代组件向后代组件跨层级传值,Vue 中的 provide & inject 来源于 Context。

Context API

React.createContext

创建一个 Context 对象,当 React 订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provide 中读取到当前的 context 值。

Context.Provider

Provider 接收一个 value 属性,传递给消费组件,允许消费组件订阅 context 的变化。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

当 Provider 的 value 的值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

Class.contextType

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。

你只通过该 API 订阅单一 context。

Context.Consumer

这里,React 组件也可以订阅到 context 变更,这能让你在函数式组件中完成订阅 context。

这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。

useContext

接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。只能用在 function 组件中。

使用 Context

创建 Context => 获取 Provider 和 Consumer => Provider 提供值 => Consumer 消费值

范例:共享主题色

import React, { Component } from 'react'
import ContextTypePage from './ContextTypePage'
import { ThemeContext, UserContext } from '../Context'
import UserContextPage from './UseContextPage'
import ConsumerPage from './ConsumerPage'

export default class ContextPage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      theme: {
        themeColor: 'red'
      },
      user: {
        name: '林慕'
      }
    }
  }

  changeColor = () => {
    const { themeColor } = this.state.name
    this.setState({
      theme: {
        themeColor: themeColor === 'red' ? 'green' : 'red'
      }
    })
  }

  render () {
    const { theme, user } = this.state
    return (
      <div>
        <h3>ContextPage</h3>
        <button onClick={this.changeColor}>change color</button>
        <ThemeContext.Provider value={theme}>
          <ContextTypePage></ContextTypePage>
          <UserContext.Provider value={user}>
            <UserContextPage></UserContextPage>
            <ConsumerPage></ConsumerPage>
          </UserContext.Provider>
        </ThemeContext.Provider>
      </div>
    )
  }
}

Context.js

import React from 'react'

export const ThemeContext = React.createContext({ themeColor: 'pink' })

export const UserContext = React.createContext()

pages/ContextTypePage.js

import React, { Component } from 'react'
import { ThemeContext } from '../Context'

export default class ContextTypePage extends Component {
  static contextType = ThemeContext
  render () {
    const { themeColor } = this.context
    return (
      <div className='border'>
        <h3 className={themeColor}>ContextTypePage</h3>
      </div>
    )
  }
}

pages/ConsumerPage.js

import React, { Component } from 'react'
import { ThemeContext, UserContext } from '../Context'

export default class ConsumerPage extends Compoennt {
  render () {
    return (
      <div className='border'>
        <ThemeContext.Consumer>
          {
            themeContext => {
              <dvi>
                <h3 className={themeContext.themeColor}>ConsumerPage</h3>
                <UserContext.Consumer>
                  {userContext => <HandleUserContext {...userContext}></HandleUserContext>}
                </UserContext.Consumer>
              </dvi>
            }
          }
        </ThemeContext.Consumer>
      </div>
    )
  }
}

function HandleUserContext (userCtx) {
  return <div>{userCtx.name}</div>
}

消费多个 Context

<ThemeProvider value={theme}>
  <ContextTypePage />
  <ConsumerPage />
  {/*多个Context */}
  <UserProvider value={user}>
    <MultipleContextsPage />
  </UserProvider>
</ThemeProvider>

如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。

pages/UseContextPage

import React, { useState, useEffect, useContext } from "react";
import { ThemeContext, UserContext } from "../Context";
export default function UseContextPage (props) {
  const themeContext = useContext(ThemeContext);
  const { themeColor } = themeContext;
  const userContext = useContext(UserContext);
  return (
    <div className="border">
      <h3 className={themeColor}>UseContextPage</h3>
      <p>{userContext.name}</p>
    </div>
  );
}

注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象:

class App extends React.Component {
  render () {
    return (
      <Provider value={{ something: 'something' }}>
        <Toolbar />
      </Provider>
    )
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state 里:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: { something: 'something' },
    };
  }
  render () {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

总结

在 React 的官方文档中,Context 被归类为高级部分(Advanced),属于 React 的高级 API,建议不要滥用。

高阶组件 - HOC

为了提高组件复用率,可测试性,就要保证组件功能单一性;但是若要满足复杂需求就要扩展功能单一的组件,在 React 里就有了 HOC(Higher-Order Component)的概念。

定义:高阶组件是参数为组件,返回值为新组件的函数

基本使用

HocPage.js

import React, { Component } from 'react'

// 这里大写开头的cmp是指function或者class组件
const foo = Cmp => props => {
  return (
    <div className='border'>
      <Cmp {...props}></Cmp>
    </div>
  )
}

function Child (props) {
  return <div>child{props.name}</div>
}

const Foo = foo(Child)
export default class HocPage extends Component {
  render () {
    return (
      <div>
        <h3>HocPage</h3>
        <Foo name='msg'></Foo>
      </div>
    )
  }
}

链式调用

import React, { Component } from 'react'

// 这里大写开头的cmp是指function或者class组件
const foo = Cmp => props => {
  return (
    <div className='border'>
      <Cmp {...props}></Cmp>
    </div>
  )
}

const foo2 = Cmp => props => {
  return (
    <div className='greenBorder'>
      <Cmp {...props}></Cmp>
    </div>
  )
}

function Child (props) {
  return <div>child{props.name}</div>
}

const Foo = foo2(foo(foo(Child)))
export default class HocPage extends Component {
  render () {
    return (
      <div>
        <h3>HocPage</h3>
        <Foo name='msg'></Foo>
      </div>
    )
  }
}

装饰器写法

高阶组件本身是对装饰器模式的应用,自然可以利用 ES7 中出现的装饰器语法来更优雅的书写代码。

yarn add @babel/plugin-proposal-decorators

更新 config-overrides.js

//配置完成后记得重启下
const { addDecoratorsLegacy } = require("customize-cra");
module.exports = override(
  ...,
  addDecoratorsLegacy()//配置装饰器器
);

如果 vsCode 对装饰器有 warning,vsCode 的设置加上:

javascript.implicitProjectConfig.experimentalDecorators": true

装饰器只能用在 class 上,执行顺序从下往上。

HocPage.js

@foo2
@foo
@foo
class Child extends Component {
  render () {
    return (
      <div>
        Child{this.props.name}
      </div>
    )
  }
}

export default class HocPage extends Component {
  render () {
    rerurn(
      <div>
        <h3>HocPage</h3>
        <Child></Child>
      </div>
    )
  }
}

组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

HOC 在 React 的第三方库中很常见,例如 React-Redux 的 connect。

使用 HOC 的注意事项

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

不要在 render 方法中使用 HOC

React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。如果从 render 返回的组件与前一个渲染中的组件相同(===),则 React 通过将子树与新子树进行区分来递归更新子树。如果他们不相等,则完全卸载前一个子树。

render() {
  // 每次调⽤用 render 函数都会创建⼀一个新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
  return <EnhancedComponent />;
}

这不仅仅是性能问题,重新挂载组件会导致该组件及其所有子组件的状态丢失。

表单组件设计与实现

antd 表单使用

实现用户名密码登录,并实现校验

FormPage.js

import React, { Component, useEffect } from "react";
import { Form, Input, Button } from "antd";
const FormItem = Form.Item;
const nameRules = { required: true, message: "请输⼊入姓名!" };
const passworRules = { required: true, message: "请输⼊入密码!" };
export default class AntdFormPage extends Component {
  formRef = React.createRef();
  componentDidMount () {
    this.formRef.current.setFieldsValue({ name: "default" });
  }
  onReset = () => {
    this.formRef.current.resetFields();
  };
  onFinish = val => {
    console.log("onFinish", val); //sy-log
  };
  onFinishFailed = val => {
    console.log("onFinishFailed", val); //sy-log
  };
  render () {
    console.log("AntdFormPage render", this.formRef.current); //sy-log
    return (
      <div>
        <h3>AntdFormPage</h3>
        <Form
          ref={this.formRef}
          onFinish={this.onFinish}
          onFinishFailed={this.onFinishFailed}
          onReset={this.onReset}>
          <FormItem label="姓名" name="name" rules={[nameRules]}>
            <Input placeholder="name input placeholder" />
          </FormItem>
          <FormItem label="密码" name="password" rules={[passworRules]}>
            <Input placeholder="password input placeholder" />
          </FormItem>
          <FormItem>
            <Button type="primary" size="large" htmlType="submit">
              Submit
            </Button>
          </FormItem>
          <FormItem>
            <Button type="default" size="large" htmlType="reset">
              Reset
            </Button>
          </FormItem>
        </Form>
      </div>
    );
  }
}

function 实现:

注意 useForm 是 React Hooks 的实现,只能用于函数组件。

export default function AntdFormPage (props) {
  const [form] = Form.useForm();
  const onFinish = val => {
    console.log("onFinish", val); //sy-log
  };
  const onFinishFailed = val => {
    console.log("onFinishFailed", val); //sy-log
  };
  const onReset = () => {
    form.resetFields();
  };
  useEffect(() => {
    form.setFieldsValue({ name: "default" });
  }, []);
  return (
    <Form
      form={form}
      onFinish={onFinish}
      onFinishFailed={onFinishFailed}
      onReset={onReset}>
      <FormItem label="姓名" name="name" rules={[nameRules]}>
        <Input placeholder="name input placeholder" />
      </FormItem>
      <FormItem label="密码" name="password" rules={[passworRules]}>
        <Input placeholder="password input placeholder" />
      </FormItem>
      <FormItem>
        <Button type="primary" size="large" htmlType="submit">
          Submit
        </Button>
      </FormItem>
      <FormItem>
        <Button type="default" size="large" htmlType="reset">
          Reset
        </Button>
      </FormItem>
    </Form>
  );
}

antd3 表单组件设计思路

  • 表单组件要求实现数据收集、校验、提交等特性,可通过高阶组件扩展

  • 高阶组件给表单组件传递一个 input 组件包装函数接管其输入事件并统一管理表单数据

  • 高阶组件给表单组件传递一个校验函数使其具备数据校验功能

但是 antd3 的设计有个问题,就是局部变化会引起整体变化,antd4 改进了这个问题。

antd4 表单组件实现

antd4 的表单实现基于 rc-field-form,git 源码地址:https://github.com/react-component/field-form

安装 rc-field-form:

yarn add rc-field-form

使用 useForm,仅限 function:

import React, { Component, useEffect } from "react";
// import Form, {Field} from "rc-field-form";
import Form, { Field } from "../components/my-rc-field-form/";
import Input from "../components/Input";
const nameRules = { required: true, message: "请输⼊入姓名!" };
const passworRules = { required: true, message: "请输⼊入密码!" };
export default function MyRCFieldForm (props) {
  const [form] = Form.useForm();
  const onFinish = val => {
    console.log("onFinish", val); //sy-log
  };
  // 表单校验失败执⾏行行
  const onFinishFailed = val => {
    console.log("onFinishFailed", val); //sy-log
  };
  useEffect(() => {
    console.log("form", form); //sy-log
    form.setFieldsValue({ username: "default" });
  }, []);
  return (
    <div>
      <h3>MyRCFieldForm</h3>
      <Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}>
        <Field name="username" rules={[nameRules]}>
          <Input placeholder="input UR Username" />
        </Field>
        <Field name="password" rules={[passworRules]}>
          <Input placeholder="input UR Password" />
        </Field>
        <button>Submit</button>
      </Form>
    </div>
  );
}

class 实现:

export default class MyRCFieldForm extends Component {
  formRef = React.createRef();
  componentDidMount () {
    console.log("form", this.formRef.current); //sy-log
    this.formRef.current.setFieldsValue({ username: "default" });
  }
  onFinish = val => {
    console.log("onFinish", val); //sy-log
  };
  // 表单校验失败执⾏行行
  onFinishFailed = val => {
    console.log("onFinishFailed", val); //sy-log
  };
  render () {
    return (
      <div>
        <h3>MyRCFieldForm</h3>
        <Form
          ref={this.formRef}
          onFinish={this.onFinish}
          onFinishFailed={this.onFinishFailed}>
          <Field name="username" rules={[nameRules]}>
            <Input placeholder="Username" />
          </Field>
          <Field name="password" rules={[passworRules]}>
            <Input placeholder="Password" />
          </Field>
          <button>Submit</button>
        </Form>
      </div>
    );
  }
}

实现 my-rc-field-form

实现 Form/index

import React from "react";
import _Form from "./Form";
import Field from "./Field";
import useForm from "./useForm";
const Form = React.forwardRef(_Form);
Form.Field = Field;
Form.useForm = useForm;
export { Field, useForm };
export default Form;

实现 Form

import React from "react";
import useForm from "./useForm";
import FieldContext from "./FieldContext";
export default function Form ({ children, onFinish, onFinishFailed, form }, ref) {
  const [formInstance] = useForm(form);
  React.useImperativeHandle(ref, () => formInstance);
  formInstance.setCallback({
    onFinish,
    onFinishFailed
  });
  return (
    <form
      onSubmit={event => {
        event.preventDefault();
        event.stopPropagation();
        formInstance.submit();
      }}>
      <FieldContext.Provider value={formInstance}>
        {children}
      </FieldContext.Provider>
    </form>
  );
}

实现 FieldContext

import React from "react";
const warnFunc = () => {
  console.log("----------err--------"); //sy-log
};
const FieldContext = React.createContext({
  registerField: warnFunc,
  setFieldsValue: warnFunc,
  getFieldValue: warnFunc,
  getFieldsValue: warnFunc,
  submit: warnFunc
});
export default FieldContext;

实现 useForm

import React from "react";
class FormStore {
  constructor() {
    this.store = {}; //存储数据,⽐比如说username password
    this.fieldEntities = [];
    // callback onFinish onFinishFailed
    this.callbacks = {};
  }
  // 注册
  registerField = entity => {
    this.fieldEntities.push(entity);
    return () => {
      this.fieldEntities = this.fieldEntities.filter(item => item !== entity);
      delete this.store[entity.props.name];
    };
  };
  setCallback = callback => {
    this.callbacks = {
      ...this.callbacks,
      ...callback
    };
  };
  // 取数据
  getFiledValue = name => {
    return this.store[name];
  };
  getFiledsValue = () => {
    return this.store;
  };
  // 设置数据
  setFieldsValue = newStore => {
    this.store = {
      ...this.store,
      ...newStore
    };
    this.fieldEntities.forEach(entity => {
      const { name } = entity.props;
      Object.keys(newStore).forEach(key => {
        if (key === name) {
          entity.onStoreChange();
        }
      });
    });
  };
  validate = () => {
    let err = [];
    // todo
    this.fieldEntities.forEach(entity => {
      const { name, rules } = entity.props;
      let value = this.getFiledValue(name);
      let rule = rules && rules[0];
      if (rule && rule.required && (value === undefined || value === "")) {
        // 出错
        err.push({
          [name]: rules.message,
          value
        });
      }
    });
    return err;
  };
  submit = () => {
    console.log("this.", this.fieldEntities); //sy-log
    let err = this.validate();
    // 在这⾥里里校验 成功的话 执⾏行行onFinish ,失败执⾏行行onFinishFailed
    const { onFinish, onFinishFailed } = this.callbacks;
    if (err.length === 0) {
      // 成功的话 执⾏行行onFinish
      onFinish(this.getFiledsValue());
    } else if (err.length > 0) {
      // ,失败执⾏行行onFinishFailed
      onFinishFailed(err);
    }
  };
  getForm = () => {
    return {
      registerField: this.registerField,
      setCallback: this.setCallback,
      submit: this.submit,
      getFiledValue: this.getFiledValue,
      getFiledsValue: this.getFiledsValue,
      setFieldsValue: this.setFieldsValue
    };
  };
}
// ⾃自定义hook
export default function useForm (form) {
  const formRef = React.useRef();
  if (!formRef.current) {
    if (form) {
      formRef.current = form;
    } else {
      // new ⼀一个
      const formStore = new FormStore();
      formRef.current = formStore.getForm();
    }
  }
  return [formRef.current];
}

实现 Field

import React, { Component } from "react";
import FieldContext from "./FieldContext";
export default class Field extends Component {
  static contextType = FieldContext;
  componentDidMount () {
    const { registerField } = this.context;
    this.cancelRegisterField = registerField(this);
  }
  componentWillUnmount () {
    if (this.cancelRegisterField) {
      this.cancelRegisterField();
    }
  }
  onStoreChange = () => {
    this.forceUpdate();
  };
  getControlled = () => {
    const { name } = this.props;
    const { getFiledValue, setFieldsValue } = this.context;
    return {
      value: getFiledValue(name), //取数据
      onChange: event => {
        // 存数据
        const newValue = event.target.value;
        setFieldsValue({ [name]: newValue });
      }
    };
  };
  render () {
    console.log("field render"); //sy-log
    const { children } = this.props;
    const returnChildNode = React.cloneElement(children,
      this.getControlled());
    return returnChildNode;
  }
}

实现 my-rc-form

import React, { Component } from "react";
// import {createForm} from "rc-form";
import createForm from "../components/my-rc-form/";
import Input from "../components/Input";
const nameRules = { required: true, message: "请输⼊入姓名!" };
const passworRules = { required: true, message: "请输⼊入密码!" };
@createForm
class MyRCForm extends Component {
  constructor(props) {
    super(props);
    // this.state = {
    // username: "",
    // password: ""
    // };
  }
  componentDidMount () {
    this.props.form.setFieldsValue({ username: "default" });
  }
  submit = () => {
    const { getFieldsValue, validateFields } = this.props.form;
    // console.log("submit", getFieldsValue()); //sy-log
    validateFields((err, val) => {
      if (err) {
        console.log("err", err); //sy-log
      } else {
        console.log("校验成功", val); //sy-log
      }
    });
  };
  render () {
    console.log("props", this.props); //sy-log
    // const {username, password} = this.state;
    const { getFieldDecorator } = this.props.form;
    return (
      <div>
        <h3>MyRCForm</h3>
        {getFieldDecorator("username", { rules: [nameRules] })(
          <Input placeholder="Username" />
        )}
        {getFieldDecorator("password", { rules: [passworRules] })(
          <Input placeholder="Password" />
        )}
        <button onClick={this.submit}>submit</button>
      </div>
    );
  }
}
export default MyRCForm;

实现:

import React, { Component } from "react";
export default function createForm (Cmp) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = {};
      this.options = {};
    }
    handleChange = e => {
      const { name, value } = e.target;
      this.setState({ [name]: value });
    };
    getFieldDecorator = (field, option) => InputCmp => {
      this.options[field] = option;
      return React.cloneElement(InputCmp, {
        name: field,
        value: this.state[field] || "",
        onChange: this.handleChange
      });
    };
    setFieldsValue = newStore => {
      this.setState(newStore);
    };
    getFieldsValue = () => {
      return this.state;
    };
    validateFields = callback => {
      // 自己想象吧~
    };
    getForm = () => {
      return {
        form: {
          getFieldDecorator: this.getFieldDecorator,
          setFieldsValue: this.setFieldsValue,
          getFieldsValue: this.getFieldsValue,
          validateFields: this.validateFields
        }
      };
    };
    render () {
      return <Cmp {...this.props} {...this.getForm()} />;
    }
  };
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270