第一章 React新的前端思維方式

? ? ? ? 我們先來直觀認識React,對任何而一種工具,只有使用才能夠熟練掌握,React也不例外。通過多React快速入手,我們會解析React的工作原理,并通過與功能相同的jQuery程序對比,從而看出React的特點:

? ? 如何初始化一個React項目

? ? 如何創建一個React組件

? ? React的工作方式。

? ? 讓我們開始旅程把!

1.1初始化一個React項目

? ? ? 為了開發React應用,你的電腦是運行微軟Windows操作系統,還是蘋果Mac,或者是linux,都不重要,只要保證具備以下條件:

? ? 安裝了瀏覽器,如果是windows操作系統,請保證微軟IE瀏覽器版本不低于8.0,因為React不支持比IE8更低版本的瀏覽器。

? ? 有一個命令環境,在Windows操作系統中有命令行界面,在蘋果Mac電腦中可以使用Terminal應用,對于linux環境,命令行環境我想不用過多解釋;

? ? 一個你最喜歡的代碼編輯器,用于編輯React應用的代碼,本教程注重實踐,只有實際編碼才能深入體會。

? ? ? ? 作為開發者,推薦使用谷歌Chrom瀏覽器,因為Chrom瀏覽器自帶的開發輔助工具非常友好,而且還可以安裝輔助React和Redux的擴展工具,具體的開發工具在第四章4.2中有詳細介紹。

? ? ? React是一個JavaScript語言的工具庫,在這個JavaScript工具鋪天蓋地的時代,沒有意外,你需要安裝Node.js,React本身并不依賴與Node.js,但是我們開發中用到的諸多工具需要Node.js的支持。

