你要的 React 面試知識(shí)點(diǎn)只磷,都在這了

摘要: 問題很詳細(xì)经磅,插圖很好看泌绣。

Fundebug經(jīng)授權(quán)轉(zhuǎn)載预厌,版權(quán)歸原作者所有阿迈。

React是流行的javascript框架之一,在2019年及以后將會(huì)更加流行轧叽。React于2013年首次發(fā)布苗沧,多年來廣受歡迎。它是一個(gè)聲明性的炭晒、基于組件的待逞、用于構(gòu)建用戶界面的高效javascript庫。

以下是面試前必須了解的話題网严。

  • 什么是聲明式編程
  • 聲明式編程 vs 命令式編程
  • 什么是函數(shù)式編程
  • 什么是組件設(shè)計(jì)模式
  • React 是什么
  • React 和 Angular 有什么不同
  • 什么是虛擬DOM及其工作原理
  • 什么是JSX
  • 組件和不同類型
  • Props 和 State
  • 什么是 PropTypes
  • 如何更新狀態(tài)和不更新狀態(tài)
  • 組件生命周期方法
  • 超越繼承的組合
  • 如何在React中應(yīng)用樣式
  • 什么是Redux及其工作原理
  • 什么是React路由器及其工作原理
  • 什么是錯(cuò)誤邊界
  • 什么是 Fragments
  • 什么是傳送門(Portals)
  • 什么是 Context
  • 什么是 Hooks
  • 如何提高性能
  • 如何在重新加載頁面時(shí)保留數(shù)據(jù)
  • 如何從React中調(diào)用API
  • 總結(jié)

什么是聲明式編程

聲明式編程是一種編程范式识樱,它關(guān)注的是你要做什么,而不是如何做震束。它表達(dá)邏輯而不顯式地定義步驟怜庸。這意味著我們需要根據(jù)邏輯的計(jì)算來聲明要顯示的組件。它沒有描述控制流步驟垢村。聲明式編程的例子有HTML割疾、SQL等

HTML file

// HTML
<div>
  <p>Declarative Programming</p>
</div>

SQL file

select * from studens where firstName = 'declarative';

聲明式編程 vs 命令式編程

聲明式編程的編寫方式描述了應(yīng)該做什么,而命令式編程描述了如何做嘉栓。在聲明式編程中宏榕,讓編譯器決定如何做事情。聲明性程序很容易推理侵佃,因?yàn)榇a本身描述了它在做什么麻昼。

下面是一個(gè)例子,數(shù)組中的每個(gè)元素都乘以 2趣钱,我們使用聲明式map函數(shù)涌献,讓編譯器來完成其余的工作,而使用命令式首有,需要編寫所有的流程步驟燕垃。

const numbers = [1,2,3,4,5];

// 聲明式
const doubleWithDec = numbers.map(number => number * 2);

console.log(doubleWithDec)

// 命令式
const doubleWithImp = [];
for(let i=0; i<numbers.length; i++) {
    const numberdouble = numbers[i] * 2;
    doubleWithImp.push(numberdouble)
}

console.log(doubleWithImp)

什么是函數(shù)式編程

函數(shù)式編程是聲明式編程的一部分。javascript中的函數(shù)是第一類公民井联,這意味著函數(shù)是數(shù)據(jù)卜壕,你可以像保存變量一樣在應(yīng)用程序中保存、檢索和傳遞這些函數(shù)烙常。

函數(shù)式編程有些核心的概念轴捎,如下:

  • 不可變性(Immutability)
  • 純函數(shù)(Pure Functions)
  • 數(shù)據(jù)轉(zhuǎn)換(Data Transformations)
  • 高階函數(shù) (Higher-Order Functions)
  • 遞歸
  • 組合

不可變性(Immutability)

不可變性意味著不可改變鹤盒。 在函數(shù)式編程中,你無法更改數(shù)據(jù)侦副,也不能更改侦锯。 如果要改變或更改數(shù)據(jù),則必須復(fù)制數(shù)據(jù)副本來更改秦驯。

例如尺碰,這是一個(gè)student對象和changeName函數(shù),如果要更改學(xué)生的名稱译隘,則需要先復(fù)制 student 對象亲桥,然后返回新對象。

在javascript中固耘,函數(shù)參數(shù)是對實(shí)際數(shù)據(jù)的引用题篷,你不應(yīng)該使用 student.firstName =“testing11”,這會(huì)改變實(shí)際的student 對象厅目,應(yīng)該使用Object.assign復(fù)制對象并返回新對象番枚。

let student = {
    firstName: "testing",
    lastName: "testing",
    marks: 500
}

function changeName(student) {
    // student.firstName = "testing11" //should not do it
    let copiedStudent = Object.assign({}, student);
    copiedStudent.firstName = "testing11";
    return copiedStudent;
}

console.log(changeName(student));

console.log(student);

純函數(shù)

純函數(shù)是始終接受一個(gè)或多個(gè)參數(shù)并計(jì)算參數(shù)并返回?cái)?shù)據(jù)或函數(shù)的函數(shù)。 它沒有副作用璧瞬,例如設(shè)置全局狀態(tài)户辫,更改應(yīng)用程序狀態(tài),它總是將參數(shù)視為不可變數(shù)據(jù)嗤锉。

我想使用 appendAddress 的函數(shù)向student對象添加一個(gè)地址渔欢。 如果使用非純函數(shù),它沒有參數(shù)瘟忱,直接更改 student 對象來更改全局狀態(tài)奥额。

使用純函數(shù),它接受參數(shù)访诱,基于參數(shù)計(jì)算垫挨,返回一個(gè)新對象而不修改參數(shù)。

let student = {
    firstName: "testing",
    lastName: "testing",
    marks: 500
}

// 非純函數(shù)
function appendAddress() {
    student.address = {streetNumber:"0000", streetName: "first", city:"somecity"};
}

console.log(appendAddress());

// 純函數(shù)
function appendAddress(student) {
    let copystudent = Object.assign({}, student);
    copystudent.address = {streetNumber:"0000", streetName: "first", city:"somecity"};
    return copystudent;
}

