react+webpack+react-router+redux項目搭建(四)

(12)react-redux

與上述手動編譯,引入store不同,react-redux提供了一個方法connect

容器組件就是使用 store.subscribe() 從 Redux state 樹中讀取部分?jǐn)?shù)據(jù),并通過 props 來把這些數(shù)據(jù)提供給要渲染的組件。你可以手工來開發(fā)容器組件,但建議使用 React Redux 庫的 connect() 方法來生成,這個方法做了性能優(yōu)化來避免很多不必要的重復(fù)渲染杰扫。

a.安裝react-redux

npm install --save react-redux

b.創(chuàng)建Counter組件

src/Counter/Counter.js中

import React, {Component} from 'react';

export default class Counter extends Component {
    render() {
        return (
            <div>
                <div>當(dāng)前計數(shù)為(顯示redux計數(shù))</div>
                <button onClick={() => {
                    console.log('調(diào)用自增函數(shù)');
                }}>自增
                </button>
                <button onClick={() => {
                    console.log('調(diào)用自減函數(shù)');
                }}>自減
                </button>
                <button onClick={() => {
                    console.log('調(diào)用重置函數(shù)');
                }}>重置
                </button>
            </div>
        )
    }
}

修改路由,增加Counter膘掰,src/router/router.js中

+ import Counter from 'pages/Counter/Counter';
+ <li><Link to="/counter">Counter</Link></li>
+ <Route path="/counter" component={Counter}/>

npm start查看效果

c .將Counter組件與Redux聯(lián)合起來

使Counter能獲得Redux的state章姓,并且能發(fā)射action佳遣。與(11).f測試方法不同,這里使用react-redux提供的connect方法凡伊。
connect接收兩個參數(shù)零渐,一個mapStateToProps,就是把redux的state,轉(zhuǎn)為組件的Props系忙,還有一個參數(shù)是mapDispatchToprops,把發(fā)射actions的方法诵盼,轉(zhuǎn)為Props屬性函數(shù)。
優(yōu)化路徑:

alias {
     + actions: path.join(__dirname, 'src/redux/actions'),
     + reducers: path.join(__dirname, 'src/redux/reducers')
    }

注意:為了避免后面使用import {createStore} from ‘react-redux’沖突银还,因此我們不將redux寫別名风宁。
在src/index.js導(dǎo)入store,作為Counter組件的屬性蛹疯,如下:

import React from 'react';
import ReactDom from 'react-dom';
import {AppContainer} from 'react-hot-loader';
+ import {Provider} from 'react-redux';

import getRouter from 'router/router';
+ import store from 'redux/store';

/*初始化*/
// 如果沒該步驟戒财,頁面會出現(xiàn)空白
renderWithHotReload(getRouter());

/*熱更新*/
if (module.hot) {
   module.hot.accept('./router/router.js', () => {
       const getRouter = require('./router/router.js').default;
       renderWithHotReload(getRouter());
   });
}
function renderWithHotReload(RootElement) {
   ReactDom.render(
       <AppContainer>
           <Provider store={store}>
               {RootElement}
           </Provider>
       </AppContainer>,
       document.getElementById('app')
   )
}

修改Counter.js

import React, {Component} from 'react';
import {increment, decrement, reset} from 'actions/counter';

import {connect} from 'react-redux';

 class Counter extends Component {
    render() {
        return (
            <div>
                <div>當(dāng)前計數(shù)為{this.props.counter.count}</div>
                <button onClick={() => 
                    this.props.increment()
                }>自增
                </button>
                <button onClick={() => 
                    this.props.decrement()
                }>自減
                </button>
                <button onClick={() => 
                    this.props.reset()
                }>重置
                </button>
            </div>
        )
    }
}
const mapStateToProps = (state) => {
    return {
        counter: state.counter
    }
};
const mapDispatchToProps = (dispatch) => {
    return{
        increment: () => {
            dispatch(increment())
        },
        decrement: () => {
            dispatch(decrement())
        },
        reset: ()=> {
            dispatch(reset())
        }
    }
};
//
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

