11React 組件化

11React 組件化

資源:

組件化

快速開始

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

npx create-react-app reactDemo

cd reactDemo

yarn start

組件化優(yōu)點

  1. 增強代碼重用性嫉入,提高開發(fā)效率豁遭;

  2. 簡化調試步驟糠涛,提升整個項目的可維護性;

  3. 便于協(xié)同開發(fā)驻债;

  4. 注意點:降低耦合性乳规。

組件跨層級通信 - Context

image.png

在一個典型的 React 應用中,數據是通過 props 屬性自上而下(由父及子)進行傳遞的合呐,但這種做法對于某些類型的屬性而言是極其繁瑣的(例如:地區(qū)偏好暮的,UI 主題),這些屬性是應用程序中許多組件都需要的淌实。Context 提供了一種在組件之間共享此類值的方式冻辩,而不是顯式地通過組件數的逐層傳遞 props。

React 中使用 Context 實現(xiàn)祖代組件向后代組件跨層級傳值拆祈,Vue 中的 provide & inject 來源于 Context恨闪。

Context API

React.createContext

創(chuàng)建一個 Context 對象,當 React 訂閱了這個 Context 對象的組件放坏,這個組件會從組件樹中離自身最近的那個匹配的 Provide 中讀取到當前的 context 值咙咽。

Context.Provider

Provider 接收一個 value 屬性,傳遞給消費組件淤年,允許消費組件訂閱 context 的變化钧敞。一個 Provider 可以和多個消費組件有對應關系。多個 Provider 也可以嵌套使用麸粮,里層的會覆蓋外層的數據溉苛。

當 Provider 的 value 的值發(fā)生變化時,它內部的所有消費組件都會重新渲染豹休。Provider 及其內部 consumer 組件都不受制于 shouldComponentUpdate 函數炊昆,因此當 consumer 組件在其祖先組件退出更新的情況下也能更新。

Class.contextType

掛載在 class 上的 contextType 屬性會被重賦值為一個由 React.createContext() 創(chuàng)建的 Context 對象威根。這能讓你使用 this.context 來消費最近 Context 上的那個值凤巨。你可以在任何生命周期中訪問到它,包括 render 函數中洛搀。

你只通過該 API 訂閱單一 context敢茁。

Context.Consumer

這里,React 組件也可以訂閱到 context 變更留美,這能讓你在函數式組件中完成訂閱 context彰檬。

這個函數接收當前的 context 值,返回一個 React 節(jié)點谎砾。傳遞給函數的 value 值等同于往上組件樹離這個 context 最近的 Provider 提供的 value 值逢倍。如果沒有對應的 Provider,value 參數等同于傳遞給 createContext() 的 defaultValue景图。

useContext

接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當前值较雕。當前 context 值由上層組件中距離當前組件最近的 <MyContext.Provider> 的 value prop 決定。只能用在 function 組件中。

使用 Context

創(chuàng)建 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 值經常被一起使用亮蒋,那你可能要考慮一下另外創(chuàng)建你自己的渲染組件扣典,以提供這些值。

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 組件中觸發(fā)意外的渲染趁怔。舉個例子湿硝,當每一次 Provider 重渲染時,以下的代碼會重渲染所有下面的 consumers 組件痕钢,因為 value 屬性總是被賦值為新的對象:

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

為了防止這種情況图柏,將 value 狀態(tài)提升到父節(jié)點的 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 中出現(xiàn)的裝飾器語法來更優(yōu)雅的書寫代碼。

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 上矗积,執(zhí)行順序從下往上。

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 算法(稱為協(xié)調)使用組件標識來確定它是應該更新現(xiàn)有子樹還是將其丟棄并掛載新子樹茵烈。如果從 render 返回的組件與前一個渲染中的組件相同(===),則 React 通過將子樹與新子樹進行區(qū)分來遞歸更新子樹砌些。如果他們不相等瞧毙,則完全卸載前一個子樹。

render() {
  // 每次調?用 render 函數都會創(chuàng)建?一個新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 這將導致子樹每次渲染都會進行卸載寄症,和重新掛載的操作宙彪!
  return <EnhancedComponent />;
}

這不僅僅是性能問題,重新掛載組件會導致該組件及其所有子組件的狀態(tài)丟失有巧。

表單組件設計與實現(xiàn)

antd 表單使用

實現(xiàn)用戶名密碼登錄释漆,并實現(xiàn)校驗

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 實現(xiàn):

注意 useForm 是 React Hooks 的實現(xiàn)男图,只能用于函數組件。

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 表單組件設計思路

  • 表單組件要求實現(xiàn)數據收集甜橱、校驗逊笆、提交等特性,可通過高階組件擴展

  • 高階組件給表單組件傳遞一個 input 組件包裝函數接管其輸入事件并統(tǒng)一管理表單數據

  • 高階組件給表單組件傳遞一個校驗函數使其具備數據校驗功能

但是 antd3 的設計有個問題岂傲,就是局部變化會引起整體變化难裆,antd4 改進了這個問題。

antd4 表單組件實現(xiàn)

antd4 的表單實現(xiàn)基于 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
  };
  // 表單校驗失敗執(zhí)?行行
  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 實現(xiàn):

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
  };
  // 表單校驗失敗執(zhí)?行行
  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>
    );
  }
}

實現(xiàn) my-rc-field-form

實現(xiàn) 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;

實現(xiàn) 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>
  );
}

實現(xiàn) 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;

實現(xiàn) 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();
    // 在這?里里校驗 成功的話 執(zhí)?行行onFinish ,失敗執(zhí)?行行onFinishFailed
    const { onFinish, onFinishFailed } = this.callbacks;
    if (err.length === 0) {
      // 成功的話 執(zhí)?行行onFinish
      onFinish(this.getFiledsValue());
    } else if (err.length > 0) {
      // 归薛,失敗執(zhí)?行行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];
}

實現(xiàn) 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;
  }
}

實現(xiàn) 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;

實現(xiàn):

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()} />;
    }
  };
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末主籍,一起剝皮案震驚了整個濱河市习贫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崇猫,老刑警劉巖沈条,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诅炉,居然都是意外死亡蜡歹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門涕烧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來月而,“玉大人,你說我怎么就攤上這事议纯「缚睿” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長憨攒。 經常有香客問我世杀,道長,這世上最難降的妖魔是什么肝集? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任瞻坝,我火速辦了婚禮,結果婚禮上杏瞻,老公的妹妹穿的比我還像新娘所刀。我一直安慰自己,他們只是感情好捞挥,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布浮创。 她就那樣靜靜地躺著,像睡著了一般砌函。 火紅的嫁衣襯著肌膚如雪斩披。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天胸嘴,我揣著相機與錄音雏掠,去河邊找鬼。 笑死劣像,一個胖子當著我的面吹牛乡话,可吹牛的內容都是我干的。 我是一名探鬼主播耳奕,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绑青,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屋群?” 一聲冷哼從身側響起闸婴,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芍躏,沒想到半個月后邪乍,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡对竣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年庇楞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片否纬。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吕晌,死狀恐怖,靈堂內的尸體忽然破棺而出临燃,到底是詐尸還是另有隱情睛驳,我是刑警寧澤烙心,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站乏沸,受9級特大地震影響淫茵,放射性物質發(fā)生泄漏。R本人自食惡果不足惜屎蜓,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一痘昌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炬转,春花似錦、人聲如沸算灸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菲驴。三九已至荐吵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赊瞬,已是汗流浹背先煎。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留巧涧,地道東北人薯蝎。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像谤绳,于是被迫代替她去往敵國和親占锯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354