console.log(appendAddress(student));

console.log(student);

數(shù)據(jù)轉(zhuǎn)換

我們講了很多關(guān)于不可變性的內(nèi)容触菜,如果數(shù)據(jù)是不可變的九榔,我們?nèi)绾胃淖償?shù)據(jù)。如上所述涡相,我們總是生成原始數(shù)據(jù)的轉(zhuǎn)換副本哲泊,而不是直接更改原始數(shù)據(jù)。

再介紹一些 javascript內(nèi)置函數(shù)催蝗,當(dāng)然還有很多其他的函數(shù)切威,這里有一些例子。所有這些函數(shù)都不改變現(xiàn)有的數(shù)據(jù)丙号,而是返回新的數(shù)組或?qū)ο蟆?/p>

let cities = ["irving", "lowell", "houston"];

// we can get the comma separated list
console.log(cities.join(','))
// irving,lowell,houston

// if we want to get cities start with i
const citiesI = cities.filter(city => city[0] === "i");
console.log(citiesI)
// [ 'irving' ]

// if we want to capitalize all the cities
const citiesC = cities.map(city => city.toUpperCase());
console.log(citiesC)
// [ 'IRVING', 'LOWELL', 'HOUSTON' ]

高階函數(shù)

高階函數(shù)是將函數(shù)作為參數(shù)或返回函數(shù)的函數(shù)先朦,或者有時(shí)它們都有缰冤。 這些高階函數(shù)可以操縱其他函數(shù)。

Array.map喳魏,Array.filter和Array.reduce是高階函數(shù)棉浸,因?yàn)樗鼈儗⒑瘮?shù)作為參數(shù)。

const numbers = [10,20,40,50,60,70,80]

const out1 = numbers.map(num => num * 100);
console.log(out1);
// [ 1000, 2000, 4000, 5000, 6000, 7000, 8000 ]

const out2 = numbers.filter(num => num > 50);
console.log(out2);
// [ 60, 70, 80 ]

const out3 = numbers.reduce((out,num) => out + num);
console.log(out3);
// 330

下面是另一個(gè)名為isPersonOld的高階函數(shù)示例截酷,該函數(shù)接受另外兩個(gè)函數(shù)涮拗,分別是 messageisYoung 乾戏。

const isYoung = age => age < 25;

const message = msg => "He is "+ msg;

function isPersonOld(age, isYoung, message) {
    const returnMessage = isYoung(age)?message("young"):message("old");
    return returnMessage;
}

// passing functions as an arguments
console.log(isPersonOld(13,isYoung,message))
// He is young

遞歸

遞歸是一種函數(shù)在滿足一定條件之前調(diào)用自身的技術(shù)迂苛。只要可能,最好使用遞歸而不是循環(huán)鼓择。你必須注意這一點(diǎn)三幻,瀏覽器不能處理太多遞歸和拋出錯(cuò)誤。

下面是一個(gè)演示遞歸的例子呐能,在這個(gè)遞歸中念搬,打印一個(gè)類似于樓梯的名稱。我們也可以使用for循環(huán)摆出,但只要可能朗徊,我們更喜歡遞歸。

function printMyName(name, count) {
    if(count <= name.length) {
        console.log(name.substring(0,count));
        printMyName(name, ++count);
    }
}

console.log(printMyName("Bhargav", 1));

/*
B
Bh
Bha
Bhar
Bharg
Bharga
Bhargav
*/

// withotu recursion
var name = "Bhargav"
var output = "";
for(let i=0; i<name.length; i++) {
    output = output + name[i];
    console.log(output);
}

組合

在React中偎漫,我們將功能劃分為小型可重用的純函數(shù)爷恳,我們必須將所有這些可重用的函數(shù)放在一起,最終使其成為產(chǎn)品象踊。 將所有較小的函數(shù)組合成更大的函數(shù)温亲,最終,得到一個(gè)應(yīng)用程序杯矩,這稱為組合栈虚。

實(shí)現(xiàn)組合有許多不同方法。 我們從Javascript中了解到的一種常見方法是鏈接史隆。 鏈接是一種使用點(diǎn)表示法調(diào)用前一個(gè)函數(shù)的返回值的函數(shù)的方法魂务。

這是一個(gè)例子。 我們有一個(gè)name泌射,如果firstNamelastName大于5個(gè)單詞的大寫字母粘姜,剛返回,并且打印名稱的名稱和長度魄幕。

const name = "Bhargav Bachina";

const output = name.split(" ")
    .filter(name => name.length > 5)
    .map(val => {
    val = val.toUpperCase();
    console.log("Name:::::"+val);
    console.log("Count::::"+val.length);
    return val;
});

console.log(output)
/*
Name:::::BHARGAV
Count::::7
Name:::::BACHINA
Count::::7
[ 'BHARGAV', 'BACHINA' ]
*/

在React中相艇,我們使用了不同于鏈接的方法,因?yàn)槿绻?0個(gè)這樣的函數(shù)纯陨,就很難進(jìn)行鏈接坛芽。這里的目的是將所有更簡單的函數(shù)組合起來生成一個(gè)更高階的函數(shù)留储。

const name = compose(
    splitmyName,
    countEachName,
    comvertUpperCase,
    returnName
)

console.log(name);

什么是 React

React是一個(gè)簡單的javascript UI庫,用于構(gòu)建高效咙轩、快速的用戶界面获讳。它是一個(gè)輕量級庫,因此很受歡迎活喊。它遵循組件設(shè)計(jì)模式丐膝、聲明式編程范式和函數(shù)式編程概念,以使前端應(yīng)用程序更高效钾菊。它使用虛擬DOM來有效地操作DOM帅矗。它遵循從高階組件到低階組件的單向數(shù)據(jù)流。

React 與 Angular 有何不同煞烫?

Angular是一個(gè)成熟的MVC框架浑此,帶有很多特定的特性,比如服務(wù)滞详、指令凛俱、模板、模塊料饥、解析器等等蒲犬。React是一個(gè)非常輕量級的庫,它只關(guān)注MVC的視圖部分岸啡。

