React Hooks

使用React Hooks有什么優(yōu)勢?

什么是hooks
hook 是一些可以讓你在函數(shù)組件里面鉤入react state 以及生命周期的特定的函數(shù)。

  • Hooks 本質(zhì)是把面向生命周期編程變成了面向業(yè)務邏輯編程;
  • Hooks 使用上是一個邏輯狀態(tài)盒子,輸入輸出表示的是一種聯(lián)系;
  • Hooks 是 React 的未來,但還是無法完全替代原始的 Class。
  • 每個 Hook 都為Function Component提供使用 React 狀態(tài)和生命周期特性的通道。Hooks 不能在Class Component中使用。

疑問

    1. 為什么只能在函數(shù)最外層調(diào)用 Hook,不要在循環(huán)、條件判斷或者子函數(shù)中調(diào)用?
    1. 為什么 useEffect 第二個參數(shù)是空數(shù)組,就相當于 ComponentDidMount ,只會執(zhí)行一次?
    1. 自定義的 Hook 是如何影響使用它的函數(shù)組件的?
    1. Capture Value 特性是如何產(chǎn)生的?

class 組件

主要問題

  • 在hooks出來之前,常見的代碼重用方式是HOCs和render props,這兩種方式帶來的問題是:你需要解構(gòu)自己的組件,非常的笨重,同時會帶來很深的組件嵌套
  • 復雜的組件邏輯:復雜的業(yè)務邏輯里面存在各種生命周期,導致代碼拆分比較困難,很難復用
  • 難理解的class 組件

hooks 的到來:

帶組件狀態(tài)的邏輯很難重用:

  • class 組件復用主要通過引入render props或higher-order components這樣的設計模式。如react-redux提供的connect方法。這種方案不夠直觀,而且需要改變組件的層級結(jié)構(gòu),極端情況下會有多個wrapper嵌套調(diào)用的情況。

  • Hooks可以在不改變組件層級關(guān)系的前提下,方便的重用帶狀態(tài)的邏輯。也可以自己定義狀態(tài)組件

復雜組件難于理解:

  • 大量的業(yè)務邏輯需要放在componentDidMount和componentDidUpdate等生命周期函數(shù)中,而且往往一個生命周期函數(shù)中會包含多個不相關(guān)的業(yè)務邏輯,如日志記錄和數(shù)據(jù)請求會同時放在componentDidMount中。另一方面,相關(guān)的業(yè)務邏輯也有可能會放在不同的生命周期函數(shù)中,如組件掛載的時候訂閱事件,卸載的時候取消訂閱,就需要同時在componentDidMount和componentWillUnmount中寫相關(guān)邏輯。

  • Hooks可以封裝相關(guān)聯(lián)的業(yè)務邏輯,讓代碼結(jié)構(gòu)更加清晰。

難于理解的 Class 組件:

  • JS 中的this關(guān)鍵字讓不少人吃過苦頭,它的取值與其它面向?qū)ο笳Z言都不一樣,是在運行時決定的

  • Hooks可以在不引入 Class 的前提下,使用 React 的各種特性。

class RandomUserModal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      user: {},
      loading: false,
    };
    this.fetchData = this.fetchData.bind(this);
  }

  componentDidMount() {
    if (this.props.visible) {
      this.fetchData();
    }
  }

  componentDidUpdate(prevProps) {
    if (!prevProps.visible && this.props.visible) {
      this.fetchData();
    }
  }
// 獲取數(shù)據(jù)
  fetchData() {
// 打開loading
    this.setState({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(res => res.json())
      .then(json => this.setState({
        user: json.results[0],
        loading: false,
      }));
  }

  render() {
    const user = this.state.user;
    return (
      <ReactModal
        isOpen={this.props.visible}
      >
        <button onClick={this.props.handleCloseModal}>Close Modal</button>
        {this.state.loading ?
          <div>loading...</div>
          :
          <ul>
            <li>Name: {`${(user.name || {}).first} ${(user.name || {}).last}`}</li>
            <li>Gender: {user.gender}</li>
            <li>Phone: {user.phone}</li>
          </ul>
        }
      </ReactModal>
    )
  }
}

該 Modal 的展示與否由父組件控制,因此會傳入?yún)?shù) visible 和 handleCloseModal(用于 Modal 關(guān)閉自己)。 實現(xiàn)在 Modal 打開的時候才進行數(shù)據(jù)獲取,我們需要同時在 componentDidMount 和 componentDidUpdate 兩個生命周期里實現(xiàn)數(shù)據(jù)獲取的邏輯

