fish_redux 入門:助你快速上手 fish_redux 狀態(tài)管理功能

前言

如果過于依賴思維慣性與經(jīng)驗主義同欠,會在學(xué)習(xí)與接觸新事物時造成很大的阻礙遏插。它可能會蒙蔽我們的認(rèn)知缸榄、減緩我們消化吸收的速度忱反,更有甚者會憑白產(chǎn)生很多根本不存在的對立矛盾來消耗我們的精力泛释,使得我們愈加抵觸新事物。
所以温算,放下過去怜校,以空杯心態(tài)去學(xué)習(xí)、認(rèn)知新事物注竿,才能更客觀全面的了解消化茄茁。

fish_redux的相關(guān)鏈接:

Fish Redux

本文適合那些對 Flutter 知識體系有初步了解的朋友裙顽,例如

  • Flutter 中 State 是什么? StatelessWidget 與 StatefulWidget 之間的區(qū)別是啥宣谈?
  • Flutter 的 Navigator 如何進(jìn)行頁面跳轉(zhuǎn)愈犹?Flutter 略微復(fù)雜的頁面開發(fā),包含 UI 更新與數(shù)據(jù)處理等闻丑。
  • 能流程使用 Dart 進(jìn)行開發(fā)漩怎,了解 Dart 的異步 Future API。

本文作為 fish_redux 入門文章梆掸,并未涉及到 fish_redux 的高級用法扬卷。但是它能幫你對 fish_redux 的狀態(tài)管理、事件分發(fā)等有個初步了解酸钦,并了解因何而用怪得,如何用。

1卑硫、fish_redux

為什么要用 fish_redux ?
一個最簡單的使用場景徒恋,你在一個 State 中使用耗時操作,例如網(wǎng)絡(luò)交互欢伏、數(shù)據(jù)庫查詢等入挣,在 then((){}) 處理回調(diào)并調(diào)用 setState((){}}) 更新UI,但是在回調(diào)時頁面處于 deactivate 或者 dispose 狀態(tài)硝拧,結(jié)果你的 Flutter 項目報錯了径筏,提示如下:

Unhandled Exception: setState() called after dispose(): _MockLeakedState#24ffc(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().

引用泄漏或錯誤狀態(tài)回調(diào)的解決辦法有很多葛假,例如在回調(diào)中判斷 State 狀態(tài)(State 中的 UI 與 邏輯代碼耦合導(dǎo)致,仍然泄露)滋恬、監(jiān)聽生命周期及時釋放回調(diào)等聊训。專業(yè)方案有諸如 Provider 、 scoped_model 恢氯、 Bloc 等带斑,而 fish_redux 則是最為出色的解決方案之一。在這里順便推薦一下 flutter_boost 混合開發(fā)管理框架勋拟,阿里比比比Q摹!敢靡!
fish_redux 功能雖然強(qiáng)大挂滓,但其 API 與設(shè)計思路較為復(fù)雜,官方介紹說其延續(xù)了前端 Redux 框架的思想啸胧。對于很多不熟悉 Redux 的朋友來說杂彭,還需要去了解 Redux ,但是框架這種東西沒實際用過是很難了解的(需要一定的代碼量)吓揪。為了幫助不熟悉 fish_redux 的朋友快速上手,于是就有了這篇文章所计,只要具有前言中提到的對 Flutter 知識體系有一定了解的朋友應(yīng)該都可以快速上手 fish_redux柠辞。

2、<a id="mockLeaked">Mock 泄漏</a>