Angular遵循兩個(gè)方向的數(shù)據(jù)流原叮,而React遵循從上到下的單向數(shù)據(jù)流。React在開發(fā)特性時(shí)給了開發(fā)人員很大的自由凰狞,例如篇裁,調(diào)用API的方式、路由等等赡若。我們不需要包括路由器庫达布,除非我們需要它在我們的項(xiàng)目。

什么是Virtual DOM及其工作原理

React 使用 Virtual DOM 來更新真正的 DOM逾冬,從而提高效率和速度黍聂。 我們來詳細(xì)討論這些。

什么是Virtual DOM

瀏覽器遵循HTML指令來構(gòu)造文檔對象模型(DOM)身腻。當(dāng)瀏覽器加載HTML并呈現(xiàn)用戶界面時(shí)产还,HTML文檔中的所有元素都變成DOM元素。

DOM是從根元素開始的元素層次結(jié)構(gòu)嘀趟。例如脐区,看看下面的HTML。

<div>
    <div>
        <h1>This is heading</h1>
        <p>this is paragraph</p>
        <div>
            <p>This is just a paragraon</p>
        </div>
    </div>
    <div>
        <h1>This is heading</h1>
        <p>this is paragraph</p>
        <div>
            <p>This is just a paragraon</p>
        </div>
    </div>
    <div>
        <h1>This is heading</h1>
        <p>this is paragraph</p>
        <div>
            <p>This is just a paragraon</p>
        </div>
    </div>
</div>

當(dāng)在瀏覽器中加載這個(gè)HTML時(shí)她按,所有這些HTML元素都被轉(zhuǎn)換成DOM元素牛隅,如下所示

當(dāng)涉及到SPA應(yīng)用程序時(shí)炕柔,首次加載index.html,并在index.html本身中加載更新后的數(shù)據(jù)或另一個(gè)html媒佣。當(dāng)用戶瀏覽站點(diǎn)時(shí)匕累,我們使用新內(nèi)容更新相同的index.html。每當(dāng)DOM發(fā)生更改時(shí)默伍,瀏覽器都需要重新計(jì)算CSS欢嘿、進(jìn)行布局并重新繪制web頁面。

React 使用 Virtual DOM 有效地重建 DOM也糊。 對于我們來說炼蹦,這使得DOM操作的一項(xiàng)非常復(fù)雜和耗時(shí)的任務(wù)變得更加容易。 React從開發(fā)人員那里抽象出所有這些显设,以便在Virtual DOM的幫助下構(gòu)建高效的UI框弛。

虛擬DOM是如何工作的

虛擬DOM只不過是真實(shí) DOM 的 javascript對象表示。 與更新真實(shí) DOM 相比捕捂,更新 javascript 對象更容易,更快捷斗搞。 考慮到這一點(diǎn)指攒,讓我們看看它是如何工作的。

React將整個(gè)DOM副本保存為虛擬DOM


每當(dāng)有更新時(shí)僻焚,它都會(huì)維護(hù)兩個(gè)虛擬DOM允悦,以比較之前的狀態(tài)和當(dāng)前狀態(tài),并確定哪些對象已被更改虑啤。 例如隙弛,段落文本更改為更改。

現(xiàn)在狞山,它通過比較兩個(gè)虛擬DOM 差異全闷,并將這些變化更新到實(shí)際DOM


一旦真正的DOM更新,它也會(huì)更新UI


什么是 JSX

JSX是javascript的語法擴(kuò)展萍启。它就像一個(gè)擁有javascript全部功能的模板語言总珠。它生成React元素,這些元素將在DOM中呈現(xiàn)勘纯。React建議在組件使用JSX局服。在JSX中,我們結(jié)合了javascript和HTML驳遵,并生成了可以在DOM中呈現(xiàn)的react元素淫奔。

下面是JSX的一個(gè)例子。我們可以看到如何將javascript和HTML結(jié)合起來堤结。如果HTML中包含任何動(dòng)態(tài)變量唆迁,我們應(yīng)該使用表達(dá)式{}佳鳖。

import React from 'react';

export const Header = () => {

    const heading = 'TODO App'

    return(
        <div style={{backgroundColor:'orange'}}>
            <h1>{heading}</h1>
        </div>
    )
}

代碼部署后可能存在的BUG沒法實(shí)時(shí)知道,事后為了解決這些BUG媒惕,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試系吩,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug

組件和不同類型

React 中一切都是組件妒蔚。 我們通常將應(yīng)用程序的整個(gè)邏輯分解為小的單個(gè)部分穿挨。 我們將每個(gè)單獨(dú)的部分稱為組件。 通常肴盏,組件是一個(gè)javascript函數(shù)科盛,它接受輸入,處理它并返回在UI中呈現(xiàn)的React元素菜皂。

在React中有不同類型的組件贞绵。讓我們詳細(xì)看看。

函數(shù)/無狀態(tài)/展示組件

函數(shù)或無狀態(tài)組件是一個(gè)純函數(shù)恍飘,它可接受接受參數(shù)榨崩,并返回react元素。這些都是沒有任何副作用的純函數(shù)章母。這些組件沒有狀態(tài)或生命周期方法母蛛,這里有一個(gè)例子。

import React from 'react';
import Jumbotron from 'react-bootstrap/Jumbotron';

export const Header = () => {
    return(
        <Jumbotron style={{backgroundColor:'orange'}}>
            <h1>TODO App</h1>
        </Jumbotron>
    )
}

類/有狀態(tài)組件

類或有狀態(tài)組件具有狀態(tài)和生命周期方可能通過setState()方法更改組件的狀態(tài)乳怎。類組件是通過擴(kuò)展React創(chuàng)建的彩郊。它在構(gòu)造函數(shù)中初始化,也可能有子組件,這里有一個(gè)例子蚪缀。

import React from 'react';
import '../App.css';
import { ToDoForm } from './todoform';
import { ToDolist } from './todolist';

export class Dashboard extends React.Component {

  constructor(props){
    super(props);

    this.state = {

    }
  }
  