npm start


2.png

總結(jié):
(a)在store.js初始化 store,然后將 state 上的屬性作為 props 在最外成組件中層層傳遞下去捺弦。
(b)在最外層容器中饮寞,把所有內(nèi)容包裹在 Provider 組件中,將之前創(chuàng)建的 store 作為 prop 傳給 Provider列吼。
(c)Provider 內(nèi)的任何一個組件幽崩,如果需要使用 state 中的數(shù)據(jù),就必須是「被 connect 過的」組件——使用 connect 方法對「你編寫的組件」進行包裝后的產(chǎn)物寞钥。
(d)connet到底做了什么呢慌申?
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
connect() 接收四個參數(shù),它們分別是 mapStateToProps理郑,mapDispatchToProps太示,mergeProps和options。
**mapStateToProps(state, ownProps) : stateProps **這個函數(shù)允許我們將 store 中的數(shù)據(jù)作為 props 綁定到組件上香浩。

const mapStateToProps = (state) => {
    return {
        counter: state.counter
    }
};

獲取我們需要的state,并轉(zhuǎn)為props臼勉,所以<Counter>會有一個名為counter的屬性邻吭。也可以將state.counter進行篩選后再動態(tài)輸出。
函數(shù)的第二個參數(shù) ownProps宴霸,是 組件自己的props囱晴。
** mapDispatchToProps(dispatch, ownProps): dispatchProps** 將action作為props綁定到組件上。

const mapDispatchToProps = (dispatch) => {
    return{
        increment: () => {
            dispatch(increment())
        },
        decrement: () => {
            dispatch(decrement())
        },
        reset: ()=> {
            dispatch(reset())
        }
    }
};

每當(dāng)我們在 store 上 dispatch 一個 action瓢谢,store 內(nèi)的數(shù)據(jù)就會相應(yīng)地發(fā)生變化畸写。
同時,Redux 本身提供了 bindActionCreators 函數(shù)氓扛,來將 action 包裝成直接可被調(diào)用的函數(shù)枯芬。

import {bindActionCreators} from 'redux';

const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({
    increase: action.increase,
    decrease: action.decrease
  });
}

不管是 stateProps 還是 dispatchProps论笔,都需要和 ownProps merge 之后才會被賦給 MyComp。connect 的第三個參數(shù)就是用來做這件事千所。通常情況下狂魔,你可以不傳這個參數(shù),connect 就會使用 Object.assign 替代該方法淫痰。

(13)action異步

當(dāng)調(diào)用異步 API 時最楷,有兩個非常關(guān)鍵的時刻:發(fā)起請求的時刻,和接收到響應(yīng)的時刻(也可能是超時)待错。

這兩個時刻都可能會更改應(yīng)用的 state籽孙;為此,你需要 dispatch 普通的同步 action火俄。一般情況下犯建,每個 API 請求都需要dispatch 至少三種 action:

①一種通知reducer請求開始的action:

對于這種 action,reducer 可能會切換一下state中的isFetching 標(biāo)記烛占。以此來告訴 UI 來顯示加載界面胎挎。

②一種通知reducer請求成功的action。

對于這種 action忆家,reducer 可能會把接收到的新數(shù)據(jù)合并到state 中犹菇,并重置 isFetching。UI 則會隱藏加載界面芽卿,并顯示接收到的數(shù)據(jù)揭芍。

③一種通知 reducer 請求失敗的action。

對于這種 action卸例,reducer 可能會重置isFetching称杨。另外,有些 reducer 會保存這些失敗信息筷转,并在 UI 里顯示出來姑原。

為了區(qū)分這三種 action,可能在 action 里添加一個專門的 status 字段作為標(biāo)記位呜舒,又或者為它們定義不同的 type锭汛。


{ type: 'FETCH_POSTS_REQUEST' }

{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }

{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

該實例的邏輯思路如下:

i. 請求開始的時候,界面轉(zhuǎn)圈提示正在加載袭蝗。isLoading置為true唤殴。

ii. 請求成功,顯示數(shù)據(jù)到腥。isLoading置為false,data填充數(shù)據(jù)朵逝。

iii. 請求失敗,顯示失敗乡范。isLoading置為false配名,顯示錯誤信息啤咽。

a.創(chuàng)建后臺API

創(chuàng)建一個user.json,等會請求用段誊,相當(dāng)于后臺的API接口闰蚕。在dist/api/user.json文件下:


{

 "name": "LALA",

 "intro": "enjoy hotpot"

}

b.創(chuàng)建action
export const GET_USER_INFO_REQUEST = "userInfo/GET_USER_INFO_REQUEST";

export const GET_USER_INFO_SUCCESS = "userInfo/GET_USER_INFO_SUCCESS";

export const GET_USER_INFO_FAIL = "userInfo/GET_USER_INFO_FAIL";

// 創(chuàng)建請求中,請求成功连舍,請求失敗 三個action創(chuàng)建函數(shù)

function getUserInfoRequest() {

 return {

 type: GET_USER_INFO_REQUEST

 }

}

function getUserInfoSuccess() {

 return {

 type: GET_USER_INFO_SUCCESS,

 userInfo: userInfo

 // userInfo: userInfo???有什么作用呢没陡?

 }

}

function getUserInfoFail() {

 return {

 type: GET_USER_INFO_FAIL

 }

}

// 將三個action與網(wǎng)絡(luò)請求聯(lián)系到一起

// 為什么要將網(wǎng)絡(luò)請求放到action,而不是rreducer???

export function getUserInfo() {

 return function (dispatch) {

 dispatch(getUserInfoRequest());

 return fetch('http://localhost:8080/api/user.json')

 .then((response => {

 return response.json();

 }))

 .then((json) => {

 dispatch(getUserInfoSuccess(json));

 }).catch(

 () => {

 dispatch(getUserInfoFail());

 }

 )

 }

}

因為這個地方的action返回的是個函數(shù)索赏,而不是對象盼玄,所以需要使用redux-thunk。

c.創(chuàng)建reducer

在src/redux/reducers/userInfo.js中


import {GET_USER_INFO_REQUEST, GET_USER_INFO_SUCCESS, GET_USER_INFO_FAIL} from 'actions/userInfo';

//初始化

const initState = {

 isLoading: false,

 userInfo: {},

 errorMsg: ''

}

export default function reducer (state = initState, action) {

 switch (action.tyoe) {

 case GET_USER_INFO_REQUEST:

 return {

 // ...state 保證其他state更新;是和別人的Object.assign()起同一個作用

 ...state,

 isLoading: true,

 userInfo: {},

 errorMsg: ''

 };

 case GET_USER_INFO_SUCCESS:

 return {

 ...state,

 isLoading: false,

 userInfo: action.userInfo,

 errorMsg: ''

 };

 case GET_USER_INFO_FAIL:

 return {

 ...state,

 isLoading: false,

 userInfo: {},

 errorMsg: '請求錯誤'

 }

 default:

 return state;

 }

}

d. 合并reducer

redux/reducer.js


import counter from 'reducers/counter';

import userInfo from 'reducers/userInfo';

export default function combineReducers(state={}, action){

 return {

 counter: counter(state.counter, action),

 userInfo: userInfo(state.userInfo, action)

 }

}

e. redux-thunk

redux中間件middleware就是action在到達reducer潜腻,先經(jīng)過中間件處理埃儿。我們之前知道reducer能處理的action只有這樣的{type:xxx},所以我們使用中間件來處理函數(shù)形式的action融涣,把他們轉(zhuǎn)為標(biāo)準(zhǔn)的action給reducer童番。這是redux-thunk的作用

f. 安裝redux-thunk

npm install --save redux-thunk

g. 在src/redux/store.js中引入middleware

+ import {createStore, applyMidddleware} from 'redux';

import combineReducers from './reducers.js';

+ import thunkMiddleware from 'redux-thunk';

+ let store = createStore(combineReducers,applyMidddleware(thunkMiddleware));

export default store;

h. 創(chuàng)建UserInfo組件驗證

src/pages/ UserInfo /UserInfo.js


import React, {Component} from 'react';

import {connect} from 'react-redux';

import {getUserInfo} from 'action/userInfo';

class UserInfo extends Component {

 render() {

 const {userInfo, isLoading, errorMsg} = this.props.userInfo;

 return(

 <div>

 {

 isLoading ? '請求信息中.......' :

 (

 errorMsg ? errorMsg :

 <div>

 <p>用戶信息:</p>

 <p>用戶名:{userInfo.name}</p>

 <p>介紹:{userInfo.intro}</p>

 </div>

 )

 }

 <button onClick={() => this.props.getUserInfo()}>請求用戶信息</button>

 </div>

 )

 }

}

export default connect((state) => ({userInfo: state.userInfo}), {getUserInfo})(UserInfo);

i. 添加路由/userInfo

在 src/router/router.js 文件下


+ import UserInfo from 'pages/UserInfo/UserInfo';

+ <li><Link to="/userInfo">UserInfo</Link></li>

+ <Route path="/userInfo" component={UserInfo}/>

npm start,查看效果
3.png
d. 修改webpack的output屬性
    output: {
    path: path.join(__dirname, 'dist'),
    filename: 'app.js',
     chunkFilename: '[name].js'
  }
4.png

此時的文件名由:

import Home from 'bundle-loader?lazy&name=home!pages/Home/Home';

中的name值決定

(20)webpack緩存

為了避免多次訪問同一頁面后不再次下載資源威鹿,需要進行緩存剃斧。可以通過命中緩存忽你,以降低網(wǎng)絡(luò)流量幼东,使網(wǎng)站加載速度更快,然而科雳,如果我們在部署新版本時不更改資源的文件名根蟹,瀏覽器可能會認(rèn)為它沒有被更新,就會使用它的緩存版本糟秘。由于緩存的存在简逮,當(dāng)你需要獲取新的代碼時,就會顯得很棘手尿赚。因此买决,可以使用webpack配置,通過必要的配置吼畏,以確保 webpack 編譯生成的文件能夠被客戶端緩存,而在文件內(nèi)容變化后嘁灯,能夠請求到新的文件泻蚊。

  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js',
    chunkFilename: '[name].[chunkhash].js'
  },

