ReactJS簡介

1、ReactJS簡介

React 起源于 Facebook 的內(nèi)部項(xiàng)目,因?yàn)樵摴緦?duì)市場(chǎng)上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設(shè) Instagram 的網(wǎng)站。做出來以后,發(fā)現(xiàn)這套東西很好用,就在2013年5月開源了。由于 React 的設(shè)計(jì)思想極其獨(dú)特,屬于革命性創(chuàng)新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關(guān)注和使用,認(rèn)為它可能是將來 Web 開發(fā)的主流工具。

2、ReactJS的背景和原理

在Web開發(fā)中,我們總需要將變化的數(shù)據(jù)實(shí)時(shí)反應(yīng)到UI上,這時(shí)就需要對(duì)DOM進(jìn)行操作。而復(fù)雜或頻繁的DOM操作通常是性能瓶頸產(chǎn)生的原因(如何進(jìn)行高性能的復(fù)雜DOM操作通常是衡量一個(gè)前端開發(fā)人員技能的重要指標(biāo))。

React為此引入了虛擬DOM(Virtual DOM)的機(jī)制:在瀏覽器端用Javascript實(shí)現(xiàn)了一套DOM API?;赗eact進(jìn)行開發(fā)時(shí)所有的DOM構(gòu)造都是通過虛擬DOM進(jìn)行,每當(dāng)數(shù)據(jù)變化時(shí),React都會(huì)重新構(gòu)建整個(gè)DOM樹,然后React將當(dāng)前整個(gè)DOM樹和上一次的DOM樹進(jìn)行對(duì)比,得到DOM結(jié)構(gòu)的區(qū)別,然后僅僅將需要變化的部分進(jìn)行實(shí)際的瀏覽器DOM更新。而且React能夠批處理虛擬DOM的刷新,在一個(gè)事件循環(huán)(Event Loop)內(nèi)的兩次數(shù)據(jù)變化會(huì)被合并,例如你連續(xù)的先將節(jié)點(diǎn)內(nèi)容從A變成B,然后又從B變成A,React會(huì)認(rèn)為UI不發(fā)生任何變化,而如果通過手動(dòng)控制,這種邏輯通常是極其復(fù)雜的。盡管每一次都需要構(gòu)造完整的虛擬DOM樹,但是因?yàn)樘摂MDOM是內(nèi)存數(shù)據(jù),性能是極高的,而對(duì)實(shí)際DOM進(jìn)行操作的僅僅是Diff部分,因而能達(dá)到提高性能的目的。這樣,在保證性能的同時(shí),開發(fā)者將不再需要關(guān)注某個(gè)數(shù)據(jù)的變化如何更新到一個(gè)或多個(gè)具體的DOM元素,而只需要關(guān)心在任意一個(gè)數(shù)據(jù)狀態(tài)下,整個(gè)界面是如何Render的。

如果你像在90年代那樣寫過服務(wù)器端Render的純Web頁面那么應(yīng)該知道,服務(wù)器端所要做的就是根據(jù)數(shù)據(jù)Render出HTML送到瀏覽器端。如果這時(shí)因?yàn)橛脩舻囊粋€(gè)點(diǎn)擊需要改變某個(gè)狀態(tài)文字,那么也是通過刷新整個(gè)頁面來完成的。服務(wù)器端并不需要知道是哪一小段HTML發(fā)生了變化,而只需要根據(jù)數(shù)據(jù)刷新整個(gè)頁面。換句話說,任何UI的變化都是通過整體刷新來完成的。而React將這種開發(fā)模式以高性能的方式帶到了前端,每做一點(diǎn)界面的更新,你都可以認(rèn)為刷新了整個(gè)頁面。至于如何進(jìn)行局部更新以保證性能,則是React框架要完成的事情。