  render() {
    return (
      <div className="dashboard"> 
          <ToDoForm />
          <ToDolist />
      </div>
    );
  }
}

受控組件

受控組件是在 React 中處理輸入表單的一種技術(shù)秫逝。表單元素通常維護(hù)它們自己的狀態(tài),而react則在組件的狀態(tài)屬性中維護(hù)狀態(tài)询枚。我們可以將兩者結(jié)合起來控制輸入表單违帆。這稱為受控組件。因此哩盲,在受控組件表單中前方,數(shù)據(jù)由React組件處理。

這里有一個(gè)例子廉油。當(dāng)用戶在 todo 項(xiàng)中輸入名稱時(shí)惠险,調(diào)用一個(gè)javascript函數(shù)handleChange捕捉每個(gè)輸入的數(shù)據(jù)并將其放入狀態(tài),這樣就在 handleSubmit中的使用數(shù)據(jù)抒线。

import React from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

export class ToDoForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = {value: ''};
  
      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }
  
    handleChange(event) {
      this.setState({value: event.target.value});
    }
  
    handleSubmit(event) {
      alert('A name was submitted: ' + this.state.value);
      event.preventDefault();
    }
  
    render() {
      return (
          <div className="todoform">
            <Form>
                <Form.Group as={Row} controlId="formHorizontalEmail">
                    <Form.Label column sm={2}>
                    <span className="item">Item</span>
                    </Form.Label>
                    <Col sm={5}>
                        <Form.Control type="text" placeholder="Todo Item" />
                    </Col>
                    <Col sm={5}>
                        <Button variant="primary" type="submit">Add</Button>
                    </Col>
                </Form.Group>
            </Form>
         </div>
      );
    }
  }

非受控組件

大多數(shù)情況下班巩,建議使用受控組件。有一種稱為非受控組件的方法可以通過使用Ref來處理表單數(shù)據(jù)。在非受控組件中抱慌,Ref用于直接從DOM訪問表單值逊桦,而不是事件處理程序。

我們使用Ref構(gòu)建了相同的表單抑进,而不是使用React狀態(tài)强经。 我們使用React.createRef() 定義Ref并傳遞該輸入表單并直接從handleSubmit方法中的DOM訪問表單值。

import React from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

export class ToDoForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = {value: ''};
      this.input = React.createRef();
  
      this.handleSubmit = this.handleSubmit.bind(this);
    }
  
    handleSubmit(event) {
      alert('A name was submitted: ' + this.input.current.value);
      event.preventDefault();
    }
  
    render() {
      return (
          <div className="todoform">
            <Form>
                <Form.Group as={Row} controlId="formHorizontalEmail">
                    <Form.Label column sm={2}>
                    <span className="item">Item</span>
                    </Form.Label>
                    <Col sm={5}>
                        <Form.Control type="text" placeholder="Todo Item" ref={this.input}/>
                    </Col>
                    <Col sm={5}>
                        <Button variant="primary" onClick={this.handleSubmit} type="submit">Add</Button>
                    </Col>
                </Form.Group>
            </Form>
         </div>
      );
    }
  }

容器組件

容器組件是處理獲取數(shù)據(jù)寺渗、訂閱 redux 存儲(chǔ)等的組件匿情。它們包含展示組件和其他容器組件,但是里面從來沒有html信殊。

高階組件

高階組件是將組件作為參數(shù)并生成另一個(gè)組件的組件炬称。 Redux connect是高階組件的示例。 這是一種用于生成可重用組件的強(qiáng)大技術(shù)涡拘。

Props 和 State

Props 是只讀屬性玲躯,傳遞給組件以呈現(xiàn)UI和狀態(tài),我們可以隨時(shí)間更改組件的輸出鳄乏。

下面是一個(gè)類組件的示例跷车,它在構(gòu)造函數(shù)中定義了propsstate,每當(dāng)使用this.setState() 修改狀態(tài)時(shí)汞窗,將再次調(diào)用 render( ) 函數(shù)來更改UI中組件的輸出姓赤。

import React from 'react';
import '../App.css';

export class Dashboard extends React.Component {

  constructor(props){
    super(props);

    this.state = {
        name: "some name"
    }
  }

  render() {

    // reading state
    const name = this.state.name;

    //reading props
    const address = this.props.address;

    return (
      <div className="dashboard"> 
          {name}
          {address}
      </div>
    );
  }
}

什么是PropTypes

隨著時(shí)間的推移,應(yīng)用程序會(huì)變得越來越大仲吏,因此類型檢查非常重要。PropTypes為組件提供類型檢查蝌焚,并為其他開發(fā)人員提供很好的文檔裹唆。如果react項(xiàng)目不使用 Typescript,建議為組件添加 PropTypes只洒。

如果組件沒有收到任何 props许帐,我們還可以為每個(gè)組件定義要顯示的默認(rèn) props。這里有一個(gè)例子毕谴。UserDisplay有三個(gè) prop:name成畦、addressage,我們正在為它們定義默認(rèn)的props 和 prop類型涝开。

import React from 'react';
import PropTypes from 'prop-types';

export const UserDisplay = ({name, address, age}) => {

    UserDisplay.defaultProps = {
        name: 'myname',
        age: 100,
        address: "0000 onestreet"
    };

    return (
        <>
            <div>
                <div class="label">Name:</div>
                <div>{name}</div>
            </div>
            <div>
                <div class="label">Address:</div>
                <div>{address}</div>
            </div>
            <div>
                <div class="label">Age:</div>
                <div>{age}</div>
            </div>
        </>
    )
}

UserDisplay.propTypes = {
    name: PropTypes.string.isRequired,
    address: PropTypes.objectOf(PropTypes.string),
    age: PropTypes.number.isRequired
}

如何更新狀態(tài)以及如何不更新

你不應(yīng)該直接修改狀態(tài)循帐。可以在構(gòu)造函數(shù)中定義狀態(tài)值舀武。直接使用狀態(tài)不會(huì)觸發(fā)重新渲染拄养。React 使用this.setState()時(shí)合并狀態(tài)。