我們需要將其按照 React 組件生命周期進行拆解。這種拆解除了代碼冗余,還很難復用。

Hooks 寫法:

function RandomUserModal(props) {
  const [user, setUser] = React.useState({});
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    if (!props.visible) return;
    setLoading(true);
    fetch('https://randomuser.me/api/').then(res => res.json()).then(json => {
      setUser(json.results[0]);
      setLoading(false);
    });
  }, [props.visible]);
  
  return (
    // View 部分幾乎與上面相同
  );
}

優(yōu)勢是代碼精簡, 可以通過 自定義 的hook 將 重要的邏輯抽離出去

// 自定義 Hook
function useFetchUser(visible) {
  const [user, setUser] = React.useState({});
  const [loading, setLoading] = React.useState(false);
  
  React.useEffect(() => {
    if (!visible) return;
    setLoading(true);
    fetch('https://randomuser.me/api/').then(res => res.json()).then(json => {
      setUser(json.results[0]);
      setLoading(false);
    });
  }, [visible]);
  return { user, loading };
}

function RandomUserModal(props) {
  const { user, loading } = useFetchUser(props.visible);
  
  return (
    // 與上面相同
  );
}

useState

useState 是一個hook,它的入?yún)⑹莝tate 的初始值,返回一個數(shù)組,包含當前state 和 用于更改 state 的函數(shù)

  • class 組件有一個大的state 對象,通過this.setState 一次改變整個state對象
  • 函數(shù)組件根本沒有狀態(tài),但useState hook允許我們在需要時添加很小的狀態(tài)塊

React有能力在調(diào)用每個組件之前做一些設置,這就是它設置這個狀態(tài)的時候。
其中做的一件事設置 Hooks 數(shù)組。 它開始是空的, 每次調(diào)用一個hook時,React 都會向該數(shù)組添加該 hook。

假如有這樣一個函數(shù)

function AudioPlayer() {
  const [volume, setVolume] = useState(80);
  const [position, setPosition] = useState(0);
  const [isPlaying, setPlaying] = useState(false);

  .....
}

因為它調(diào)用useState 3次,React 會在第一次渲染時將這三個 hook 放入 Hooks 數(shù)組中。

下次渲染時,同樣的3個hooks以相同的順序被調(diào)用,所以React可以查看它的數(shù)組,并發(fā)現(xiàn)已經(jīng)在位置0有一個useState hook ,所以React不會創(chuàng)建一個新狀態(tài),而是返回現(xiàn)有狀態(tài)。

  • 1、React 創(chuàng)建組件時,它還沒有調(diào)用函數(shù)。React 創(chuàng)建元數(shù)據(jù)對象和Hooks的空數(shù)組。假設這個對象有一個名為nextHook的屬性,它被放到索引為0的位置上,運行的第一個hook將占用位置0。

  • 2、React 調(diào)用你的組件(這意味著它知道存儲hooks的元數(shù)據(jù)對象)。

  • 3、調(diào)用useState,React創(chuàng)建一個新的狀態(tài),將它放在hooks數(shù)組的第0位,返回[volume,setVolume]對,并將volume 設置為其初始值80,它還將nextHook索引遞增1。

  • 4、再次調(diào)用useState,React查看數(shù)組的第1位,看到它是空的,并創(chuàng)建一個新的狀態(tài)。 然后它將nextHook索引遞增為2,并返回 [position,setPosition]

  • 5、第三次調(diào)用useState。 React看到位置2為空,同樣創(chuàng)建新狀態(tài),將nextHook遞增到3,并返回[isPlaying,setPlaying]

useEffect

你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數(shù)的組合。