借用Facebook介紹React的視頻中聊天應(yīng)用的例子,當(dāng)一條新的消息過來時(shí),你的開發(fā)過程需要知道哪條數(shù)據(jù)過來了,如何將新的DOM結(jié)點(diǎn)添加到當(dāng)前DOM樹上;而基于React的開發(fā)思路,你永遠(yuǎn)只需要關(guān)心數(shù)據(jù)整體,兩次數(shù)據(jù)之間的UI如何變化,則完全交給框架去做??梢钥吹?,使用React大大降低了邏輯復(fù)雜性,意味著開發(fā)難度降低,可能產(chǎn)生Bug的機(jī)會(huì)也更少。

3、組件化

虛擬DOM(virtual-dom)不僅帶來了簡單的UI開發(fā)邏輯,同時(shí)也帶來了組件化開發(fā)的思想,所謂組件,即封裝起來的具有獨(dú)立功能的UI部件。React推薦以組件的方式去重新思考UI構(gòu)成,將UI上每一個(gè)功能相對(duì)獨(dú)立的模塊定義成組件,然后將小的組件通過組合或者嵌套的方式構(gòu)成大的組件,最終完成整體UI的構(gòu)建。例如,F(xiàn)acebook的instagram.com整站都采用了React來開發(fā),整個(gè)頁面就是一個(gè)大的組件,其中包含了嵌套的大量其它組件。

如果說MVC的思想讓你做到視圖-數(shù)據(jù)-控制器的分離,那么組件化的思考方式則是帶來了UI功能模塊之間的分離。我們通過一個(gè)典型的Blog評(píng)論界面來看MVC和組件化開發(fā)思路的區(qū)別。

對(duì)于MVC開發(fā)模式來說,開發(fā)者將三者定義成不同的類,實(shí)現(xiàn)了表現(xiàn),數(shù)據(jù),控制的分離。開發(fā)者更多的是從技術(shù)的角度來對(duì)UI進(jìn)行拆分,實(shí)現(xiàn)松耦合。

對(duì)于React而言,則完全是一個(gè)新的思路,開發(fā)者從功能的角度出發(fā),將UI分成不同的組件,每個(gè)組件都獨(dú)立封裝。

在React中,你按照界面模塊自然劃分的方式來組織和編寫你的代碼,對(duì)于評(píng)論界面而言,整個(gè)UI是一個(gè)通過小組件構(gòu)成的大組件,每個(gè)組件只關(guān)心自己部分的邏輯,彼此獨(dú)立。


image.png

React認(rèn)為一個(gè)組件應(yīng)該具有如下特征:

(1)可組合(Composeable):一個(gè)組件易于和其它組件一起使用,或者嵌套在另一個(gè)組件內(nèi)部。如果一個(gè)組件內(nèi)部創(chuàng)建了另一個(gè)組件,那么說父組件擁有(own)它創(chuàng)建的子組件,通過這個(gè)特性,一個(gè)復(fù)雜的UI可以拆分成多個(gè)簡單的UI組件。

(2)可重用(Reusable):每個(gè)組件都是具有獨(dú)立功能的,它可以被使用在多個(gè)UI場(chǎng)景。

(3)可維護(hù)(Maintainable):每個(gè)小的組件僅僅包含自身的邏輯,更容易被理解和維護(hù)。

4、JSX簡介

HTML 語言直接寫在 JavaScript 語言之中,不加任何引號(hào),這就是 JSX(JavaScript and XML) 的語法,JSX,是一種 JavaScript 的語法擴(kuò)展,它允許 HTML 與 JavaScript 的混寫。JSX是facebook為React框架開發(fā)的一套語法糖,語法糖又叫做糖衣語法,是指計(jì)算機(jī)語言中添加的某種語法,這種語法對(duì)語言的功能并沒有影響,但是更方便程序員使用,它主要的目的是增加程序的可讀性,從而減少程序代碼錯(cuò)處的機(jī)會(huì)。JSX就是JS的一種語法糖,類似的還有CoffeeScript、TypeScript,最終它們都會(huì)被解析成JS才能被瀏覽器理解和執(zhí)行,如果不解析瀏覽器是沒有辦法識(shí)別它們的,這也是所有語法糖略有不足的地方。