? ? ? ? ? ? 在Node.js的官網(https://nodejs.org/)可以找到合適的安裝方式,安裝Node.js的同時也就安裝了npm,npm是Node.js的安裝包管理工具,因為我們不可能自己開發所有功能,會大量使用現有的安裝包,就需要npm的幫助。

1.1.1create-react-app工具

? ? ? ? React技術依賴與一個很龐大的技術棧,比如,轉譯JavaScript代碼需要使用Babel,模塊打包工具又要使用Webpack,定制build過程需要grunt或者gulp……這些技術棧都需要各自的配置文件,還沒有開始寫一行React相關代碼,開發人員就已經被各種技術名詞淹沒。

? ? ? ? ? ? 針對這種情況,React的創建者Facebook提供了一個快速開發React應用工具,名叫create-react-app,這個工具的目的是將開發人員從配置工作中解脫出來,無需過早關注這些技術細節,通過創建一個已經完成基本配置的應用,讓開發者快速開始React應用的開發。

? ? ? ? ? ? 本書中所有應用實例都有create-react-app創建,我們用這種最簡單的方式創建可運行的運用,必要的時候才會介紹底層技術棧的細節,畢竟,沒有聲明比一個能運行的應用更加增強開發者的信心。

? ? ? ? 本書中所有應用實例都有create-react-app創建,我們用這種最簡單的方式創建可運行的運用,必要的時候才會介紹底層技術棧的細節,畢竟,沒有聲明比一個能運行的應用更加增強開發者的信心。

? ? ? ? crate-react-app是一個通過npm發布的安裝包,在確認Node.js和npm安裝好之后,命令行中執行下面的命令安裝create-react-app:

? ? npm install --global create-react-app

? ? 安裝過程結束之后,你的電腦就會有create-react-app這樣一個可以執行的命令,這個命令會在當前目錄下創建指定參數名的應用目錄。

? ? 我們在命令行中執行下面的命令:

? ? create-react-app first_react_app

? ? ? ? 這個命令會在當前目錄創建一個名為first_react_app的目錄,在這個目錄中會自動添加一個應用的框架,隨后我們只需要在這個框架的基礎上修改文件就可以開發React應用,避免了大量的手工配置文件。

? ? ? ? 在creat-react-app命令一大段文字輸出之后,根據提示輸入下面的命令:

? ? cd first_react_app

? ? npm start

? ? 這個命令啟動一個開發模式的服務器,同時也會讓你的瀏覽器自動打開了一個網頁,指向本地地址http://localhost:3000/。

? ? 恭喜你,你的第一個React應用誕生了!

? ? 接下來,我們會用React開發一個簡單的功能,讓我們繼續吧。

1.2增加一個新的React組件

? ? ? ? React的首要思想是通過組件(Component)來開發應用。所謂組件,指的是能完成某個特定功能的獨立的、可重用的代碼。

? ? ? ? 基于組件的應用開發是廣泛使用的軟件開發模式,用分而治之的方法,把一個大的應用分解成若干小的組件,每個組件只關注于某個小范圍的特定功能,但是把組件組合起來,就能夠構成一個功能龐大的應用。如果分解功能的過程足夠巧妙,那么每個組件可以在不同場景下重用,那樣不光可以構建龐大的應用,還可以構建出靈活的應用。打個比方,每個組件是一塊磚,而一個應用是一座樓,想要一次鍛造創建一座樓是不現實的。實際上,總是鍛造很多磚,通過排列組合這些磚,才能構建偉大的建筑。

? ? ? ? React非常適合構建用戶交互組件,讓我們創建一個React組件開始。

? ? ? ? 學習任何一門語言或者任何一門課程,往往是從寫Hello World程序開始,不過只是展示一句Hello World并不足以體現React的神奇能力,所以,我們要做一個不那么簡單的組件,為了體現React對交互功能的支持,我們做一個顯示點擊次數的組件。

? ? ? ? 我們先看一看create-react-app給我們自動產生的代碼,在first_react_app目錄下包含如下文件和目錄:

? ? ? ? src/

? ? ? ? public/ 、

? ? ? ? README.md

? ? ? ? package.json

? ? ? ? node_modules

? ? ? ? 在開發過程中,我們主要關注src目錄中的內容,這個目錄是所有的源代碼。

? ? ? ? create-react-app所創建的應用入口是src/index.js文件,我們看看中間的內容,代碼如下:? ?

import React from 'react';

import ReactDOM from 'react-dom';

import App from './App';

import './index.css';

ReactDOM.render(

? ? <App/>,

? ? document.getElementById('root');

);

? ? ? ? 這個應用所做的事情,只是渲染一個名叫App的組件,App組件在目錄下的App.js文件中定義。

? ? ? ? 我們要定義一個新的能夠計算點擊數組件,名叫ClickCounter,所以我們修改index.js文件如下:

import React from 'react';

import ReactDOM from 'react-dom';

import ClickCounter from './ClickCounter ';

import './index.css';

ReactDOM.render(

<ClickCounter />,

document.getElementById('root');

);

? ? ? ? 我們接下來會介紹代碼的含義。

? ? ? ? 現在我們先來看看如何添加一個新組件,在src目錄下增加一個新的代碼文件ClickCounter.js,代碼如下:

import React,{Component} from 'react';

class ClickCounter extends Component{

? ? ? ? constructor(props){

? ? ? ? super(props);

? ? ? ? this.onClickButton = this.onClickButton.bind(this);

? ? ? ? this.state = {count:0};

}

? ? ? ? onClickButton(){

? ? ? ? this.setState({count:this.state.count+1});

? ? ? ? }

? ? ? ? render(){

? ? ? ? ? ? return(

? ? ? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? ? ? <button onClick={this.onClickButton}>Clicki Me</button>

? ? ? ? ? ? ? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? ? ? ? ? ? ? Click Count:{this.state.count}

? ? ? ? ? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? )

? ? ? ? }?

? }? ? ?

export default ClickCounter;?

? ? ? 如果你是從上一節不停頓直接讀到這里,而且沒有關閉命令行中的npm start命令,當你保存完這個文件之后,不需要主動做刷新網頁的動作,就會發現網頁中的內容已經發時改變。? ?

? ? ? 去點擊那個"Click Me"按鈕,可以看到"Click Count"后面的數字會隨之增加,每點擊一次加一次。? ?

? ? ? ? 恭喜你,現在你已經構建了一個有交互性的組件!? ?

? ? ? ? 現在讓我們來逐步解釋代碼中各部分的意義。? ?

? ? ? ? 在index.js文件中,使用import導入了ClickCounter組件,代替了之前的App組件。? ?

? ? ? ? import ClickCounter from './ClickCounter ';?

? ? ? ? import是ES6(EcmaSript6)語法中導入文件模板的方式。ES6語法是一個大集合,大部分功能都被最新瀏覽器支持。不過這個import方法卻不再廣泛支持之列,這沒有關系,ES6語法的JavaScript代碼會被webpack和babel轉譯成所以瀏覽器都支持的ES5語法,而這一切無需開發人員做配置,create-react-app已經替我們完成了這些工作。?

? ? ? ? 在ClickCounter.js文件的第一行,我們從react庫中引入了React和Component,如下所示:?

import React,{Component} from 'react';? ?

? ? ? ? Component作為所有組件的基類,提供了很多組件共有的功能,下面這行代碼,使用的是ES6語法來創建一個叫ClickCouner的組件類,ClickCounter的父類就是Component:? ?

class ClickCounter extends Component{?

? ? ? ? 在React出現之初,使用的是React.createClass方式來創造組件類,這種方法已經被廢棄了,但是在互聯網上依然存在大量的文章來基于React.createClass來講解React,這些文章中依然有很多真知灼見的部分,但是讀者要意識到,使用React.createClass是一種過時的方法。在本教程中,我們只使用ES6的語法來構建組件類。

細心的讀者會發現,雖然我們導入的Componnent類在ClickCounter組件定義中使用了,可是導入的React卻沒有被使用,難道在這里引入react沒有必要嗎?

事實上,引入react非常必要,你可以嘗試刪掉第一行中的React,在網頁中會立刻出現錯誤信息。

這個錯誤信息的含義是:“在使用JSX范圍內必須要有React。”

也就是說,在使用JSX的代碼文件中,即使代碼中并沒有直接使用React,也一定要導入這個React,這是因為JSX會最終被轉譯成依賴于React的表達式。

接下來,我們就要認識什么是JSX。

1.2.1JSX

? ? ? ? 所謂JSX,是JavaCript的語法擴展(eXtension),讓我們在JavaScrript中可以編寫像HTML一樣的代碼。在ClickCounter.js的render函數中,就出現了類似這樣的HTML代碼,在indexjs中,ReactDOM.render的第一個參數<App/>也是一段JSX代碼。

JSX中的這幾段代碼看起來和HTML幾乎一模一樣,都可以使用<div><button>之類的元素,所以只要熟悉HTML,學習JSX完全不成問題,但是,我們一定要明白兩者的不同之處

首先,在JSX中使用的“元素”,不局限與HTML中的元素,可以是任何一個React組件,在App.js中可以看到,我們創建的ClickCounter組件直接被直接應用到JSX中,使用方法喝其他元素一樣,這一點是傳統的HTML做不到的。

React判斷一個元素是HTML元素還是React組件的原則就是看第一個字母是否是大寫,如果在JSX中我們不用ClickCounter而是用clickCounter,那就得不到我們想要的結果。

其次,在JSX中我們可以通過onClick這樣的方式給一個元素添加一個事件處理函數,當然,在HTML中也可以用onclick,但在HTML中直接書寫onclick一直就是為讓詬病的寫法,網頁應用開發界一直倡導的是用jQuery的方法添加事件處理函數,直接寫onclick會帶來代碼混亂的問題。

這就帶來一個問題,既然長期一直不倡導在HTML中使用onclick,為什么在React的JSX中我們卻要使用onClick這樣的方式來添加事件處理函數呢?

1.2.2JSX是進步還是倒退

在React出現之初,很多人對React這樣的設計非常反感,因為React把類似HTML的標記語言和JavaScript混在一起了,但是,隨著時間的推移,業界逐漸認可了這種方式,因為大家發現,以前用HTML來代表內容,用CSS代表樣式,用JavaScript來定義交互行為,這三種語言分在三種不同的文件里面,實際上是把不同的技術分開管理了,而不是邏輯上的“”分而治之“”。

根據做同一件事的代碼應該有高耦合性的設計原則,既然我們要實現一個ClickCounter,那為什么不把實現這個功能的所有代碼集中在一個文件里呢?

這點對于初學者可能有點難以接受,但是相信你在看完這個教程后,觀點會隨之改變。

那么,在JSX中使用onClick添加事件處理函數,是否代表網頁應用開發兜了一個大圈,最終回到了起點了呢?

不是這樣,JSX的onClick事件處理方式和HTML中直接使用onclick有很大不同。

即使現在,我們還是要說在HTML中直接使用onclick很不專業,原因如下:

?①onclick添加的事件處理函數是在全局環境下執行的,這污染了全局環境,很容易產生意料不到的后果;

②給很多DOM元素添加onclick事件,可能會影響網頁的性能,畢竟,網頁需要的事件處理函數越多,性能就會越低。

③對于使用onclick的DOM元素,如果要動態地從DOM樹中刪掉的話,需要把對應的事件處理器注銷,假如忘了注銷,就可能造成內存泄露,這樣的bug很難被發現。

上面說的這些問題,在JSX中都不存在。

我們在JSX中看到一個組件使用了onClick,但并沒有產生直接使用 onclick(注意是onclick不是onClick)的HTML,而是使用了事件委托(event delegation)的方式處理點擊事件,無論有多少個onClick出現,其實最后都只在DOM樹上添加了一個事件處理函數,掛在最頂層的DOM節點上。所有的點擊事件都被這個事件處理函數捕獲,然后根據具體組件分配給特定函數,使用事件委托的性能當然要比為每個onClick都掛載一個事件處理函數要高。

因為React控制了組件的生命周期,在unmount的時間自然能夠清除相關的所有事件處理函數,內存泄露也不再是一個問題。

除了在組件中定義交互行為,我們還可以在React組件中定義樣式,我們可以修改ClickCounter.js中的render函數,代碼如下:

render(){


const? counterStyle={


margin:'16px'


}


return (


<div style={countStyle}>


<button onClick={this.onClickButton}>Click Me</button>


<div>


Click Counter:<span id="clickCount">{this.state.count}</span>


</div>


</div>


);


}? ? ?

? ? ? ? 我們在JavaScript代碼中定義一個counterStyle對象,然后在JSX中復制給頂層div的style屬性,可以看到這個網頁總這個部分的margin真的變大了。

你看,React的組件可以把JavaScript、HTML和CSS的功能在一個文件里,實現真正的組件封裝。

1.3分解React應用? ? ? ? ?

前面我們提到過,React應用實際上依賴于一個很大很復雜的技術棧,我們使用create-react-app避免在一開始就費太多精力配置技術棧,不過現在是時候了解一下這個技術棧了。

我們啟動React應用的命令是npm start,看一看package.json中對start腳本的定義,如下所示:

“scripts":{

"start":"react-scripts start",

"build":"react-scripts build",

"test":"react-scripts test --env=jsdom",

"eject":"react-scripts eject"

}? ? ? ? ? ? ? ?

可以看到,start命令實際上是調用了react-scripts這個命令,react-scripts是create-react-app添加的一個npm包,所有的配置文件都藏在node_modules/react-scripts目錄下,我們當然可以鉆進這個目錄去一探究竟,但是也可以使用eject方法來看清楚背后的原理。

你可以發現package.json文件和start并列還有其他幾個命令,其中build可以創建生產環境優化代碼,test用于單元測試,還有一個eject命令很有意思,這個eject(彈射)命令做的事情,就是把潛藏在react-scripts中的一系列技術棧配置都“”彈射“”到應用的頂層,然后我們就可以研究這些配置細節了,而且可以更靈活地定制應用的配置。

注意:eject命令是不可逆的,所以,當執行eject之前,最好做一下備份。

我們在命令行執行下面的命令,完成“”彈射“”操作:

npm run eject

這個命令會改變一些文件 ,也會添加一些文件。

當前目錄下會增加兩個目錄,一個是scripts,同時,package.json文件中的scripts部分發生了變換:

“scripts":{

"start":"node scripts/start.js",

"build":"node scripts/build.js",

"test":"node scripts/test.js --env=jsdom"

}