打包后的文件:


6.png

但是在dist/index.html中引用的還是之前的filename: app.js ,訪問時會出錯丑婿。因此性雄,可以用插件HtmlWebpackPlugin

(21)HtmlWebpackPlugin

HtmlWebpackPlugin插件會自動生成一個HTML文件没卸,并引用相關(guān)的文件。

a. 安裝

npm install --save-dev html-webpack-plugin

b. webpack配置
+let HtmlWebpackPlugin = require('html-webpack-plugin'); //通過 npm 安裝
+  plugins: [new HtmlWebpackPlugin({
        filename: 'index.html',
        template: path.join(__dirname, 'src/index.html')
    })],
c.創(chuàng)建index.html

刪除 dist/index.html秒旋,創(chuàng)建src/html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Data</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

此時我們會發(fā)現(xiàn)


image.png

找到



我們發(fā)現(xiàn)之前在dist/index.html中的<script>仍然存在约计,這是為什么呢?迁筛?煤蚌?
(22)提取公共代碼

對于基本不會發(fā)生變化的代碼提取出來,比如react,redux,react-router细卧。但是他們合并到了bundle.js中了尉桩,每次項目發(fā)布需要重新提取。
CommonsChunkPlugin 可以用于將模塊分離到單獨的文件中贪庙。能夠在每次修改后的構(gòu)建結(jié)果中蜘犁,將 webpack 的樣板(boilerplate)和 manifest 提取出來。通過指定 entry 配置中未用到的名稱止邮,此插件會自動將我們需要的內(nèi)容提取到單獨的包中:

a.配置webpack.dev.config.js
+ const webpack = require('webpack'); //訪問內(nèi)置的插件
+  entry: {app: [
    'react-hot-loader/patch',
     path.join(__dirname, 'src/index.js'),
  ],
  vendor: ['react', 'react-router-dom', 'redux', 'react-dom', 'react-redux']
  }
+ plugins: [
…
new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor'
        })
]

把react等庫生成打包到vendor.hash.js里面去这橙。

但是你現(xiàn)在可能發(fā)現(xiàn)編譯生成的文件main.[hash].js和vendor.[hash].js生成的hash一樣的,這里是個問題导披,因為呀屈扎,你每次修改代碼,都會導(dǎo)致vendor.[hash].js名字改變,那我們提取出來的意義也就沒了盛卡。其實文檔上寫的很清楚助隧,

output: {
        path: path.join(__dirname, './dist'),
        filename: '[name].[hash].js', //這里應(yīng)該用chunkhash替換hash
        chunkFilename: '[name].[chunkhash].js'
    }
b.存在問題

但是無奈,如果用chunkhash滑沧,會報錯并村。和webpack-dev-server --hot不兼容,具體看這里滓技。
現(xiàn)在我們在配置開發(fā)版配置文件哩牍,就向webpack-dev-server妥協(xié),因為我們要用他令漂。問題先放這里膝昆,等會再配置正式版webpack.dev.config.js的時候要解決這個問題。

(23)構(gòu)建生產(chǎn)環(huán)境

開發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)的構(gòu)建目標(biāo)差異很大叠必。在開發(fā)環(huán)境中荚孵,我們需要具有強大的、具有實時重新加載(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server纬朝。而在生產(chǎn)環(huán)境中收叶,我們的目標(biāo)則轉(zhuǎn)向于關(guān)注更小的 bundle,更輕量的 source map共苛,以及更優(yōu)化的資源判没,以改善加載時間蜓萄。由于要遵循邏輯分離,我們通常建議為每個環(huán)境編寫彼此獨立的 webpack 配置澄峰。

a. 編寫webpack.config.js

在webpack.dev.config.js基礎(chǔ)上做一下修改:
①先刪除webpack-dev-server相關(guān)的東西
②devtool的值改成cheap-module-source-map
③將filename: '[name].[hash].js',改成[chunkhash]嫉沽,這是因為npm run build后,發(fā)現(xiàn)app.xxx.js和vendor.xxx.js不一樣了哦俏竞。

c. package.json增加打包腳本
"build":"webpack --config webpack.config.js"

運行 npm run build
注意绸硕,不是npm build

image.png

生成了vundor.[chunkhash].html

(24)壓縮文件
a. 安裝

npm i --save-dev uglifyjs-webpack-plugin

b. 配置

在webpack.config.js下

const UglifyJSPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  plugins: [
    new UglifyJSPlugin()
  ]
}
image.png

此時,vendor.[hash].js小了很多

(25)指定環(huán)境

許多 library 將通過與 process.env.NODE_ENV 環(huán)境變量關(guān)聯(lián)胞此,以決定 library 中應(yīng)該引用哪些內(nèi)容臣咖。例如,當(dāng)不處于生產(chǎn)環(huán)境中時漱牵,某些 library 為了使調(diào)試變得容易夺蛇,可能會添加額外的日志記錄(log)和測試(test)。其實酣胀,當(dāng)使用 process.env.NODE_ENV === 'production' 時刁赦,一些 library 可能針對具體用戶的環(huán)境進行代碼優(yōu)化,從而刪除或添加一些重要代碼闻镶。我們可以使用 webpack 內(nèi)置的 DefinePlugin 為所有的依賴定義這個變量:

在webpack.config.js下

module.exports = {
  plugins: [
       new webpack.DefinePlugin({
          'process.env': {
              'NODE_ENV': JSON.stringify('production')
           }
       })
  ]
}
image.png

此時, vendor.[hash].js更小了

(26)優(yōu)化緩存