默認情況下,它在第一次渲染之后和每次更新之后都會執(zhí)行。

useEffect做了什么

引用官方文檔的例子:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

通過使用這個 Hook,你可以告訴 React 組件需要在渲染后執(zhí)行某些操作。React 會保存你傳遞的函數(shù)(我們將它稱之為 “effect”),并且在執(zhí)行 DOM 更新之后調(diào)用它。

  • 在渲染的時候被創(chuàng)建,在瀏覽器繪制之后運行。
  • 如果給出了銷毀指令,它們將在下一次繪制前被銷毀。
  • 它們會按照定義的順序被運行。

渲染函數(shù)只是創(chuàng)建了 fiber 節(jié)點,但是并沒有繪制任何內(nèi)容。

通常來說,應該是 fiber 保存包含了 effect 節(jié)點的隊列。每個 effect 節(jié)點都是一個不同的類型,并能在適當?shù)臓顟B(tài)下被定位到:

hook effect 將會被保存在 fiber 一個稱為 updateQueue 的屬性上,每個 effect 節(jié)點都有如下的結(jié)構(gòu)(詳見源碼):

type Effect = {
  tag: HookEffectTag,  // 它控制了 effect 節(jié)點的行為
  create: () => mixed,  // 繪制之后運行的回調(diào)函數(shù)
  destroy: (() => mixed) | null,
  inputs: Array<mixed>,  // 一個集合,該集合中的值將會決定一個 effect 節(jié)點是否應該被銷毀或者重新創(chuàng)建。
  next: Effect,   //它指向下一個定義在函數(shù)組件中的 effect 節(jié)點
};

export type HookEffectTag = number;

export const NoEffect = /*             */ 0b00000000;
export const UnmountSnapshot = /*      */ 0b00000010;
export const UnmountMutation = /*      */ 0b00000100;
export const MountMutation = /*        */ 0b00001000;
export const UnmountLayout = /*        */ 0b00010000;
export const MountLayout = /*          */ 0b00100000;
export const MountPassive = /*         */ 0b01000000;
export const UnmountPassive = /*       */ 0b10000000;
// 這個 tag 屬性值是由二進制的值組合而成

React 提供了一些特殊的 effect hook:比如 useMutationEffect() 和 useLayoutEffect()。這兩個 effect hook 內(nèi)部都使用了 useEffect(),實際上這就意味著它們創(chuàng)建了 effect hook,但是卻使用了不同的 tag 屬性值。

react 又是如何檢查處罰的呢?

do {
      if ((effect.tag & unmountTag) !== NoHookEffect) {
        // Unmount
        const destroy = effect.destroy;
        effect.destroy = null;
        if (destroy !== null) {
          destroy();
        }
      }
      if ((effect.tag & mountTag) !== NoHookEffect) {
        // Mount
        const create = effect.create;
        let destroy = create();
        if (typeof destroy !== 'function') {
          if (__DEV__) {
            if (destroy !== null && destroy !== undefined) {
              warningWithoutStack(
                false,
                'useEffect function must return a cleanup function or ' +
                  'nothing.%s%s',
                typeof destroy.then === 'function'
                  ? ' Promises and useEffect(async () => ...) are not ' +
                    'supported, but you can call an async function inside an ' +
                    'effect.'
                  : '',
                getStackByFiberInDevAndProd(finishedWork),
              );
            }
          }
          destroy = null;
        }
        effect.destroy = destroy;
      }
      effect = effect.next;
    }

React 組件中有兩種常見的副作用:
需要清理的副作用
不需要清理的副作用。

無需清除的effect:

只想在 React 更新 DOM 之后運行一些額外的代碼。比如發(fā)送網(wǎng)絡請求,手動變更 DOM,記錄日志,這些都是常見的無需清除的操作。

需要清除的effect:

例如訂閱外部數(shù)據(jù)源。這種情況下,清除工作是非常重要的,可以防止引起內(nèi)存泄露!

使用class

class FriendStatus extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }

  render() {
    if (this.state.isOnline === null) {
      return 'Loading...';
    }
    return this.state.isOnline ? 'Online' : 'Offline';
  }
}