從此之后,start腳本將使用scripts目錄下的start.js,而不是弄得_modules目錄下的react-scripts,彈射成功,再也回不去了。

在config目錄下的webpackage.config.dev.js文件,定制的就是npm start所做的構造過程,其中有一段關于babel的定義:

test:/\.(js|jsx)$/,

include:paths.appSrc,

loader:'babel',

query:{

cacheDirectory:true

}

},

代碼中paths.appSrc的值就是src,所以這段配置的含義指的是所有以js或者jsx為擴展名的文件,都會由babel所處理。

并不是所有的瀏覽器都支持所有ES6語法,但是有了babel,我們就可以不用顧忌太多,因為babel會把ES6語法的JavaScript代碼轉譯(trranspile)成瀏覽器普遍支持的JavaScript代碼,實際上,在React社區,不使用ES6語法寫代碼才顯得奇怪。

1.4React的工作方式

在繼續深入學習React的其他知識之前,我們先就這個簡單的ClickCounter組件思考一下React的工作方式,要了解一樣東西的特點,最好的方法就是拿這個東西和另一樣東西作比較。我們就拿React和jQuery來比較。

1.4.1Jquery如何工作

假設我們用jQuery來實現ClickCounter的功能,該怎么做呢?首先,我們要產生一個網頁的HTML,寫一個index.html文件如下所示:

<!DOCTYPE html>

<html>

<body>

<div>

<button id="clickMe">Click Me</button>

<div>

Click Count:<span id="clickCount">0</span>

</div>

</div>

<script? src="../jquery-1.9.1.min.js">

<script src="./clickCounter.js"></script>

</body>

</html>

實際產品中,產生這樣的HTML可以用PHP、Java、Ruby on Rails或者任何一種服務器端語音和框架來做,也可以在瀏覽器中使用Mustache、Hogan這樣的模板產生,這里我們只是把問題簡化,直接書寫HTML。

上面的HTML只是展示樣式,并沒有任何交互功能,現在我們用jQuery來實現交互功能,和jQuery的 傳統一樣,我們把JavaScript代碼寫在一個獨立的文件clickCounterr.js里面,如下:

$(function(){

$('#clickMe').click(function(){

var clickCounter = $('#clickCount');

var count = parseInt(clickCounter.text(),10);

clickCounter.text(count+1);

})

})

用瀏覽器打開上面創造的index.html,可以看到實際效果和我們寫的React應用一模一樣,但是對比這兩段程序可以看出差異。

在jQuery的解決方案中,首先根據CSS規則找到id為clickCounter的按鈕,掛上一個匿名事件處理函數,在事件處理函數中,選中那個需要被修改的DOM元素,讀取其中的文本值,加以修改,然后修改這個DOM元素。