const element = <h1>Hello, world!</h1>;

上面這種看起來可能有些奇怪的標(biāo)簽語法既不是字符串也不是HTML,被稱為 JSX,JSX帶來的一大便利就是我們可以直接在JS里面寫類DOM的結(jié)構(gòu),比我們用原生的JS去拼接字符串,然后再用正則替換等方式來渲染模板方便和簡單太多了。推薦在 React 中使用 JSX 來描述用戶界面。JSX 用來聲明 React 當(dāng)中的元素, 乍看起來可能比較像是模版語言,但事實(shí)上它完全是在 JavaScript 內(nèi)部實(shí)現(xiàn)的。

你可以任意地在 JSX 當(dāng)中使用 JavaScript 表達(dá)式,在 JSX 當(dāng)中的表達(dá)式要包含在大括號(hào)里。例子如下:

const names = ['Jack', 'Tom', 'Alice'];
const element = (
  <div>
     { names.map(function (name) { return <div>Hello, {name}!</div>}) }
  </div>
);

在書寫 JSX 的時(shí)候一般都會(huì)帶上換行和縮進(jìn),這樣可以增強(qiáng)代碼的可讀性。與此同時(shí),推薦在 JSX 代碼的外面擴(kuò)上一個(gè)小括號(hào),這樣可以防止 分號(hào)自動(dòng)插入 的bug。

上面我們聲明了一個(gè)names數(shù)組,然后遍歷names數(shù)組在前面加上Hello,生成了element數(shù)組。JSX 允許直接在模板插入 JavaScript 變量。如果這個(gè)變量是一個(gè)數(shù)組,則會(huì)展開這個(gè)數(shù)組的所有成員。JSX 本身其實(shí)也是一種表達(dá)式,在編譯之后,JSX 其實(shí)會(huì)被轉(zhuǎn)化為普通的 JavaScript 對(duì)象。代碼如下:

import React from 'react';
import ReactDOM from 'react-dom';

const names = ['Jack', 'Tom', 'Alice'];
const element = names.map(function (name) { return <div>Hello, {name}!</div>});

ReactDOM.render(
  element,
  document.getElementById('root')
);

顯示結(jié)果如下:


image.png

JSX屬性:

你可以使用引號(hào)來定義以字符串為值的屬性:

const element = <div tabIndex="0"></div>;

也可以使用大括號(hào)來定義以 JavaScript 表達(dá)式為值的屬性:

const element = <img src={user.avatarUrl}></img>;

切記當(dāng)使用了大括號(hào)包裹的 JavaScript 表達(dá)式時(shí)就不要再到外面套引號(hào)了。JSX 會(huì)將引號(hào)當(dāng)中的內(nèi)容識(shí)別為字符串而不是表達(dá)式

5、ReactJS組件

組件可以將UI切分成一些的獨(dú)立的、可復(fù)用的部件,這樣你就只需專注于構(gòu)建每一個(gè)單獨(dú)的部件。

組件從概念上看就像是函數(shù),它可以接收任意的輸入值(稱之為“props”),并返回一個(gè)需要在頁面上展示的React元素

定義一個(gè)組件最簡單的方式是使用JavaScript函數(shù),函數(shù)定義組件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

該函數(shù)是一個(gè)有效的React組件,它接收一個(gè)單一的“props”對(duì)象并返回了一個(gè)React元素。我們之所以稱這種類型的組件為函數(shù)定義組件,是因?yàn)閺淖置嫔蟻砜?,它就是一個(gè)JavaScript函數(shù)。

你也可以使用 ES6 class來定義一個(gè)組件,類定義組件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

上面兩個(gè)組件在React中是相同的。

ReactJS是基于組件化的開發(fā),React 允許將代碼封裝成組件(component),然后像插入普通 HTML 標(biāo)簽一樣,在網(wǎng)頁中插入這個(gè)組件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