//  錯(cuò)誤方式
this.state.name = "some name"
//  正確方式
this.setState({name:"some name"})

使用this.setState()的第二種形式總是更安全的银舱,因?yàn)楦碌膒rops和狀態(tài)是異步的瘪匿。這里跛梗,我們根據(jù)這些 props 更新狀態(tài)。

// 錯(cuò)誤方式
this.setState({
    timesVisited: this.state.timesVisited + this.props.count
})
// 正確方式
this.setState((state, props) => {
    timesVisited: state.timesVisited + props.count
});

組件生命周期方法

組件在進(jìn)入和離開DOM時(shí)要經(jīng)歷一系列生命周期方法棋弥,下面是這些生命周期方法核偿。

componentWillMount()

在渲染前調(diào)用,在客戶端也在服務(wù)端,它只發(fā)生一次顽染。

componentDidMount()

在第一次渲染后調(diào)用漾岳,只在客戶端。之后組件已經(jīng)生成了對應(yīng)的DOM結(jié)構(gòu)家乘,可以通過this.getDOMNode()來進(jìn)行訪問蝗羊。 如果你想和其他JavaScript框架一起使用,可以在這個(gè)方法中調(diào)用setTimeout, setInterval或者發(fā)送AJAX請求等操作(防止異部操作阻塞UI)仁锯。

componentWillReceiveProps()

在組件接收到一個(gè)新的 prop (更新后)時(shí)被調(diào)用耀找。這個(gè)方法在初始化render時(shí)不會(huì)被調(diào)用。

shouldComponentUpdate()

返回一個(gè)布爾值业崖。在組件接收到新的props或者state時(shí)被調(diào)用野芒。在初始化時(shí)或者使用forceUpdate時(shí)不被調(diào)用。 可以在你確認(rèn)不需要更新組件時(shí)使用双炕。

componentWillUpdate()

在組件接收到新的props或者state但還沒有render時(shí)被調(diào)用狞悲。在初始化時(shí)不會(huì)被調(diào)用。

componentDidUpdate()

在組件完成更新后立即調(diào)用妇斤。在初始化時(shí)不會(huì)被調(diào)用摇锋。

componentWillUnMount()

件從 DOM 中移除的時(shí)候立刻被調(diào)用。

getDerivedStateFromError()

這個(gè)生命周期方法在ErrorBoundary類中使用站超。實(shí)際上荸恕,如果使用這個(gè)生命周期方法,任何類都會(huì)變成ErrorBoundary死相。這用于在組件樹中出現(xiàn)錯(cuò)誤時(shí)呈現(xiàn)回退UI融求,而不是在屏幕上顯示一些奇怪的錯(cuò)誤。

componentDidCatch()

這個(gè)生命周期方法在ErrorBoundary類中使用算撮。實(shí)際上生宛,如果使用這個(gè)生命周期方法,任何類都會(huì)變成ErrorBoundary肮柜。這用于在組件樹中出現(xiàn)錯(cuò)誤時(shí)記錄錯(cuò)誤陷舅。

超越繼承的組合

在React中,我們總是使用組合而不是繼承素挽。我們已經(jīng)在函數(shù)式編程部分討論了什么是組合蔑赘。這是一種結(jié)合簡單的可重用函數(shù)來生成高階組件的技術(shù)。下面是一個(gè)組合的例子,我們在 dashboard 組件中使用兩個(gè)小組件todoFormtodoList缩赛。

import React from 'react';
import '../App.css';
import { ToDoForm } from './todoform';
import { ToDolist } from './todolist';

export class Dashboard extends React.Component {

  render() {
    return (
      <div className="dashboard"> 
          <ToDoForm />
          <ToDolist />
      </div>
    );
  }
}

如何在React中應(yīng)用樣式

將樣式應(yīng)用于React組件有三種方法耙箍。

外部樣式表

在此方法中,你可以將外部樣式表導(dǎo)入到組件使用類中酥馍。 但是你應(yīng)該使用className而不是class來為React元素應(yīng)用樣式, 這里有一個(gè)例子辩昆。

import React from 'react';
import './App.css';
import { Header } from './header/header';
import { Footer } from './footer/footer';
import { Dashboard } from './dashboard/dashboard';
import { UserDisplay } from './userdisplay';

function App() {
  return (
    <div className="App">
      <Header />
      <Dashboard />
      <UserDisplay />
      <Footer />
    </div>
  );
}

export default App;

內(nèi)聯(lián)樣式

在這個(gè)方法中,我們可以直接將 props 傳遞給HTML元素旨袒,屬性為style帮碰。這里有一個(gè)例子哥遮。這里需要注意的重要一點(diǎn)是渡处,我們將javascript對象傳遞給style容劳,這就是為什么我們使用 backgroundColor 而不是CSS方法backbackground -color

import React from 'react';

export const Header = () => {

    const heading = 'TODO App'

    return(
        <div style={{backgroundColor:'orange'}}>
            <h1>{heading}</h1>
        </div>
    )
}

定義樣式對象并使用它

因?yàn)槲覀儗avascript對象傳遞給style屬性必孤,所以我們可以在組件中定義一個(gè)style對象并使用它猾骡。下面是一個(gè)示例,你也可以將此對象作為 props 傳遞到組件樹中敷搪。

import React from 'react';

const footerStyle = {
    width: '100%',
    backgroundColor: 'green',
    padding: '50px',
    font: '30px',
    color: 'white',
    fontWeight: 'bold'
}

export const Footer = () => {
    return(
        <div style={footerStyle}>
            All Rights Reserved 2019
        </div>
    )
}

什么是Redux及其工作原理

Redux 是 React的一個(gè)狀態(tài)管理庫兴想,它基于flux。 Redux簡化了React中的單向數(shù)據(jù)流赡勘。 Redux將狀態(tài)管理完全從React中抽象出來嫂便。

它是如何工作的

在React中,組件連接到 redux 闸与,如果要訪問 redux毙替,需要派出一個(gè)包含 id和負(fù)載(payload) 的 action。action 中的 payload 是可選的践樱,action 將其轉(zhuǎn)發(fā)給 Reducer蔚龙。