選中一些DOM元素,然后對這些元素做一些操作,這是一種最容易理解的開發模式。jQuery的發明人John Resig就是發現了網頁應用開發者的這個編程模式,才創造出了jQuery,其一問世就得到普遍認可,因為這種模式直觀易懂5。但是,對于龐大的項目,這種模式會造成代碼結構復雜,難以維護,每個jQuery的使用者都會有這種體會。

1.4.2React的理念

與jQuery不同,用React開發應用是另一種體驗,我們回顧一下,用React開發的ClickCounter組件并沒有像jQuery那樣做“選中一些DOM元素然后做一些事情”的動作。

React的理念,歸結為一個公式,就像下面這樣:

UI=render(data)

讓我們來看看這個公式表達的含義,用戶看到的界面(UI),應該是一個函數(在這里叫render)的執行結果,只接受數據(data)作為參數。這個函數是一個純函數,所謂純函數,指的是沒有任何副作用,輸出完全依賴于輸入的函數,兩次函數調用如果輸入相同,得到的結果也絕對相同。如此一來,最終的用戶界面,在render函數確定的情況下完全取決于輸入數據。

對于開發者來說,重要的是區分分開哪些屬于data,哪些屬于render,想要更新用戶界面,要做的就是更新data,用戶界面自然會做出響應,所以React實踐的也是“響應式編程“(Reactive Programming)的思想,這也就是React為什么叫做React的原因。

1.4.3Virtual DOM

