React Hooks 整理

1.useState

  • 使用單個(gè) state 變量還是多個(gè) state 變量

    useState 的出現(xiàn)春缕,讓我們可以使用多個(gè) state 變量來保存 state纵竖,比如:

    const [left, setLeft] = useState(0);
    const [top, setTop] = useState(0);
    

    但同時(shí)吗浩,我們也可以像 Class 組件的 this.state 一樣,將所有的 state 放到一個(gè) object 中于个, 這樣只需一個(gè) state 變量即可:

     const [state, setState] = useState({
       left: 0,
       top: 0
     });
    

    如果使用單個(gè) state 變量锈津,每次更新 state 時(shí)需要合并之前的 state。因?yàn)?useState 返回的 setState 會(huì)替換原來的值隆箩。這一點(diǎn)和 Class 組件的 this.setState 不同该贾。this.setState 會(huì)把更新的字段自動(dòng)合并到 this.state 對象中。

     const handleMouseMove = (e) => {
       setState((prevState) => ({
       ...prevState,
       left: e.pageX,
       top: e.pageY,
       }))
     };
    

    使用多個(gè) state 變量可以讓 state 的粒度更細(xì)摘仅,更易于邏輯的拆分和組合靶庙。比如,我們可以將關(guān)聯(lián)的邏輯提取到自定義 Hook 中:

     function usePosition() {
       const [left, setLeft] = useState(0);
       const [top, setTop] = useState(0);
    
       useEffect(() => {
       // ...
       }, []);
    
       return [left, top, setLeft, setTop];
     }
    

    我們發(fā)現(xiàn)娃属,每次更新 left 時(shí) top 也會(huì)隨之更新六荒。因此,把 top 和 left 拆分為兩個(gè) state 變量顯得有點(diǎn)多余矾端。
    在使用 state 之前掏击,我們需要考慮狀態(tài)拆分的「粒度」問題。如果粒度過細(xì)秩铆,代碼就會(huì)變得比較冗余砚亭。如果粒度過粗,代碼的可復(fù)用性就會(huì)降低殴玛。那么捅膘,到底哪些 state 應(yīng)該合并,哪些 state 應(yīng)該拆分呢滚粟?我總結(jié)了下面兩點(diǎn):

    1.將完全不相關(guān)的 state 拆分為多組 state寻仗。比如 size 和 position。
    2.如果某些 state 是相互關(guān)聯(lián)的凡壤,或者需要一起發(fā)生改變署尤,就可以把它們合并為一組 state。 比如 left 和 top亚侠。

    function Box() {
      const [position, setPosition] = usePosition();
      const [size, setSize] = useState({width: 100, height: 100});
      // ...
    }
    
    function usePosition() {
      const [position, setPosition] = useState({left: 0, top: 0});
    
      useEffect(() => {
        // ...
      }, []);
    
      return [position, setPosition];
    }
    
  • 使用setState更新state的選擇
    傳值更新

    setState(newState);
    

    函數(shù)式更新
    如果新的 state 需要通過使用先前的 state 計(jì)算得出曹体,那么可以將函數(shù)傳遞給 setState。該函數(shù)將接收先前的 state硝烂,并返回一個(gè)更新后的值箕别。下面的計(jì)數(shù)器組件示例展示了 setState 的兩種用法:

    function Counter({initialCount}) {
      const [count, setCount] = useState(initialCount);
      return (
        <>
          Count: {count}
          <button onClick={() => setCount(initialCount)}>Reset</button>
          <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
          <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
        </>
      );
    }
    

    “+” 和 “-” 按鈕采用函數(shù)式形式,因?yàn)楸桓碌?state 需要基于之前的 state。但是“重置”按鈕則采用普通形式究孕,因?yàn)樗偸前?count 設(shè)置回初始值啥酱。
    如果你的更新函數(shù)返回值與當(dāng)前 state 完全相同,則隨后的重渲染會(huì)被完全跳過厨诸。

    除此之外,我們還可以在其他地方活用函數(shù)式更新禾酱。
    有時(shí)候微酬,你的 effect 可能會(huì)使用一些頻繁變化的值。你可能會(huì)忽略依賴列表中 state颤陶,但這通常會(huì)引起 Bug:

     function Counter() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
           const id = setInterval(() => {
           setCount(count + 1); // 這個(gè) effect 依賴于 `count` state
           }, 1000);
           return () => clearInterval(id);
        }, []); // ?? Bug: `count` 沒有被指定為依賴
    
        return <h1>{count}</h1>;
     }
    

    傳入空的依賴數(shù)組 []颗管,意味著該 hook 只在組件掛載時(shí)運(yùn)行一次,并非重新渲染時(shí)滓走。但如此會(huì)有問題垦江,在 setInterval 的回調(diào)中,count 的值不會(huì)發(fā)生變化搅方。因?yàn)楫?dāng) effect 執(zhí)行時(shí)比吭,我們會(huì)創(chuàng)建一個(gè)閉包,并將 count 的值被保存在該閉包當(dāng)中姨涡,且初值為 0衩藤。每隔一秒,回調(diào)就會(huì)執(zhí)行 setCount(0 + 1)涛漂,因此赏表,count 永遠(yuǎn)不會(huì)超過 1。
    指定 [count] 作為依賴列表就能修復(fù)這個(gè) Bug匈仗,但會(huì)導(dǎo)致每次改變發(fā)生時(shí)定時(shí)器都被重置瓢剿。事實(shí)上,每個(gè) setInterval 在被清除前(類似于 setTimeout)都會(huì)調(diào)用一次悠轩。但這并不是我們想要的间狂。要解決這個(gè)問題,我們可以使用 setState 的函數(shù)式更新形式哗蜈。它允許我們指定 state 該 如何 改變而不用引用 當(dāng)前 state:

    function Counter() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        const id = setInterval(() => {
        setCount(c => c + 1); // ? 在這不依賴于外部的 `count` 變量
        }, 1000);
         return () => clearInterval(id);
      }, []); // ? 我們的 effect 不適用組件作用域中的任何變量
    
       return <h1>{count}</h1>;
    }
    

    此時(shí)前标,setInterval 的回調(diào)依舊每秒調(diào)用一次,但每次 setCount 內(nèi)部的回調(diào)取到的 count 最新值(在回調(diào)中變量命名為 c)距潘。