當(dāng)reducer收到action時(shí),通過 swithc...case 語法比較 actiontype映胁。 匹配時(shí),更新對應(yīng)的內(nèi)容返回新的 state甲雅。

當(dāng)Redux狀態(tài)更改時(shí)解孙,連接到Redux的組件將接收新的狀態(tài)作為props。當(dāng)組件接收到這些props時(shí)抛人,它將進(jìn)入更新階段并重新渲染 UI弛姜。

Redux 循環(huán)細(xì)節(jié)

讓我們詳細(xì)看看整個(gè)redux 循環(huán)細(xì)節(jié)。

Action: Action 只是一個(gè)簡單的json對象妖枚,type 和有payload作為鍵廷臼。type 是必須要有的,payload是可選的。下面是一個(gè) action 的例子荠商。

// action

{ 
  type:"SEND_EMAIL", 
  payload: data
};

Action Creators:這些是創(chuàng)建Actions的函數(shù)寂恬,因此我們在派發(fā)action時(shí)不必在組件中手動(dòng)編寫每個(gè) action。 以下是 action creator 的示例莱没。

// action creator

export function sendEamil(data) {
    return { type:"SEND_EMAIL", payload: data};
}

Reducers:Reducers 是純函數(shù)初肉,它將 action和當(dāng)前 state 作為參數(shù),計(jì)算必要的邏輯并返回一個(gè)新r的state。 這些 Reducers 沒有任何副作用饰躲。 它不會(huì)改變 state 而是總是返回 state 牙咏。

export default function emailReducer(state = [], action){
 
  switch(action.type) {
      case "SEND_EMAIL":  return Object.assign({}, state, {
       email: action.payload
      });
      default: return state;
  }
}

組件如何與 redux 進(jìn)行連接

mapStateToProps:此函數(shù)將state映射到 props 上,因此只要state發(fā)生變化嘹裂,新 state 會(huì)重新映射到 props妄壶。 這是訂閱store的方式。

mapDispatchToProps:此函數(shù)用于將 action creators 綁定到你的props 寄狼。以便我們可以在第12行中使用This . props.actions.sendemail()來派發(fā)一個(gè)動(dòng)作丁寄。

connectbindActionCreators來自 redux。 前者用于連接 store 例嘱,如第22行狡逢,后者用于將 action creators 綁定到你的 props ,如第20行拼卵。

// import connect
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'

// import action creators
import * as userActions from '../../../actions/userActions';

export class User extends React.Component {
  
    handleSubmit() {
        // dispatch an action
        this.props.actions.sendEmail(this.state.email);
    }
  
}

// you are mapping you state props
const mapStateToProps = (state, ownProps) => ({user: state.user})
// you are binding your action creators to your props
const mapDispatchToProps = (dispatch) => ({actions: bindActionCreators(userActions, dispatch)})

export default connect(mapStateToProps, mapDispatchToProps)(User);

什么是 React Router Dom 及其工作原理

react-router-dom是應(yīng)用程序中路由的庫奢浑。 React庫中沒有路由功能,需要單獨(dú)安裝react-router-dom腋腮。

react-router-dom 提供兩個(gè)路由器BrowserRouterHashRoauter雀彼。前者基于rul的pathname段,后者基于hash段即寡。

 前者:http://127.0.0.1:3000/article/num1

 后者:http://127.0.0.1:3000/#/article/num1(不一定是這樣徊哑,但#是少不了的)

react-router-dom 組件

  • BrowserRouterHashRouter 是路由器。
  • Route 用于路由匹配聪富。
  • Link 組件用于在應(yīng)用程序中創(chuàng)建鏈接莺丑。 它將在HTML中渲染為錨標(biāo)記。
  • NavLink是突出顯示當(dāng)前活動(dòng)鏈接的特殊鏈接墩蔓。
  • Switch 不是必需的梢莽,但在組合路由時(shí)很有用。
  • Redirect 用于強(qiáng)制路由重定向

下面是組件中的Link奸披、NavLinkRedirect 的例子

// normal link
<Link to="/gotoA">Home</Link>

// link which highlights currentlu active route with the given class name
<NavLink to="/gotoB" activeClassName="active">
  React
</NavLink>

// you can redirect to this url
<Redirect to="/gotoC" />

以下是 react router 組件的示例昏名。 如果你查看下面的示例,我們將匹配路徑并使用SwitchRoute呈現(xiàn)相應(yīng)的組件阵面。

import React from 'react'
// import react router DOM elements
import { Switch, Route, Redirect } from 'react-router-dom'
import ComponentA from '../common/compa'
import ComponentB from '../common/compb'
import ComponentC from '../common/compc'
import ComponentD from '../common/compd'
import ComponentE from '../common/compe'


const Layout = ({ match }) => {
    return(
        <div className="">
            <Switch>
                <Route exact path={`${match.path}/gotoA`} component={ComponentA} />
                <Route path={`${match.path}/gotoB`} component={ComponentB} />
                <Route path={`${match.path}/gotoC`} component={ComponentC} />
                <Route path={`${match.path}/gotoD`} component={ComponentD} />
                <Route path={`${match.path}/gotoE`} component={ComponentE} />
            </Switch>
        </div>
    )}

export default Layout

什么是錯(cuò)誤邊界

在 React 中轻局,我們通常有一個(gè)組件樹洪鸭。如果任何一個(gè)組件發(fā)生錯(cuò)誤,它將破壞整個(gè)組件樹仑扑。沒有辦法捕捉這些錯(cuò)誤览爵,我們可以用錯(cuò)誤邊界優(yōu)雅地處理這些錯(cuò)誤。

錯(cuò)誤邊界有兩個(gè)作用

  • 如果發(fā)生錯(cuò)誤夫壁,顯示回退UI
  • 記錄錯(cuò)誤

下面是ErrorBoundary類的一個(gè)例子拾枣。如果類實(shí)現(xiàn)了 getDerivedStateFromErrorcomponentDidCatch 這兩個(gè)生命周期方法的任何一下,盒让,那么這個(gè)類就會(huì)成為ErrorBoundary梅肤。前者返回{hasError: true}來呈現(xiàn)回退UI,后者用于記錄錯(cuò)誤邑茄。