既然React應用就是通過重復渲染來實現用戶交互,你可能會有一個疑慮:這樣的重復渲染會不會效率太低了呢?畢竟,在jQuery的實現方式中,我們可以清楚地看到每次只有需要變化的那一個DOM元素被修改了;可是,在React的實現方式中,看起來每次render函數被調用,都要把整個組件重新繪制一次,這樣看起來有點浪費。

事實并不是這樣,React利用Virtual DOM,讓每次渲染都只重新渲染最少的DOM元素。

要了解Virtual DOM,就要先了解DOM,DOM是結構化文本的抽象表達式,特定于web環境中,這個結構化文本就是HTML文本,HTML中的每個元素都對于DOM中某個節點,這樣,因為HTML元素的逐級包含關系,DOM節點自然就構成了一個樹形結構,稱為DOM叔5。

瀏覽器為了渲染HTML格式的網頁,會先HTML文本解析以構建DOM樹,然后根據DOM樹渲染出用戶看到的界面,當要改變界面內容的時候,就去改變DOM樹上的節點。

Web前端開發關于性能優化有一個原則:盡量減少DOM操作。雖然DOM操作也只是一些簡單的JavaScript語句,但是DOM操作會引起瀏覽器對網頁進行重新布局,重新繪制,這就是一個比JavaScript語句執行慢很多的過程。

雖然JSX看起來很像是一個模板,但是最終會被Babel解析為一條條創建React組件或者HTML元素的語句,神奇之處在于,React并不是通過這些語句直接構建DOM樹,而是首先構建Virtual DOM。

既然DOM樹是對HTML的抽象,那Virtual DOM就是對DOM樹的抽象。Virtual DOM不會觸及瀏覽器的部分,只是存在于JavaScript空間的樹形結構,每次自上而下渲染React組件時,會對比這一次產生的Virtual DOM,對比就會發現差別,然后修改真正的DOM樹時就只需要觸及差別中的部分就行。

以ClickCounter為例,一開始計數為0,用戶點擊按鈕讓點擊計數變成1,這一次重新渲染,React通過Virtual DOM的對比發現其實只是id為clickCount的sapn元素中內容從0變成了1而已:

<span id="clickCount">{this.sate.count}</span>

React發現這次渲染要做的事情就是更換這個span元素的內容而已,其他DOM元素都不需要觸及,于是執行類似寫了的語句,就完成了任務:

document.getElementById("clickCount").innerHTML="1";

React對比Virtual DOM尋找差異的過程比較復雜,后面第五章我們會詳細介紹對比的過程。

1.4.4React工作方式的優點

毫無疑問,jQuery的方式直觀易懂,對于初學者十分適用,但是當項目逐漸變得龐大時,用jQuery寫出的代碼往往互相糾纏,難以維護。

使用React的方式,就可以避免構建這樣復雜的程序結構,無論何種事件,引發的都是React組件的重新渲染,至于如何只修改必要的DOM部分,則完全交給React去操作,開發者并不需要去關心。

React利用函數式編程的思維來姐姐用戶界面渲染的問題,最大的優勢是開發者的效率會大大提高,開發出的代碼可維護性和可讀性也大大增強。

React等于強制所有組件都按照這種由數據驅動渲染的模式來工作,無論應用的規模多大,都能讓程序處于可控范圍內。

1.5本章小結

在這一章里,我們用create-react-app創造了一個簡單的React應用,在一開始,我們就按照 組件的思想來開發應用,React的主要理念之一就是基于組件來開發應用。

通過和同樣功能的jQuery實現方式對比,我們了解了React的工作方式,React利用聲明式的語法,讓開發者專注于描述用戶界面“”顯示成什么樣子“”,而不是重復思考“”如何去顯示“”,這樣可以大大提高開發效率,也讓代碼? ? 更加容易管理? 。

雖然React是通過重復渲染來實現動態更新效果,但是借助Virtual DOM技術,實際上這個過程并不牽扯太多的DOM操作,所以渲染效率更高。


更多內容,請訪問的我的個人博客:[https://liugezhou.github.io/blog](https://liugezhou.github.io/blog)

您也可以關注我的個人公眾號:【Wakaka】

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

推薦閱讀更多精彩內容