我們現(xiàn)在來解決(21).b存在的問題甚脉,如何讓在修改任意一個.js文件后,vendor.[].js名字保持不變铆农。Webpack官網(wǎng)推薦了HashedModuleIdsPlugin插件牺氨,見https://doc.webpack-china.org/plugins/hashed-module-ids-plugin

該插件會根據(jù)模塊的相對路徑生成一個四位數(shù)的hash作為模塊id, 建議用于生產(chǎn)環(huán)境。


plugins: [

…

 new webpack.HashedModuleIdsPlugin()

 ]

然而墩剖,再次大包后vendor的名字還會發(fā)生變化猴凹。這時還需要添加一個runtime代碼抽取


new webpack.optimize.CommonsChunkPlugin({

 name: 'runtime'

})

這時因為:

runtime,以及伴隨的 manifest 數(shù)據(jù)岭皂,主要是指:在瀏覽器運行時郊霎,webpack 用來連接模塊化的應(yīng)用程序的所有代碼。runtime 包含:在模塊交互時爷绘,連接模塊所需的加載和解析邏輯书劝。包括瀏覽器中的已加載模塊的連接,以及懶加載模塊的執(zhí)行邏輯土至。

引入順序在這里很重要购对。CommonsChunkPlugin 的 'vendor' 實例,必須在 'runtime' 實例之前引入

(27)打包優(yōu)化

每次打包后的文件都存在了/dist文件夾下陶因,我們希望每次打包前自動清理下dist文件洞斯。可以使用clean-webpack-plugin進行清理。

a. 安裝

npm install clean-webpack-plugin --save-dev

b.配置

webpack.config.js下


+ const CleanWebpackPlugin = require('clean-webpack-plugin');

+plugins: [

…

 new CleanWebpackPlugin(['dist'])

]

運行nom run build會發(fā)現(xiàn)烙如,之前的./dist文件先被刪除后才生成新的文件。

(28)抽取CSS

目前的CSS打包進了js里面毅否,webpack提供了一個插件亚铁,可以將css提取出來。Webpack提供了extract-text-webpack-plugin插件螟加,可以提取單獨的css文件徘溢。見:https://github.com/webpack-contrib/extract-text-webpack-plugin

a. 安裝

npm install --save-dev extract-text-webpack-plugin

b. 配置

webpack.config.js


const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {

 module: {

 rules: [

 {

 test: /\.scss$/,

 use: ExtractTextPlugin.extract({

 fallback: 'style-loader',

 use: ['css-loader', 'sass-loader']

 })

 }

 ]

 },

 plugins: [

…

new ExtractTextPlugin({

 filename: '[name].[contenthash:5].css',

 allChunks: true

 })

 ]

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捆探,一起剝皮案震驚了整個濱河市然爆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黍图,老刑警劉巖曾雕,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異助被,居然都是意外死亡剖张,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門揩环,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搔弄,“玉大人,你說我怎么就攤上這事丰滑」擞蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵褒墨,是天一觀的道長炫刷。 經(jīng)常有香客問我,道長貌亭,這世上最難降的妖魔是什么柬唯? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮圃庭,結(jié)果婚禮上锄奢,老公的妹妹穿的比我還像新娘。我一直安慰自己剧腻,他們只是感情好拘央,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著书在,像睡著了一般灰伟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天栏账,我揣著相機與錄音帖族,去河邊找鬼。 笑死挡爵,一個胖子當(dāng)著我的面吹牛竖般,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茶鹃,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼涣雕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闭翩?” 一聲冷哼從身側(cè)響起挣郭,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疗韵,沒想到半個月后兑障,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡伶棒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年旺垒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肤无。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡先蒋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宛渐,到底是詐尸還是另有隱情竞漾,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布窥翩,位于F島的核電站业岁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寇蚊。R本人自食惡果不足惜笔时,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仗岸。 院中可真熱鬧允耿,春花似錦、人聲如沸扒怖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盗痒。三九已至蚂蕴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骡楼。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工熔号, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸟整。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓跨嘉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吃嘿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345