你通常會在 componentDidMount 中設置訂閱,并在 componentWillUnmount 中清除它。
使用生命周期函數(shù)迫使我們拆分這些邏輯代碼,即使這兩部分代碼都作用于相同的副作用

使用 Hook 的示例:

function FriendStatus(props) {
  // ...
  useEffect(() => {
    // ...
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

這是 effect 可選的清除機制。每個 effect 都可以返回一個清除函數(shù)。如此可以將添加和移除訂閱的邏輯放在一起。它們都屬于 effect 的一部分

多個effect:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

Hook 允許我們按照代碼的用途分離他們, 而不是像生命周期函數(shù)那樣。React 將按照 effect 聲明的順序依次調(diào)用組件中的每一個 effect。
它會在調(diào)用一個新的 effect 之前對前一個 effect 進行清理


// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // 運行第一個 effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一個 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // 運行下一個 effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一個 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // 運行下一個 effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一個 effect

effect優(yōu)化

每次渲染后都執(zhí)行清理或者執(zhí)行 effect 可能會導致性能問題

useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // 僅在 props.friend.id 發(fā)生變化時,重新訂閱
只在最頂層使用 Hook

不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook, 確保總是在你的 React 函數(shù)的最頂層調(diào)用他們。遵守這條規(guī)則,你就能確保 Hook 在每一次渲染中都按照同樣的順序被調(diào)用

useContext

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

當組件上層最近的 <MyContext.Provider> 更新時,該 Hook 會觸發(fā)重渲染,并使用最新傳遞給 MyContext provider 的 context value 值

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

useReducer:

useState的替代方案。接受類型為(state,action)=> newState的reducer,并返回與dispatch方法配對的當前狀態(tài)

const [state, dispatch] = useReducer(reducer, initialArg, init);
有兩種不同初始化 useReducer state 的方式

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter({initialState}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

初始值也可以延遲初始化, useReducer(reducer, initialCount, init), init 是一個函數(shù), 初始值將設置為init(initialArg)
如果從Reducer Hook返回與當前狀態(tài)相同的值,則React將退出而不渲染子項或觸發(fā)效果。

useCallback

useCallback將返回一個回調(diào)的memoized(一種優(yōu)化手段,遇到計算開銷很大的函數(shù)時,會緩存其計算結(jié)果,下次同樣的輸入就可以直接返回緩存的結(jié)果)版本,該版本僅在其中一個依賴項發(fā)生更改時才會更改。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useCallback(fn, deps) 相當于 useMemo(() => fn, deps)。

useRef

const refContainer = useRef(initialValue);

useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。返回的 ref 對象在組件的整個生命周期內(nèi)保持不變。

當 ref 對象內(nèi)容發(fā)生變化時,useRef 并不會通知你。變更 .current 屬性不會引發(fā)組件重新渲染
然而,useRef()ref 屬性更有用。它可以很方便地保存任何可變值,其類似于在 class 中使用實例字段的方式

useRef這個hooks函數(shù),除了傳統(tǒng)的用法之外,它還可以“跨渲染周期”保存數(shù)據(jù)。

在一個組件中有什么東西可以跨渲染周期,也就是在組件被多次渲染之后依舊不變的屬性?第一個想到的應該是state。沒錯,一個組件的state可以在多次渲染之后依舊不變。但是,state的問題在于一旦修改了它就會造成組件的重新渲染。

import React, { useState, useEffect, useMemo, useRef } from 'react';

export default function App(props){
  const [count, setCount] = useState(0);

  const doubleCount = useMemo(() => {
    return 2 * count;
  }, [count]);

  const timerID = useRef();
  
  useEffect(() => {
    timerID.current = setInterval(()=>{
        setCount(count => count + 1);
    }, 1000); 
  }, []);
  
  useEffect(()=>{
      if(count > 10){
          clearInterval(timerID.current);
      }
  });
  // 用ref對象的current屬性來存儲定時器的ID
  return (
    <>
      <button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
    </>
  );
}

為什么只能在函數(shù)最外層調(diào)用 Hook,不要在循環(huán)、條件判斷或者子函數(shù)中調(diào)用?

function ExampleWithManyStates() {
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
}
// useState無論調(diào)用多少次,相互之間是獨立的

react是根據(jù)useState出現(xiàn)的順序來定的

//第一次渲染
useState(42);  //將age初始化為42
useState('banana');  //將fruit初始化為banana
useState([{ text: 'Learn Hooks' }]); //...

//第二次渲染
useState(42);  //讀取狀態(tài)變量age的值(這時候傳的參數(shù)42直接被忽略)
useState('banana');  //讀取狀態(tài)變量fruit的值(這時候傳的參數(shù)banana直接被忽略)
useState([{ text: 'Learn Hooks' }]); //...

如果放在循環(huán)或者判斷里面

let showFruit = true;
function ExampleWithManyStates() {
  const [age, setAge] = useState(42);

  if(showFruit) {
    const [fruit, setFruit] = useState('banana');
    showFruit = false;
  }

  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
}

//第一次渲染
useState(42);  //將age初始化為42
useState('banana');  //將fruit初始化為banana
useState([{ text: 'Learn Hooks' }]); //...

//第二次渲染
useState(42);  //讀取狀態(tài)變量age的值(這時候傳的參數(shù)42直接被忽略)
// useState('banana');  
useState([{ text: 'Learn Hooks' }]); //讀取到的卻是狀態(tài)變量fruit的值,導致報錯

這樣一來不能確保hooks 的執(zhí)行順序一致。

useMemo

我們都知道 react 一個組件的更新,然后下面的字組件都會更新,有一次被問到class 組件中,如果讓不需要更新的組件不更新,當時只想起來了 shouldComponentUpdate,他是在重新渲染的過程中觸發(fā)的,
PureComponent 就是自動為我們加了shouldComponentUpdate 的方法,如果組件的 props 和 state 都沒發(fā)生改變, render 方法就不會觸發(fā)

1、 上面提到了 PureComponent來優(yōu)化 class 組件,
2、 React.memo() (16.6正式發(fā)布的)用戶函數(shù)組件和PureComponent 很相似

hooks引入了useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

記住,傳入 useMemo 的函數(shù)會在渲染期間執(zhí)行 不要再這個函數(shù)執(zhí)行的內(nèi)部執(zhí)行與渲染無關(guān)的操作,諸如副作用這類的操作屬于 useEffect 的適用范疇。

不適用useMemo 之前:


import React , {useState,useMemo} from 'react';
 
function Home(){
     const [name, setName] = useState('名稱');
    const [content,setContent] = useState('內(nèi)容')

    return (
        <>
           <button onClick={() => setName(new Date().getTime())}>name</button>
           <button onClick={() => setContent(new Date().getTime())}>content</button>
            <ChildComponent name={name}>{content}</ChildComponent>
        </>
    )
}

function ChildComponent({name,children}){
    function changeName(name){
        console.log('觸發(fā)了changeName')
        return name+',改變了name'
    }
 
    const _changeName = changeName(name)
    return (
        <>
            <div>{_changeName}</div>
            <div>{children}</div>
        </>
    )
}

// 點擊修改內(nèi)容,changeName也會觸發(fā),每次都會執(zhí)行。如果我們想要name 變化的時候 changeName 才觸發(fā)。

使用useMemo 優(yōu)化:

// 父組件不變

function ChildComponent({name,children}){
    function changeName(name){
        console.log('觸發(fā)了changeName')
        return name+',改變了name'
    }
 
   // const _changeName = changeName(name)
   const _changeName = useMemo(()=>changeName(name),[name])
    return (
        <>
            <div>{_changeName}</div>
            <div>{children}</div>
        </>
    )
}
// name 變化的時候,changeName 才會觸發(fā)

useMemo僅在其依賴項數(shù)組中的元素發(fā)生更改時重新計算值(如果沒有依賴項 - 即數(shù)組為空,則會在每次調(diào)用/呈現(xiàn)時重新計算)。調(diào)用該函數(shù)不會導致重新渲染。它也在組件的渲染過程中運行,而不是之前

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容