import React from 'react'

export class ErrorBoundary extends React.Component {
    constructor(props) {
      super(props);
      this.state = { hasError: false };
    }
  
    static getDerivedStateFromError(error) {
      // Update state so the next render will show the fallback UI.
      return { hasError: true };
    }
  
    componentDidCatch(error, info) {
      // You can also log the error to an error reporting service
      console.log('Error::::', error);
    }
  
    render() {
      if (this.state.hasError) {
        // You can render any custom fallback UI
        return <h1>OOPS!. WE ARE LOOKING INTO IT.</h1>;
      }
  
      return this.props.children; 
    }
  }

以下是我們?nèi)绾卧谄渲幸粋€(gè)組件中使用ErrorBoundary姨蝴。使用ErrorBoundary類包裹 ToDoFormToDoList。 如果這些組件中發(fā)生任何錯(cuò)誤肺缕,我們會(huì)記錄錯(cuò)誤并顯示回退UI左医。

import React from 'react';
import '../App.css';
import { ToDoForm } from './todoform';
import { ToDolist } from './todolist';
import { ErrorBoundary } from '../errorboundary';

export class Dashboard extends React.Component {

  render() {
    return (
      <div className="dashboard"> 
        <ErrorBoundary>
          <ToDoForm />
          <ToDolist />
        </ErrorBoundary>
      </div>
    );
  }
}

什么是 Fragments

在React中,我們需要有一個(gè)父元素同木,同時(shí)從組件返回React元素浮梢。有時(shí)在DOM中添加額外的節(jié)點(diǎn)會(huì)很煩人。使用 Fragments彤路,我們不需要在DOM中添加額外的節(jié)點(diǎn)秕硝。我們只需要用 React.Fragment 或才簡寫 <> 來包裹內(nèi)容就行了。如下 所示:

 // Without Fragments   
return (
    <div>
       <CompoentA />
       <CompoentB />
       <CompoentC />
    </div>
)

// With Fragments   
  return (
    <React.Fragment>
       <CompoentA />
       <CompoentB />
       <CompoentC />
    </React.Fragment>
  )

  // shorthand notation Fragments   
  return (
    <>
       <CompoentA />
       <CompoentB />
       <CompoentC />
    </>
  )

什么是傳送門(Portals)

默認(rèn)情況下洲尊,所有子組件都在UI上呈現(xiàn)远豺,具體取決于組件層次結(jié)構(gòu)。Portal 提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的 DOM 節(jié)點(diǎn)的優(yōu)秀的方案坞嘀。

這里有一個(gè)例子躯护。默認(rèn)情況下,父組件在DOM層次結(jié)構(gòu)中有子組件丽涩。

我們可以將 children 組件移出parent 組件并將其附加 idsomeid 的 Dom 節(jié)點(diǎn)下棺滞。

首先,獲取 id 為 someid矢渊,我們在constrcutorand中創(chuàng)建一個(gè)元素div检眯,將child附加到componentDidMount中的someRoot。 最后昆淡,我們在ReactDOM.createPortal(this.props.childen),domnode的幫助下將子節(jié)點(diǎn)傳遞給該特定DOM節(jié)點(diǎn)刽严。

首先昂灵,先獲取 id 為someid DOM元素避凝,接著在構(gòu)造函數(shù)中創(chuàng)建一個(gè)元素div,在 componentDidMount方法中將 someRoot 放到 div 中 眨补。 最后管削,通過
ReactDOM.createPortal(this.props.childen), domnode)children 傳遞到對應(yīng)的節(jié)點(diǎn)下。

const someRoot = document.getElementById('someid');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    someRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    someRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

什么是上下文

有時(shí)我們必須將props 傳遞給組件樹撑螺,即使所有中間組件都不需要這些props 含思。上下文是一種傳遞props 的方法,而不用在每一層傳遞組件樹甘晤。

什么是 Hooks

Hooks 是React版本16.8中的新功能含潘。 請記住,我們不能在函數(shù)組件中使用state 线婚,因?yàn)樗鼈儾皇穷惤M件遏弱。Hooks 讓我們在函數(shù)組件中可以使用state 和其他功能。

目前沒有重大變化塞弊,我們不必放棄類組件漱逸。

Hook 不會(huì)影響你對 React 概念的理解。 恰恰相反游沿,Hook 為已知的 React 概念提供了更直接的 API:props饰抒, state,context诀黍,refs 以及生命周期袋坑。稍后我們將看到,Hook 還提供了一種更強(qiáng)大的方式來組合他們蔗草。

我們可以使用一些鉤子咒彤,例如useState,useEffect咒精,useContext镶柱,useReducer等。

下面是 Hooks 的基本規(guī)則

  • Hooks 應(yīng)該在外層使用模叙,不應(yīng)該在循環(huán)歇拆,條件或嵌套函數(shù)中使用
  • Hooks 應(yīng)該只在函數(shù)組件中使用。

讓我們看一個(gè)例子來理解 hooks范咨。 這是一個(gè)函數(shù)組件故觅,它采用props并在UI上顯示這些props。 在useState鉤子的幫助下渠啊,我們將這個(gè)函數(shù)組件轉(zhuǎn)換為有狀態(tài)組件输吏。 首先,我們在第5行定義狀態(tài)替蛉,這相當(dāng)于

constructor(props) {
 super(props);
 this.state = {
     name:'myname', age:10, address:'0000 one street'
 }
}

useState返回兩個(gè)項(xiàng)贯溅,一個(gè)是user拄氯,另一個(gè)是setUser函數(shù)。 user 是一個(gè)可以在沒有 this關(guān)鍵字的情況下直接使用的對象它浅,setUser是一個(gè)可以用來設(shè)置用戶點(diǎn)擊第21行按鈕的狀態(tài)的函數(shù)译柏,該函數(shù)等效于以下內(nèi)容。

this.setState({name:'name changed'})
import React, { useState } from "react";