2.useEffect

  • effect清除

    通常炼列,組件卸載時(shí)需要清除 effect 創(chuàng)建的諸如訂閱或計(jì)時(shí)器 ID 等資源。要實(shí)現(xiàn)這一點(diǎn)音比,useEffect 函數(shù)需返回一個(gè)清除函數(shù)俭尖。為防止內(nèi)存泄漏,清除函數(shù)會(huì)在組件卸載前執(zhí)行。

    1. 清除訂閱
    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
      // 清除訂閱
      subscription.unsubscribe();
     };
    });
    
    1. 避免組件unmount后的state update
     useEffect(() => {
      let ignore = false;
      async function fetchProduct() {
       const response = await fetch('http://myapi/product/' + productId);
       const json = await response.json();
       if (!ignore) setProduct(json);
      }
    
     fetchProduct();
     return () => { ignore = true };
    }, [productId]);
    
  • 使用useEffect deps過多

    使用 useEffect hook 時(shí)稽犁,為了避免每次 render 都去執(zhí)行它的 callback焰望,我們通常會(huì)傳入第二個(gè)參數(shù)「dependency array」(下面統(tǒng)稱為依賴數(shù)組)。這樣已亥,只有當(dāng)依賴數(shù)組發(fā)生變化時(shí)熊赖,才會(huì)執(zhí)行 useEffect 的回調(diào)函數(shù)。

    function Example({id, name}) {
      useEffect(() => {
        // 由于依賴數(shù)組中不包含 name虑椎,所以當(dāng) name 發(fā)生變化時(shí)震鹉,無法打印日志
        console.log(id, name); 
      }, [id]);
    }
    

    在 React 中,除了 useEffect 外捆姜,接收依賴數(shù)組作為參數(shù)的 Hook 還有 useMemo传趾、 useCallback 和 useImperativeHandle。我們剛剛也提到了泥技,依賴數(shù)組中千萬不要遺漏回調(diào)函數(shù)內(nèi)部依賴的值浆兰。但是,如果依賴數(shù)組依賴了過多東西珊豹,可能導(dǎo)致代碼難以維護(hù).

    const refresh = useCallback(() => {
      // ...
    }, [name, searchState, address, status, personA, personB, progress, page, size]);
    

    不要說內(nèi)部邏輯了簸呈,光是看到這一堆依賴就令人頭大!如果項(xiàng)目中到處都是這樣的代碼平夜,可想而知維護(hù)起來多么痛苦蝶棋。如何才能避免寫出這樣的代碼呢?

    首先忽妒,你需要重新思考一下玩裙,這些 deps 是否真的都需要?看下面這個(gè)例子:

     function Example({id}) {
       const requestParams = useRef({});
    
       useEffect(() => {
         requestParams.current = {page: 1, size: 20, id};
       });
    
       const refresh = useCallback(() => {
         doRefresh(requestParams.current);
       }, []);
    
    
       useEffect(() => {
         id && refresh();
       }, [id, refresh]); // 思考這里的 deps list 是否合理段直?
    }
    

    雖然 useEffect 的回調(diào)函數(shù)依賴了 id 和 refresh 方法吃溅,但是觀察 refresh 方法可以發(fā)現(xiàn),它在首次 render 被創(chuàng)建之后鸯檬,永遠(yuǎn)不會(huì)發(fā)生改變了决侈。因此,把它作為 useEffect 的 deps 是多余的喧务。

    其次赖歌,如果這些依賴真的都是需要的,那么這些邏輯是否應(yīng)該放到同一個(gè) hook 中功茴?

    function Example({id, name, address, status, personA, personB, progress}) {
      const [page, setPage] = useState();
      const [size, setSize] = useState();
    
      const doSearch = useCallback(() => {
       // ...
      }, []);
    
      const doRefresh = useCallback(() => {
       // ...
      }, []);
    
    
      useEffect(() => {
       id && doSearch({name, address, status, personA, personB, progress});
       page && doRefresh({name, page, size});
     }, [id, name, address, status, personA, personB, progress, page, size]);
    }
    

    可以看出庐冯,在 useEffect 中有兩段邏輯,這兩段邏輯是相互獨(dú)立的坎穿,因此我們可以將這兩段邏輯放到不同 useEffect 中:

    useEffect(() => {
     id && doSearch({name, address, status, personA, personB, progress});
    }, [id, name, address, status, personA, personB, progress]);
    
    useEffect(() => {
     page && doRefresh({name, page, size});
    }, [name,  page, size]);
    

    如果邏輯無法繼續(xù)拆分展父,但是依賴數(shù)組還是依賴了過多東西返劲,該怎么辦呢?就比如我們上面的代碼:

    useEffect(() => {
     id && doSearch({name, address, status, personA, personB, progress});
    }, [id, name, address, status, personA, personB, progress]);
    

    這段代碼中的 useEffect 依賴了七個(gè)值栖茉,還是偏多了篮绿。仔細(xì)觀察上面的代碼,可以發(fā)現(xiàn)這些值都是「過濾條件」的一部分吕漂,通過這些條件可以過濾頁面上的數(shù)據(jù)亲配。因此,我們可以將它們看做一個(gè)整體痰娱,也就是我們前面講過的合并 state:

     const [filters, setFilters] = useState({
       name: "",
       address: "",
       status: "",
       personA: "",
       personB: "",
      progress: ""
     });
    
    useEffect(() => {
      id && doSearch(filters);
    }, [id, filters]);
    

    如果 state 不能合并弃榨,在 callback 內(nèi)部又使用了 setState 方法,那么可以考慮使用 setState callback 來減少一些依賴梨睁。比如:

     const useValues = () => {
      const [values, setValues] = useState({
        data: {},
        count: 0
      });
    
      const [updateData] = useCallback(
        (nextData) => {
         setValues({
           data: nextData,
           count: values.count + 1 // 因?yàn)?callback 內(nèi)部依賴了外部的 values 變量,所以必須在依賴數(shù)組中指定它
         });
       },
       [values], 
       );
    
       return [values, updateData];
    };
    

    上面的代碼中娜饵,我們必須在 useCallback 的依賴數(shù)組中指定 values坡贺,否則我們無法在 callback 中獲取到最新的 values 狀態(tài)。但是箱舞,通過 setState 回調(diào)函數(shù)遍坟,我們不用再依賴外部的 values 變量,因此也無需在依賴數(shù)組中指定它晴股。就像下面這樣:

     const useValues = () => {
      const [values, setValues] = useState({});
    
      const [updateData] = useCallback((nextData) => {
        setValues((prevValues) => ({
        data: nextData,
        count: prevValues.count + 1, // 通過 setState 回調(diào)函數(shù)獲取最新的 values 狀態(tài)愿伴,這時(shí) callback 不再依賴于外部的 values 變量了,因此依賴數(shù)組中不需要指定任何值
       }));
      }, []); // 這個(gè) callback 永遠(yuǎn)不會(huì)重新創(chuàng)建
    
      return [values, updateData];
    };
    

    說了這么多电湘,歸根到底都是為了寫出更加清晰隔节、易于維護(hù)的代碼。如果發(fā)現(xiàn)依賴數(shù)組依賴過多寂呛,我們就需要重新審視自己的代碼怎诫。

    1.依賴數(shù)組依賴的值最好不要超過 3 個(gè),否則會(huì)導(dǎo)致代碼會(huì)難以維護(hù)贷痪。
    2.如果發(fā)現(xiàn)依賴數(shù)組依賴的值過多幻妓,我們應(yīng)該采取一些方法來減少它。
    3.去掉不必要的依賴劫拢。
    4.將 Hook 拆分為更小的單元肉津,每個(gè) Hook 依賴于各自的依賴數(shù)組。
    5.通過合并相關(guān)的 state舱沧,將多個(gè)依賴值聚合為一個(gè)妹沙。
    6.通過 setState 回調(diào)函數(shù)獲取最新的 state,以減少外部依賴狗唉。

  • useMemo

    該不該使用 useMemo初烘?對于這個(gè)問題,有的人從來沒有思考過,有的人甚至不覺得這是個(gè)問題肾筐。不管什么情況哆料,只要用 useMemo 或者 useCallback 「包裹一下」,似乎就能使應(yīng)用遠(yuǎn)離性能的問題吗铐。但真的是這樣嗎东亦?有的時(shí)候 useMemo 沒有任何作用,甚至還會(huì)影響應(yīng)用的性能唬渗。

    為什么這么說呢典阵?首先,我們需要知道 useMemo本身也有開銷镊逝。useMemo 會(huì)「記住」一些值壮啊,同時(shí)在后續(xù) render 時(shí),將依賴數(shù)組中的值取出來和上一次記錄的值進(jìn)行比較撑蒜,如果不相等才會(huì)重新執(zhí)行回調(diào)函數(shù)歹啼,否則直接返回「記住」的值。這個(gè)過程本身就會(huì)消耗一定的內(nèi)存和計(jì)算資源座菠。因此狸眼,過度使用 useMemo 可能會(huì)影響程序的性能。

    要想合理使用 useMemo浴滴,我們需要搞清楚 useMemo 適用的場景:

    • 有些計(jì)算開銷很大拓萌,我們就需要「記住」它的返回值,避免每次 render 都去重新計(jì)算升略。
    • 由于值的引用發(fā)生變化微王,導(dǎo)致下游組件重新渲染,我們也需要「記住」這個(gè)值降宅。

    讓我們來看個(gè)例子:

     interface IExampleProps {
       page: number;
       type: string;
     }
    
    const Example = ({page, type}: IExampleProps) => {
       const resolvedValue = useMemo(() => {
         return getResolvedValue(page, type);
       }, [page, type]);
    
       return <ExpensiveComponent resolvedValue={resolvedValue}/>;
    };
    

    在上面的例子中骂远,渲染 ExpensiveComponent 的開銷很大。所以腰根,當(dāng) resolvedValue 的引用發(fā)生變化時(shí)激才,作者不想重新渲染這個(gè)組件。因此额嘿,作者使用了 useMemo瘸恼,避免每次 render 重新計(jì)算 resolvedValue,導(dǎo)致它的引用發(fā)生改變册养,從而使下游組件 re-render东帅。

    這個(gè)擔(dān)憂是正確的,但是使用 useMemo 之前球拦,我們應(yīng)該先思考兩個(gè)問題:

    1.傳遞給 useMemo 的函數(shù)開銷大不大靠闭?在上面的例子中帐我,就是考慮 getResolvedValue 函數(shù)的開銷大不大。JS 中大多數(shù)方法都是優(yōu)化過的愧膀,比如 Array.map拦键、Array.forEach 等。如果你執(zhí)行的操作開銷不大檩淋,那么就不需要記住返回值芬为。否則,使用 useMemo 本身的開銷就可能超過重新計(jì)算這個(gè)值的開銷蟀悦。因此媚朦,對于一些簡單的 JS 運(yùn)算來說,我們不需要使用 useMemo 來「記住」它的返回值日戈。

    2.當(dāng)輸入相同時(shí)询张,「記憶」值的引用是否會(huì)發(fā)生改變?在上面的例子中浙炼,就是當(dāng) page 和 type 相同時(shí)瑞侮,resolvedValue 的引用是否會(huì)發(fā)生改變?這里我們就需要考慮 resolvedValue 的類型了鼓拧。如果 resolvedValue 是一個(gè)對象,由于我們項(xiàng)目上使用「函數(shù)式編程」越妈,每次函數(shù)調(diào)用都會(huì)產(chǎn)生一個(gè)新的引用季俩。但是,如果 resolvedValue 是一個(gè)原始值(string, boolean, null, undefined, number, symbol)梅掠,也就不存在「引用」的概念了酌住,每次計(jì)算出來的這個(gè)值一定是相等的。也就是說阎抒,ExpensiveComponent 組件不會(huì)被重新渲染酪我。

    因此,如果 getResolvedValue 的開銷不大且叁,并且 resolvedValue 返回一個(gè)字符串之類的原始值都哭,那我們完全可以去掉 useMemo,就像下面這樣:

    interface IExampleProps {
     page: number;
     type: string;
    }
    
    const Example = ({page, type}: IExampleProps) => {
     const resolvedValue = getResolvedValue(page, type);
     return <ExpensiveComponent resolvedValue={resolvedValue}/>;
    };
    

    保持引用不變

     // 使用 useMemo
    function Example() {
      const users = useMemo(() => [1, 2, 3], []);
    
      return <ExpensiveComponent users={users} />
    }
    

    在上面的例子中逞带,我們用 useMemo 來「記住」users 數(shù)組欺矫,不是因?yàn)閿?shù)組本身的開銷大,而是因?yàn)?users 的引用在每次 render 時(shí)都會(huì)發(fā)生改變展氓,從而導(dǎo)致子組件 ExpensiveComponent 重新渲染(可能會(huì)帶來較大開銷)穆趴。

    在編寫自定義 Hook 時(shí),返回值一定要保持引用的一致性遇汞。因?yàn)槟銦o法確定外部要如何使用它的返回值未妹。如果返回值被用做其他 Hook 的依賴簿废,并且每次 re-render 時(shí)引用不一致(當(dāng)值相等的情況),就可能會(huì)產(chǎn)生 bug络它。比如:

    function Example() {
     const data = useData();
     const [dataChanged, setDataChanged] = useState(false);
    
     useEffect(() => {
       setDataChanged((prevDataChanged) => !prevDataChanged); // 當(dāng) data 發(fā)生變化時(shí)族檬,調(diào)用 setState。如果 data 值相同而引用不同酪耕,就可能會(huì)產(chǎn)生非預(yù)期的結(jié)果导梆。
     }, [data]);
    
     console.log(dataChanged);
    
     return <ExpensiveComponent data={data} />;
    }
    
    const useData = () => {
    // 獲取異步數(shù)據(jù)
    const resp = getAsyncData([]);
    
    // 處理獲取到的異步數(shù)據(jù),這里使用了 Array.map迂烁。因此看尼,即使 data 相同,每次調(diào)用得到的引用也是不同的盟步。
    const mapper = (data) => data.map((item) => ({...item, selected: false}));
    
    return resp ? mapper(resp) : resp;
    };
    

    在上面的例子中藏斩,我們通過 useData Hook 獲取了 data。每次 render 時(shí) data 的值沒有發(fā)生變化却盘,但是引用卻不一致狰域。如果把 data 用到 useEffect 的依賴數(shù)組中,就可能產(chǎn)生非預(yù)期的結(jié)果黄橘。另外兆览,由于引用的不同,也會(huì)導(dǎo)致 ExpensiveComponent 組件 re-render塞关,產(chǎn)生性能問題抬探。

    因此,在使用 useMemo 之前帆赢,我們不妨先問自己幾個(gè)問題:

    1.要記住的函數(shù)開銷很大嗎小压?
    2.返回的值是原始值嗎?
    3.記憶的值會(huì)被其他 Hook 或者子組件用到嗎椰于?

    一怠益、應(yīng)該使用 useMemo 的場景

    1.保持引用相等

    • 對于組件內(nèi)部用到的 object、array瘾婿、函數(shù)等蜻牢,如果用在了其他 Hook 的依賴數(shù)組中,或者作為 props 傳遞給了下游組件憋他,應(yīng)該使用 useMemo孩饼。
    • 自定義 Hook 中暴露出來的 object、array竹挡、函數(shù)等镀娶,都應(yīng)該使用 useMemo 。以確保當(dāng)值相同時(shí)揪罕,引用不發(fā)生變化梯码。
    • 使用 Context 時(shí)宝泵,如果 Provider 的 value 中定義的值(第一層)發(fā)生了變化,即便用了 Pure Component 或者 React.memo轩娶,仍然會(huì)導(dǎo)致子組件 re-render儿奶。這種情況下,仍然建議使用 useMemo 保持引用的一致性鳄抒。
    1. 成本很高的計(jì)算

    二闯捎、無需使用 useMemo 的場景

    • 如果返回的值是原始值: string, boolean, null, undefined, number, symbol(不包括動(dòng)態(tài)聲明的 Symbol),一般不需要使用 useMemo许溅。
    • 僅在組件內(nèi)部用到的 object瓤鼻、array、函數(shù)等(沒有作為 props 傳遞給子組件)贤重,且沒有用到其他 Hook 的依賴數(shù)組中茬祷,一般不需要使用 useMemo。
  • useRef

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

    function TextInputWithFocusButton() {
     const inputEl = useRef(null);
     const onButtonClick = () => {
     // `current` 指向已掛載到 DOM 上的文本輸入元素
     inputEl.current.focus();
     };
     return (
       <>
        <input ref={inputEl} type="text" />
        <button onClick={onButtonClick}>Focus the input</button>
       </>
     );
    }
    

    除此之外滚停,useRef()ref 屬性更有用沃粗。它可以很方便地保存任何可變值,其類似于在 class 中使用實(shí)例字段的方式键畴。
    這是因?yàn)樗鼊?chuàng)建的是一個(gè)普通 Javascript 對象陪每。而 useRef() 和自建一個(gè) {current: ...} 對象的唯一區(qū)別是,useRef 會(huì)在每次渲染時(shí)返回同一個(gè) ref 對象镰吵。

    1. 使用useRef保證引用不變
      // 使用 useRef
    function Example() {
      const {current: users} = useRef([1, 2, 3]);
    
      return <ExpensiveComponent users={users} />
    }
    
    1. 使用ref保存可變變量
      實(shí)現(xiàn)獲取上一輪的 props 或 state
    function Counter() {
      const [count, setCount] = useState(0);
      const prevCount = usePrevious(count);
      return <h1>Now: {count}, before: {prevCount}</h1>;
    }
    
    function usePrevious(value) {
      const ref = useRef();
      useEffect(() => {
        ref.current = value;
      });
      return ref.current;
    }
    

    實(shí)現(xiàn)effect deps減少

    function Example(props) {
      // 把最新的 props 保存在一個(gè) ref 中
      const latestProps = useRef(props);
      useEffect(() => {
        latestProps.current = props;
      });
    
      useEffect(() => {
        function tick() {
        // 在任何時(shí)候讀取最新的 props
        console.log(latestProps.current);
      }
    
       const id = setInterval(tick, 1000);
       return () => clearInterval(id);
      }, []); // 這個(gè) effect 從不會(huì)重新執(zhí)行
    }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挂签,隨后出現(xiàn)的幾起案子疤祭,更是在濱河造成了極大的恐慌,老刑警劉巖饵婆,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勺馆,死亡現(xiàn)場離奇詭異,居然都是意外死亡侨核,警方通過查閱死者的電腦和手機(jī)草穆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搓译,“玉大人悲柱,你說我怎么就攤上這事⌒┘海” “怎么了豌鸡?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵嘿般,是天一觀的道長。 經(jīng)常有香客問我涯冠,道長炉奴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任蛇更,我火速辦了婚禮瞻赶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘派任。我一直安慰自己砸逊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布吨瞎。 她就那樣靜靜地躺著痹兜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颤诀。 梳的紋絲不亂的頭發(fā)上字旭,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機(jī)與錄音崖叫,去河邊找鬼遗淳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛心傀,可吹牛的內(nèi)容都是我干的屈暗。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼脂男,長吁一口氣:“原來是場噩夢啊……” “哼养叛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宰翅,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤弃甥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汁讼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淆攻,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年嘿架,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓶珊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耸彪,死狀恐怖伞芹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝉娜,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站榛斯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏稽屏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一西乖、第九天 我趴在偏房一處隱蔽的房頂上張望狐榔。 院中可真熱鬧,春花似錦获雕、人聲如沸薄腻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庵楷。三九已至,卻和暖如春楣颠,著一層夾襖步出監(jiān)牢的瞬間尽纽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工童漩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弄贿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓矫膨,卻偏偏與公主長得像差凹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子侧馅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評論 2 361