之后的小節(jié)主胧,我會在每處都打個 tag 叭首,在操作處與結(jié)尾處都會備注 tag 名稱。本小節(jié)的 tag 為 mockLeaked 踪栋。

  1. 使用 Flutter 命令創(chuàng)建一個 Application 項目焙格,在 yaml 中依賴fish_redux。
    創(chuàng)建 fish_
    $ flutter create fish_redux_demo
    
    建議使用 IDEA 打開項目夷都,需要裝有 Flutter DartFishReduxTemplate 這三個插件眷唉。編輯 pubspec.yaml 文件:
    # 建議 sdk 版本 2.6.0 及其以上《诠伲可以使用 擴(kuò)展函數(shù)冬阳,真香。
    environment:
      sdk: ">=2.6.0 <3.0.0"
    
    dependencies:
      flutter:
        sdk: flutter
    
      # The following adds the Cupertino Icons font to your application.
      # Use with the CupertinoIcons class for iOS style icons.
      cupertino_icons: ^0.1.2
      # 依賴 fish_redx
      fish_redux: ^0.3.3
    
    在 terminal 中輸入 flutter pub get 更新項目配置党饮。
  2. 編輯頁面肝陪,模擬在銷毀時遇到的泄漏問題。
    ///--------------------main.dart 代碼------------------------------
    import 'package:fish_redux_demo/page/leaked_demo.dart';
    import 'package:flutter/material.dart';
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'FishReduxDemo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'FishReduxDemo'),
          routes: {
            '/page/mockLeakedPage':(_)=>MockLeakedPage()
          },
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      
      void _incrementCounter() {
        Navigator.pushNamed(context, '/page/mockLeakedPage');
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  '點擊跳轉(zhuǎn)mockLeakedPage',
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'GoToMockLeakedPage',
            child: Icon(Icons.more_horiz),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    ///--------------------main.dart 代碼------------------------------
    ///--------------------main.dart 代碼------------------------------
    ///--------------------mock_leaked_demo.dart 代碼------------------------------
    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    
    class MockLeakedPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _MockLeakedState();
    }
    
    class _MockLeakedState extends State<MockLeakedPage> {
      String _content = "MockLeakedPage";
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(
              '模擬泄漏',
              style: TextStyle(fontSize: 18),
            ),
            centerTitle: true,
          ),
          body: Container(
            alignment: Alignment(0, 0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Text(
                  _content,
                  style: TextStyle(fontSize: 16),
                ),
                RaisedButton(
                  child: Text('調(diào)用異步函數(shù)'),
                  onPressed: () {
                    Timer(Duration(seconds: 3), () {
                      _content = '3秒延時已到';
                      setState(() {});
                    });
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    ///--------------------mock_leaked_demo.dart 代碼------------------------------
    
    

運行 App刑顺,點擊跳到模擬泄漏頁面氯窍,點擊 調(diào)用異步函數(shù) 按鈕立馬退出頁面饲常,就可以在logcat看到該異常

Unhandled Exception: setState() called after dispose(): _MockLeakedState#24ffc(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().

3、<a id="fixLeaked">使用 fish_redux 解決泄漏</a>

本小節(jié) tag 是 fixLeaked 狼讨。
對于不熟悉 fish_redux 的朋友贝淤,強(qiáng)烈推薦安裝 FishReduxTemplate 插件,用于生成 fish_redux 的相關(guān) API 文件熊楼。

  1. 安裝好 FishReduxTemplate 插件之后霹娄,創(chuàng)建一個文件夾 fixleaked ,右鍵該文件夾選擇 New -> FishReduxTemplate 創(chuàng)建模板文件鲫骗,選擇 Page 類型犬耻,輸入名稱 FixLeaked 。生成文件如下执泰。生成的 dart 文件名稱是固定的枕磁,但是類名根據(jù)輸入的名稱變化,所以命名請盡量做到見名知意术吝。
    創(chuàng)建fish_redux模板之一

    創(chuàng)建fish_redux模板之二

    創(chuàng)建fish_redux模板之三
  2. 這里暫不介紹這六個文件的作用计济,直接擼代碼。關(guān)注數(shù)據(jù)源 排苍,編輯 state.dart 文件沦寂,F(xiàn)ishRedux 要求提供一個 State 類,該類我們可以理解為 MVC 淘衙、 MVP 或 MVVM 中的 Model 传藏,它的作用就是承載數(shù)據(jù)。我們在 FixLeakedState 類中創(chuàng)建一個公開的成員變量 content 彤守,需要注意在 clone 函數(shù)中拷貝 FixLeakedState 的成員屬性值毯侦。該類的 initState(Map<String, dynamic> args) 頂級函數(shù),根據(jù)傳遞的參數(shù)創(chuàng)建 FixLeakedState 初始對象來決定頁面的初始狀態(tài)具垫。
    import 'package:fish_redux/fish_redux.dart';
    
    class FixLeakedState implements Cloneable<FixLeakedState> {
      String content;
    
      @override
      FixLeakedState clone() {
        //級聯(lián)語法給 clone 對象賦值
        return FixLeakedState()..content = this.content;
      }
    }
    
    FixLeakedState initState(Map<String, dynamic> args) {
      return FixLeakedState()..content = "MockLeakedPage";
    }
    
  3. 繪制 UI侈离,修改 view.dart 文件 ,該文件用于創(chuàng)建 Widget 對象筝蚕,提供一個頂級函數(shù) Widget buildView(FixLeakedState state, Dispatch dispatch, ViewService viewService)卦碾。參數(shù)列表:
    • FixLeakedStateFishRedux 在合適的時機(jī)會重新調(diào)用 buildView 函數(shù),我們根據(jù) state 的狀態(tài)去構(gòu)造不同 UI 效果即可饰及。至于合適的時機(jī)是啥時候蔗坯,后文再說。
    • Dispatch一個派發(fā)函數(shù)對象燎含,調(diào)用該對象宾濒,我們可以分發(fā)出不同的事件出去,F(xiàn)ishRedux 會在 effect 或者 reducer 中注冊監(jiān)聽事件屏箍。也就是說我們想把事件從 view 中派發(fā)出去绘梦,使用 Dispatch 對象就好啦橘忱。事件的 API 定義在 action 文件中。
    import 'package:fish_redux/fish_redux.dart';
    
    class FixLeakedState implements Cloneable<FixLeakedState> {
      String content;
    
      @override
      FixLeakedState clone() {
        return FixLeakedState()..content = this.content;
      }
    }
    
    FixLeakedState initState(Map<String, dynamic> args) {
      return FixLeakedState()..content = "MockLeakedPage";
    }
    
    • ViewService:帶有 BuildContext 上下文對象的對象卸奉。
    • 從該文件:我們就可知钝诚,buildView(FixLeakedState, Dispatch, ViewService) 函數(shù)通過 state 對象來決定 UI 展示效果,而 Dispatch 對象用于幫助在頂級函數(shù)派發(fā)事件(Action)榄棵,ViewService 提供了我們需要用到的 BuildContext 上下文對象凝颇。
    import 'package:fish_redux/fish_redux.dart';
    import 'package:flutter/material.dart' hide Action;
    import 'action.dart';
    import 'state.dart';
    
    Widget buildView(FixLeakedState state, Dispatch dispatch, ViewService viewService) {
      return Scaffold(
        appBar: AppBar(
          title: Text(
            '模擬泄漏',
            style: TextStyle(fontSize: 18),
          ),
          centerTitle: true,
        ),
        body: Container(
          alignment: Alignment(0, 0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                state.content,
                style: TextStyle(fontSize: 16),
              ),
              RaisedButton(
                child: Text('調(diào)用異步函數(shù)'),
                onPressed: () {
                  //dispatch something
                },
              )
            ],
          ),
        ),
      );
    }
    
  4. buildView 我們已經(jīng)知道通過 Dispatch來分發(fā)事件了,那么事件如何定義與創(chuàng)建呢疹鳄?答案在 action.dart 中拧略。 action.dart 文件定義有兩個類,一個枚舉類和一個構(gòu)造器類瘪弓。
    import 'package:fish_redux/fish_redux.dart';
    
    enum FixLeakedAction { action }
    
    class FixLeakedActionCreator {
      static Action onAction() {
        return const Action(FixLeakedAction.action);
      }
    }
    
    需要什么事件垫蛆,我們可以定義在枚舉類中,構(gòu)造器類可以傳入 dynamic payload 負(fù)載來生成對應(yīng)的枚舉對象腺怯。
    ///介紹一下 FishRedux 定義的 Action 類袱饭,該類看看就行了
    ///需要一個 type 來區(qū)分事件,
    ///通過 dynamic 類型的 payload 來傳遞數(shù)據(jù)呛占。
    class Action {
      const Action(this.type, {this.payload});
      final Object type;
      final dynamic payload;
    }
    ///實際上 action.dart 編輯后的內(nèi)容虑乖,
    ///刪除默認(rèn)生成的 action 定義之后,注意 effect.dart 與 reduce.dart 使用到了默認(rèn) action
    ///注意刪除掉它晾虑。
    import 'package:fish_redux/fish_redux.dart';
    
    enum FixLeakedAction {
      delay,
      modifyContent,
    }
    
    class FixLeakedActionCreator {
    
      ///創(chuàng)建 delay action决左,模擬耗時任務(wù)
      static Action delay() {
        return const Action(FixLeakedAction.delay);
      }
    
      ///創(chuàng)建修改 content 的 action
      static Action modifyContent(String content) {
        return Action(FixLeakedAction.delay, payload: content);
      }
    }
    
  5. 回到 buildView 方法中,通過按鈕 RaisedButton 將模擬延時的事件通過 Dispatch 對象派發(fā)出去走贪。
    import 'package:fish_redux/fish_redux.dart';
    import 'package:flutter/material.dart' hide Action;
    
    import 'action.dart';
    import 'state.dart';
    
    Widget buildView(FixLeakedState state, Dispatch dispatch, ViewService viewService) {
      return Scaffold(
        appBar: AppBar(
          title: Text(
            '模擬泄漏',
            style: TextStyle(fontSize: 18),
          ),
          centerTitle: true,
        ),
        body: Container(
          alignment: Alignment(0, 0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text(
                state.content,
                style: TextStyle(fontSize: 16),
              ),
              RaisedButton(
                child: Text('調(diào)用異步函數(shù)'),
                onPressed: () {
                  dispatch(FixLeakedActionCreator.delay());
                },
              )
            ],
          ),
        ),
      );
    }
    
  6. 事件分發(fā)之后,在 effect.dartreducer.dart 來處理事件惑芭。 那么 effectreducer 有啥區(qū)別勒坠狡?
    • effect:直譯成 n.影響,作用 vt. 產(chǎn)生遂跟;達(dá)到目的 等逃沿,在此我覺得 翻譯為動詞產(chǎn)生更符合其定義FishRedux 將 effect 設(shè)計為一個 UI 無關(guān)的任務(wù)觸發(fā)器幻锁,我們可以通過 Action 與其 payload 來進(jìn)行一些與 State 凯亮、buildView 等均毫無關(guān)系的工作任務(wù),例如網(wǎng)絡(luò)交互哄尔、數(shù)據(jù)庫讀寫假消、IO操作等等。
    • reducer:直譯成 n. [助劑] 減速器; 縮減者岭接,減壓器富拗,還原劑; 臼予,相信很多朋友剛開始看到這個文件肯定是一臉蒙蔽
      問號臉

      我覺得,在直譯這方面啃沪,也許只有還原劑能搭上邊吧粘拾。reducer 接受 Action 的事件之后,會更改 State 對象的狀態(tài)创千,而 State 對象的狀態(tài)變化之后缰雇,F(xiàn)ishRedux 會觸發(fā) buildView 函數(shù),重新構(gòu)建 UI 追驴。UI 構(gòu)建時會對比 Widget 對象械哟,所以如果你在這里遇到 UI 沒有刷新最好看看生成的新舊 Widget 對象比對結(jié)果如何。
    ///--------------effect.dart-------------------
    import 'dart:async';
    
    import 'package:fish_redux/fish_redux.dart';
    import 'action.dart';
    import 'state.dart';
    
    Effect<FixLeakedState> buildEffect() {
      return combineEffects(<Object, Effect<FixLeakedState>>{
        FixLeakedAction.delay: _delay,
      });
    }
    
    void _delay(Action action, Context<FixLeakedState> ctx) {
      Timer(Duration(seconds: 3), () {
        ctx.dispatch(FixLeakedActionCreator.modifyContent('耗時操作結(jié)束'));
      });
    }
    ///--------------effect.dart-------------------
    ///--------------reducer.dart-------------------
    import 'package:fish_redux/fish_redux.dart';
    
    import 'action.dart';
    import 'state.dart';
    
    Reducer<FixLeakedState> buildReducer() {
      return asReducer(
        <Object, Reducer<FixLeakedState>>{
          FixLeakedAction.modifyContent: _modifyContent,
        },
      );
    }
    
    FixLeakedState _modifyContent(FixLeakedState state, Action action) {
      return state.clone()..content = action.payload;
    }
    ///--------------reducer.dart-------------------
    
  7. 萬事俱備氯檐,就等跳轉(zhuǎn)到該頁面了戒良,這么多個類是如何聯(lián)系在一起的呢?
    答案在 page.dart 中冠摄,該文件中的 FixLeakedPage 類會把除了 action.dart 文件之外的四個文件串起來糯崎,構(gòu)成一個頁面。我們只需要調(diào)用 FixLeakedPage().buildPage(args) 生成一個 Widget 對象給 Navigator 跳轉(zhuǎn)即可河泳,這里的 args 暫時傳遞 null 即可沃呢。
    另外在 state.dart 文件中的初始化函數(shù):initState(Map<String, dynamic> args) 就是經(jīng)由 FixLeakedPage().buildPage(args) 傳遞賦值的。
    ///--------------page.dart-------------------
    import 'package:fish_redux/fish_redux.dart';
    
    import 'effect.dart';
    import 'reducer.dart';
    import 'state.dart';
    import 'view.dart';
    
    class FixLeakedPage extends Page<FixLeakedState, Map<String, dynamic>> {
      FixLeakedPage()
          : super(
                initState: initState,
                effect: buildEffect(),
                reducer: buildReducer(),
                view: buildView,
                dependencies: Dependencies<FixLeakedState>(
                    adapter: null,
                    slots: <String, Dependent<FixLeakedState>>{
                    }),
                middleware: <Middleware<FixLeakedState>>[
                ],);
    
    }
        ///--------------page.dart-------------------
    
    ///改造一下main.dart文件
    ///--------------main.dart-------------------
    import 'package:fish_redux_demo/page/fixleaked/page.dart';
    import 'package:fish_redux_demo/page/mock_leaked_demo.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'FishReduxDemo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'FishReduxDemo'),
          routes: {
            '/page/mockLeakedPage': (_) => MockLeakedPage(),
            '/page/fixLeakedPage': (_) => FixLeakedPage().buildPage(null),
          },
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      void _incrementCounter() {
        Navigator.pushNamed(context, '/page/mockLeakedPage');
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  '點擊跳轉(zhuǎn)mockLeakedPage',
                ),
                RaisedButton(
                  child: Text('跳轉(zhuǎn) FishRedux FixLeaked 頁面'),
                  onPressed: () {
                    Navigator.pushNamed(context, '/page/fixLeakedPage');
                  },
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'GoToMockLeakedPage',
            child: Icon(Icons.more_horiz),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    ///--------------main.dart-------------------
    

重新運行 App 拆挥,無論你咋退出頁面薄霜,在第2小節(jié)中泄漏的問題都不會再泄漏啦。舉一反三纸兔,在許許多多用到異步的地方惰瓜,我們使用 fish_redux 就可以愉快搞定他們啦。

注意:effect 中接受的 Action 汉矿,在 reducer 中不能接受到崎坊。也就是同一個 Action 被 Dispatch 出去,effect 先于 reducer 接受洲拇,并且 effect 接受之后不會再向后派發(fā)奈揍。所以 Action 的定義需要注意消費順序。同樣的赋续,fish_redux 的全局事件派發(fā)也有同樣的事項需要注意男翰。但這不是本文的重點,之后有時間寫 fish_redux 全局狀態(tài)管理的筆記再注明吧纽乱。

4蛾绎、小結(jié)

本文中的源碼地址:fish_redux_demo。tag 列表如下:

關(guān)于 fish_redux 的更多知識大家可參考下列內(nèi)容话原。另外fish_redux 還有很多好用的用法夕吻,例如全局狀態(tài)管理、 Adapter 繁仁、middleware 等涉馅,本文由于篇幅原因只介紹 fish_redux 的簡單應(yīng)用,力求大家在看完本文章之后能對 fish_redux 的作用與工作流程有個簡單的了解黄虱。如有錯漏稚矿,還煩請指出,十分感謝哦捻浦!

從我的角度繪制了一下文中 fish_redux Page 對象的創(chuàng)建朱灿、渲染昧识、派發(fā) Action 的活動圖,本圖不涉及到開發(fā)中看不見的核心 API盗扒,這部分在學(xué)習(xí)源碼之后有空再補充吧跪楞。

fish_redux簡單活動圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侣灶,隨后出現(xiàn)的幾起案子甸祭,更是在濱河造成了極大的恐慌,老刑警劉巖褥影,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淋叶,死亡現(xiàn)場離奇詭異,居然都是意外死亡伪阶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門处嫌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栅贴,“玉大人,你說我怎么就攤上這事熏迹¢苁恚” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坛缕。 經(jīng)常有香客問我墓猎,道長,這世上最難降的妖魔是什么赚楚? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任毙沾,我火速辦了婚禮,結(jié)果婚禮上宠页,老公的妹妹穿的比我還像新娘左胞。我一直安慰自己,他們只是感情好举户,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布烤宙。 她就那樣靜靜地躺著,像睡著了一般俭嘁。 火紅的嫁衣襯著肌膚如雪躺枕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天供填,我揣著相機(jī)與錄音拐云,去河邊找鬼。 笑死捕虽,一個胖子當(dāng)著我的面吹牛慨丐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泄私,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼房揭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了晌端?” 一聲冷哼從身側(cè)響起捅暴,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咧纠,沒想到半個月后蓬痒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡漆羔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年梧奢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片演痒。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡亲轨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸟顺,到底是詐尸還是另有隱情惦蚊,我是刑警寧澤器虾,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蹦锋,受9級特大地震影響兆沙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莉掂,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一葛圃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巫湘,春花似錦装悲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阅嘶,卻和暖如春属瓣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背讯柔。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工抡蛙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人魂迄。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓粗截,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捣炬。 傳聞我的和親對象是個殘疾皇子熊昌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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