export const UserDisplay = ({ name, address, age }) => {
    const [user, setUser] = useState({
        name: "myname",
        age: 10,
        address: "0000 onestreet"
    });

    return (
        <>
            <div>
                <div class="label">Name:</div>
                <div>{user.name}</div>
            </div>
            <div>
                <div class="label">Address:</div>
                <div>{user.address}</div>
            </div>
            <div>
                <div class="label">Age:</div>
                <div>{user.age}</div>
            </div>
            <button onClick={() => setUser({ name: "name changed" })}>
                Click me
            </button>
        </>
    );
};

如何提高性能

我們可以通過多種方式提高應(yīng)用性能姐霍,以下這些比較重要:

  • 適當(dāng)?shù)厥褂?code>shouldComponentUpdate生命周期方法鄙麦。 它避免了子組件的不必要的渲染。 如果樹中有100個(gè)組件镊折,則不重新渲染整個(gè)組件樹來提高應(yīng)用程序性能胯府。
  • 使用create-react-app來構(gòu)建項(xiàng)目,這會(huì)創(chuàng)建整個(gè)項(xiàng)目結(jié)構(gòu)腌乡,并進(jìn)行大量優(yōu)化盟劫。
  • 不可變性是提高性能的關(guān)鍵。不要對數(shù)據(jù)進(jìn)行修改与纽,而是始終在現(xiàn)有集合的基礎(chǔ)上創(chuàng)建新的集合侣签,以保持盡可能少的復(fù)制,從而提高性能急迂。
  • 在顯示列表或表格時(shí)始終使用 Keys影所,這會(huì)讓 React 的更新速度更快
  • 代碼分離是將代碼插入到單獨(dú)的文件中,只加載模塊或部分所需的文件的技術(shù)僚碎。

如何在重新加載頁面時(shí)保留數(shù)據(jù)

單頁應(yīng)用程序首先在DOM中加載index.html猴娩,然后在用戶瀏覽頁面時(shí)加載內(nèi)容,或者從同一index.html中的后端API獲取任何數(shù)據(jù)勺阐。

如果通過點(diǎn)擊瀏覽器中的重新加載按鈕重新加載頁面index.html卷中,整個(gè)React應(yīng)用程序?qū)⒅匦录虞d,我們將丟失應(yīng)用程序的狀態(tài)渊抽。 如何保留應(yīng)用狀態(tài)蟆豫?

每當(dāng)重新加載應(yīng)用程序時(shí),我們使用瀏覽器localstorage來保存應(yīng)用程序的狀態(tài)懒闷。我們將整個(gè)存儲(chǔ)數(shù)據(jù)保存在localstorage中十减,每當(dāng)有頁面刷新或重新加載時(shí),我們從localstorage加載狀態(tài)愤估。

如何在React進(jìn)行API調(diào)用

我們使用redux-thunk在React中調(diào)用API帮辟。因?yàn)?code>reduce是純函數(shù),所以沒有副作用玩焰,比如調(diào)用API由驹。

因此,我們必須使用redux-thunk從 action creators 那里進(jìn)行 API 調(diào)用昔园。Action creator 派發(fā)一個(gè)action荔棉,將來自API的數(shù)據(jù)放入action 的 payload 中闹炉。Reducers 接收我們在上面的redux循環(huán)中討論的數(shù)據(jù),其余的過程也是相同的润樱。

redux-thunk是一個(gè)中間件。一旦它被引入到項(xiàng)目中羡棵,每次派發(fā)一個(gè)action時(shí)壹若,都會(huì)通過thunk傳遞。如果它是一個(gè)函數(shù)皂冰,它只是等待函數(shù)處理并返回響應(yīng)店展。如果它不是一個(gè)函數(shù),它只是正常處理秃流。

這里有一個(gè)例子赂蕴。sendEmailAPI是從組件中調(diào)用的函數(shù),它接受一個(gè)數(shù)據(jù)并返回一個(gè)函數(shù)舶胀,其中dispatch作為參數(shù)概说。我們使用redux-thunk調(diào)用API apiservice,并等待收到響應(yīng)嚣伐。一旦接收到響應(yīng)糖赔,我們就使用payload 派發(fā)一個(gè)action

import apiservice from '../services/apiservice';

export function sendEmail(data) {
    return { type:"SEND_EMAIL", payload: data };
}

export function sendEmailAPI(email) {
    return function(dispatch) {
        return apiservice.callAPI(email).then(data => {
            dispatch(sendEmail(data));
        });
    }
}

總結(jié)

要想有把握的面試轩端,必須充分了解上述所有主題放典。 即使你目前正在使用React,理解這些概念也能增強(qiáng)你在職場中信心基茵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奋构,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拱层,更是在濱河造成了極大的恐慌弥臼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舱呻,死亡現(xiàn)場離奇詭異醋火,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)箱吕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門芥驳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茬高,你說我怎么就攤上這事兆旬。” “怎么了怎栽?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵丽猬,是天一觀的道長宿饱。 經(jīng)常有香客問我,道長脚祟,這世上最難降的妖魔是什么谬以? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮由桌,結(jié)果婚禮上为黎,老公的妹妹穿的比我還像新娘。我一直安慰自己行您,他們只是感情好铭乾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娃循,像睡著了一般炕檩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捌斧,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天笛质,我揣著相機(jī)與錄音,去河邊找鬼骤星。 笑死经瓷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洞难。 我是一名探鬼主播舆吮,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼队贱!你這毒婦竟也來了色冀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤柱嫌,失蹤者是張志新(化名)和其女友劉穎锋恬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體编丘,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡与学,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘉抓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片索守。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抑片,靈堂內(nèi)的尸體忽然破棺而出卵佛,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布截汪,位于F島的核電站疾牲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衙解。R本人自食惡果不足惜阳柔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚓峦。 院中可真熱鬧盔沫,春花似錦、人聲如沸枫匾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽干茉。三九已至,卻和暖如春很泊,著一層夾襖步出監(jiān)牢的瞬間角虫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工委造, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戳鹅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓昏兆,卻偏偏與公主長得像枫虏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子爬虱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348