定義組件時(shí)有幾點(diǎn)需要注意:

  1. 創(chuàng)建的組件名稱首字母必須大寫。

  2. 為元素添加css的class時(shí),要用className。

  3. 組件的style屬性的設(shè)置方式也值得注意,要寫成style={{width: this.state.witdh}}而不是style="opacity:{this.state.opacity};"。

  4. 組件的返回值只能有一個(gè)根元素。

組件的生命周期:如同人有生老病死,自然界有日月更替。每個(gè)組件在網(wǎng)頁中也會(huì)被創(chuàng)建、更新和刪除,如同有生命的機(jī)體一樣。

React嚴(yán)格定義了組件的生命周期,生命周期可能會(huì)經(jīng)歷如下三個(gè)過程:

  1. 裝載過程(Mount),也就是把組件第一次在DOM樹中渲染的過程;

  2. 更新過程(Update),當(dāng)組件被重新渲染的過程。

  3. 卸載過程(Unmount),組件從DOM中刪除的過程。

三種不同的過程,React庫會(huì)依次調(diào)用組件的一些成員函數(shù),這些函數(shù)稱為生命周期函數(shù)。所以,要定制一個(gè)React組件,實(shí)際上就是定制這些生命周期函數(shù)。

1、裝載過程(Mount):

裝載過程,當(dāng)組件第一次被渲染的時(shí)候,依次調(diào)用如下函數(shù):

  1. constructor

  2. getInitialState

  3. getDefaultProps

  4. componentWillMount

  5. render

  6. componentDidMount

2、更新過程(Update):

當(dāng)組件被裝載到DOM樹上之后,用戶在網(wǎng)頁上可以看到組件的第一印象,但是要提供更好的交互體驗(yàn),就要讓該組件可以隨著用戶操作改變展現(xiàn)的內(nèi)容,當(dāng)props或者state被修改的時(shí)候,就會(huì)引發(fā)組件的更新過程。

更新過程會(huì)依次調(diào)用下面的生命周期函數(shù),其中render函數(shù)和裝載過程一樣,沒有差別:

  1. componentWillReceiveProps

  2. shouldComponentUpdate

  3. componentWillUpdate

  4. render

  5. componentDidUpdate

注意:并不是所有的更新過程都會(huì)執(zhí)行全部函數(shù)。

3、卸載過程(Unmount)

React組件的卸載過程只涉及一個(gè)函數(shù)componentWillUnmount,當(dāng)React組件要從DOM樹上刪除掉之前,對(duì)應(yīng)的componentWillUnmount函數(shù)會(huì)被調(diào)用,所以這個(gè)函數(shù)適合做一些清理的工作。

和裝載過程與更新過程不一樣,這個(gè)函數(shù)沒有配對(duì)的Did函數(shù),就一個(gè)函數(shù),因?yàn)樾遁d完就完了,沒有“卸載完再做的事情”。

componentWillUnmount中的工作往往和componentDidMount有關(guān),比如,在componentDidMount中用非React的方法創(chuàng)造了一些DOM元素,如果撒手不管可能會(huì)造成內(nèi)存泄漏,那就需要在componentWillUnmount中把這些創(chuàng)造的DOM元素清理掉。

6、ReactJS小結(jié)

  1. ReactJs是基于組件化的開發(fā),所以最終你的頁面應(yīng)該是由若干個(gè)小組件組成的大組件。

  2. 可以通過屬性,將值傳遞到組件內(nèi)部,同理也可以通過屬性將內(nèi)部的結(jié)果傳遞到父級(jí)組件(留給大家研究);要對(duì)某些值的變化做DOM操作的,要把這些值放到state中。

  3. 為組件添加外部css樣式時(shí),類名應(yīng)該寫成className而不是class;添加內(nèi)部樣式時(shí),應(yīng)該是style={{opacity: this.state.opacity}}而不是style="opacity:{this.state.opacity};"。

  4. 組件名稱首字母必須大寫。

  5. 組件的返回值只能有一個(gè)根元素。

  6. 變量名用{}包裹,且不能加雙引號(hào)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容