? ? ? ? 我們先來直觀認識React蠢挡,對任何而一種工具弧岳,只有使用才能夠熟練掌握,React也不例外业踏。通過多React快速入手禽炬,我們會解析React的工作原理,并通過與功能相同的jQuery程序?qū)Ρ惹诩遥瑥亩闯鯮eact的特點:
? ? 如何初始化一個React項目
? ? 如何創(chuàng)建一個React組件
? ? React的工作方式腹尖。
? ? 讓我們開始旅程把!
1.1初始化一個React項目
? ? ? 為了開發(fā)React應(yīng)用伐脖,你的電腦是運行微軟Windows操作系統(tǒng)热幔,還是蘋果Mac,或者是linux讼庇,都不重要绎巨,只要保證具備以下條件:
? ? 安裝了瀏覽器,如果是windows操作系統(tǒng)蠕啄,請保證微軟IE瀏覽器版本不低于8.0认烁,因為React不支持比IE8更低版本的瀏覽器。
? ? 有一個命令環(huán)境介汹,在Windows操作系統(tǒng)中有命令行界面却嗡,在蘋果Mac電腦中可以使用Terminal應(yīng)用,對于linux環(huán)境嘹承,命令行環(huán)境我想不用過多解釋窗价;
? ? 一個你最喜歡的代碼編輯器,用于編輯React應(yīng)用的代碼叹卷,本教程注重實踐撼港,只有實際編碼才能深入體會。
? ? ? ? 作為開發(fā)者骤竹,推薦使用谷歌Chrom瀏覽器帝牡,因為Chrom瀏覽器自帶的開發(fā)輔助工具非常友好,而且還可以安裝輔助React和Redux的擴展工具蒙揣,具體的開發(fā)工具在第四章4.2中有詳細介紹靶溜。
? ? ? React是一個JavaScript語言的工具庫,在這個JavaScript工具鋪天蓋地的時代懒震,沒有意外罩息,你需要安裝Node.js,React本身并不依賴與Node.js个扰,但是我們開發(fā)中用到的諸多工具需要Node.js的支持瓷炮。
? ? ? ? ? ? 在Node.js的官網(wǎng)(https://nodejs.org/)可以找到合適的安裝方式,安裝Node.js的同時也就安裝了npm递宅,npm是Node.js的安裝包管理工具娘香,因為我們不可能自己開發(fā)所有功能,會大量使用現(xiàn)有的安裝包办龄,就需要npm的幫助烘绽。
1.1.1create-react-app工具
? ? ? ? React技術(shù)依賴與一個很龐大的技術(shù)棧,比如土榴,轉(zhuǎn)譯JavaScript代碼需要使用Babel诀姚,模塊打包工具又要使用Webpack,定制build過程需要grunt或者gulp……這些技術(shù)棧都需要各自的配置文件玷禽,還沒有開始寫一行React相關(guān)代碼赫段,開發(fā)人員就已經(jīng)被各種技術(shù)名詞淹沒。
? ? ? ? ? ? 針對這種情況矢赁,React的創(chuàng)建者Facebook提供了一個快速開發(fā)React應(yīng)用工具糯笙,名叫create-react-app,這個工具的目的是將開發(fā)人員從配置工作中解脫出來撩银,無需過早關(guān)注這些技術(shù)細節(jié)给涕,通過創(chuàng)建一個已經(jīng)完成基本配置的應(yīng)用,讓開發(fā)者快速開始React應(yīng)用的開發(fā)。
? ? ? ? ? ? 本書中所有應(yīng)用實例都有create-react-app創(chuàng)建够庙,我們用這種最簡單的方式創(chuàng)建可運行的運用恭应,必要的時候才會介紹底層技術(shù)棧的細節(jié),畢竟耘眨,沒有聲明比一個能運行的應(yīng)用更加增強開發(fā)者的信心昼榛。
? ? ? ? 本書中所有應(yīng)用實例都有create-react-app創(chuàng)建,我們用這種最簡單的方式創(chuàng)建可運行的運用剔难,必要的時候才會介紹底層技術(shù)棧的細節(jié)胆屿,畢竟,沒有聲明比一個能運行的應(yīng)用更加增強開發(fā)者的信心偶宫。
? ? ? ? crate-react-app是一個通過npm發(fā)布的安裝包非迹,在確認Node.js和npm安裝好之后,命令行中執(zhí)行下面的命令安裝create-react-app:
? ? npm install --global create-react-app
? ? 安裝過程結(jié)束之后纯趋,你的電腦就會有create-react-app這樣一個可以執(zhí)行的命令憎兽,這個命令會在當(dāng)前目錄下創(chuàng)建指定參數(shù)名的應(yīng)用目錄。
? ? 我們在命令行中執(zhí)行下面的命令:
? ? create-react-app first_react_app
? ? ? ? 這個命令會在當(dāng)前目錄創(chuàng)建一個名為first_react_app的目錄结闸,在這個目錄中會自動添加一個應(yīng)用的框架唇兑,隨后我們只需要在這個框架的基礎(chǔ)上修改文件就可以開發(fā)React應(yīng)用,避免了大量的手工配置文件桦锄。
? ? ? ? 在creat-react-app命令一大段文字輸出之后扎附,根據(jù)提示輸入下面的命令:
? ? cd first_react_app
? ? npm start
? ? 這個命令啟動一個開發(fā)模式的服務(wù)器,同時也會讓你的瀏覽器自動打開了一個網(wǎng)頁结耀,指向本地地址http://localhost:3000/留夜。
? ? 恭喜你,你的第一個React應(yīng)用誕生了图甜!
? ? 接下來碍粥,我們會用React開發(fā)一個簡單的功能,讓我們繼續(xù)吧黑毅。
1.2增加一個新的React組件
? ? ? ? React的首要思想是通過組件(Component)來開發(fā)應(yīng)用嚼摩。所謂組件,指的是能完成某個特定功能的獨立的矿瘦、可重用的代碼枕面。
? ? ? ? 基于組件的應(yīng)用開發(fā)是廣泛使用的軟件開發(fā)模式,用分而治之的方法缚去,把一個大的應(yīng)用分解成若干小的組件潮秘,每個組件只關(guān)注于某個小范圍的特定功能,但是把組件組合起來易结,就能夠構(gòu)成一個功能龐大的應(yīng)用枕荞。如果分解功能的過程足夠巧妙柜候,那么每個組件可以在不同場景下重用,那樣不光可以構(gòu)建龐大的應(yīng)用躏精,還可以構(gòu)建出靈活的應(yīng)用渣刷。打個比方,每個組件是一塊磚玉控,而一個應(yīng)用是一座樓飞主,想要一次鍛造創(chuàng)建一座樓是不現(xiàn)實的。實際上高诺,總是鍛造很多磚,通過排列組合這些磚碾篡,才能構(gòu)建偉大的建筑虱而。
? ? ? ? React非常適合構(gòu)建用戶交互組件,讓我們創(chuàng)建一個React組件開始开泽。
? ? ? ? 學(xué)習(xí)任何一門語言或者任何一門課程牡拇,往往是從寫Hello World程序開始,不過只是展示一句Hello World并不足以體現(xiàn)React的神奇能力穆律,所以惠呼,我們要做一個不那么簡單的組件,為了體現(xiàn)React對交互功能的支持峦耘,我們做一個顯示點擊次數(shù)的組件剔蹋。
? ? ? ? 我們先看一看create-react-app給我們自動產(chǎn)生的代碼,在first_react_app目錄下包含如下文件和目錄:
? ? ? ? src/
? ? ? ? public/ 辅髓、
? ? ? ? README.md
? ? ? ? package.json
? ? ? ? node_modules
? ? ? ? 在開發(fā)過程中泣崩,我們主要關(guān)注src目錄中的內(nèi)容,這個目錄是所有的源代碼洛口。
? ? ? ? create-react-app所創(chuàng)建的應(yīng)用入口是src/index.js文件矫付,我們看看中間的內(nèi)容,代碼如下:? ?
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
? ? <App/>,
? ? document.getElementById('root');
);
? ? ? ? 這個應(yīng)用所做的事情第焰,只是渲染一個名叫App的組件买优,App組件在目錄下的App.js文件中定義。
? ? ? ? 我們要定義一個新的能夠計算點擊數(shù)組件挺举,名叫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');
);
? ? ? ? 我們接下來會介紹代碼的含義。
? ? ? ? 現(xiàn)在我們先來看看如何添加一個新組件豹悬,在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;?
? ? ? 如果你是從上一節(jié)不停頓直接讀到這里,而且沒有關(guān)閉命令行中的npm start命令瞻佛,當(dāng)你保存完這個文件之后脱篙,不需要主動做刷新網(wǎng)頁的動作娇钱,就會發(fā)現(xiàn)網(wǎng)頁中的內(nèi)容已經(jīng)發(fā)時改變。? ?
? ? ? 去點擊那個"Click Me"按鈕绊困,可以看到"Click Count"后面的數(shù)字會隨之增加文搂,每點擊一次加一次。? ?
? ? ? ? 恭喜你秤朗,現(xiàn)在你已經(jīng)構(gòu)建了一個有交互性的組件煤蹭!? ?
? ? ? ? 現(xiàn)在讓我們來逐步解釋代碼中各部分的意義。? ?
? ? ? ? 在index.js文件中取视,使用import導(dǎo)入了ClickCounter組件硝皂,代替了之前的App組件。? ?
? ? ? ? import ClickCounter from './ClickCounter ';?
? ? ? ? import是ES6(EcmaSript6)語法中導(dǎo)入文件模板的方式作谭。ES6語法是一個大集合稽物,大部分功能都被最新瀏覽器支持。不過這個import方法卻不再廣泛支持之列折欠,這沒有關(guān)系贝或,ES6語法的JavaScript代碼會被webpack和babel轉(zhuǎn)譯成所以瀏覽器都支持的ES5語法,而這一切無需開發(fā)人員做配置锐秦,create-react-app已經(jīng)替我們完成了這些工作咪奖。?
? ? ? ? 在ClickCounter.js文件的第一行,我們從react庫中引入了React和Component酱床,如下所示:?
import React,{Component} from 'react';? ?
? ? ? ? Component作為所有組件的基類羊赵,提供了很多組件共有的功能,下面這行代碼斤葱,使用的是ES6語法來創(chuàng)建一個叫ClickCouner的組件類慷垮,ClickCounter的父類就是Component:? ?
class ClickCounter extends Component{?
? ? ? ? 在React出現(xiàn)之初,使用的是React.createClass方式來創(chuàng)造組件類揍堕,這種方法已經(jīng)被廢棄了料身,但是在互聯(lián)網(wǎng)上依然存在大量的文章來基于React.createClass來講解React,這些文章中依然有很多真知灼見的部分衩茸,但是讀者要意識到芹血,使用React.createClass是一種過時的方法。在本教程中楞慈,我們只使用ES6的語法來構(gòu)建組件類幔烛。
細心的讀者會發(fā)現(xiàn),雖然我們導(dǎo)入的Componnent類在ClickCounter組件定義中使用了囊蓝,可是導(dǎo)入的React卻沒有被使用饿悬,難道在這里引入react沒有必要嗎?
事實上聚霜,引入react非常必要狡恬,你可以嘗試刪掉第一行中的React珠叔,在網(wǎng)頁中會立刻出現(xiàn)錯誤信息。
這個錯誤信息的含義是:“在使用JSX范圍內(nèi)必須要有React弟劲〉话玻”
也就是說,在使用JSX的代碼文件中兔乞,即使代碼中并沒有直接使用React汇鞭,也一定要導(dǎo)入這個React,這是因為JSX會最終被轉(zhuǎn)譯成依賴于React的表達式庸追。
接下來霍骄,我們就要認識什么是JSX。
1.2.1JSX
? ? ? ? 所謂JSX锚国,是JavaCript的語法擴展(eXtension)腕巡,讓我們在JavaScrript中可以編寫像HTML一樣的代碼。在ClickCounter.js的render函數(shù)中血筑,就出現(xiàn)了類似這樣的HTML代碼,在indexjs中煎楣,ReactDOM.render的第一個參數(shù)<App/>也是一段JSX代碼豺总。
JSX中的這幾段代碼看起來和HTML幾乎一模一樣,都可以使用<div><button>之類的元素择懂,所以只要熟悉HTML喻喳,學(xué)習(xí)JSX完全不成問題,但是困曙,我們一定要明白兩者的不同之處
首先表伦,在JSX中使用的“元素”,不局限與HTML中的元素慷丽,可以是任何一個React組件蹦哼,在App.js中可以看到,我們創(chuàng)建的ClickCounter組件直接被直接應(yīng)用到JSX中要糊,使用方法喝其他元素一樣纲熏,這一點是傳統(tǒng)的HTML做不到的。
React判斷一個元素是HTML元素還是React組件的原則就是看第一個字母是否是大寫锄俄,如果在JSX中我們不用ClickCounter而是用clickCounter局劲,那就得不到我們想要的結(jié)果。
其次奶赠,在JSX中我們可以通過onClick這樣的方式給一個元素添加一個事件處理函數(shù)鱼填,當(dāng)然,在HTML中也可以用onclick毅戈,但在HTML中直接書寫onclick一直就是為讓詬病的寫法苹丸,網(wǎng)頁應(yīng)用開發(fā)界一直倡導(dǎo)的是用jQuery的方法添加事件處理函數(shù)愤惰,直接寫onclick會帶來代碼混亂的問題。
這就帶來一個問題谈跛,既然長期一直不倡導(dǎo)在HTML中使用onclick羊苟,為什么在React的JSX中我們卻要使用onClick這樣的方式來添加事件處理函數(shù)呢?
1.2.2JSX是進步還是倒退
在React出現(xiàn)之初感憾,很多人對React這樣的設(shè)計非常反感蜡励,因為React把類似HTML的標(biāo)記語言和JavaScript混在一起了,但是阻桅,隨著時間的推移凉倚,業(yè)界逐漸認可了這種方式,因為大家發(fā)現(xiàn)嫂沉,以前用HTML來代表內(nèi)容稽寒,用CSS代表樣式,用JavaScript來定義交互行為趟章,這三種語言分在三種不同的文件里面杏糙,實際上是把不同的技術(shù)分開管理了,而不是邏輯上的“”分而治之“”蚓土。
根據(jù)做同一件事的代碼應(yīng)該有高耦合性的設(shè)計原則宏侍,既然我們要實現(xiàn)一個ClickCounter,那為什么不把實現(xiàn)這個功能的所有代碼集中在一個文件里呢蜀漆?
這點對于初學(xué)者可能有點難以接受谅河,但是相信你在看完這個教程后,觀點會隨之改變确丢。
那么绷耍,在JSX中使用onClick添加事件處理函數(shù),是否代表網(wǎng)頁應(yīng)用開發(fā)兜了一個大圈鲜侥,最終回到了起點了呢褂始?
不是這樣,JSX的onClick事件處理方式和HTML中直接使用onclick有很大不同剃毒。
即使現(xiàn)在病袄,我們還是要說在HTML中直接使用onclick很不專業(yè),原因如下:
?①onclick添加的事件處理函數(shù)是在全局環(huán)境下執(zhí)行的赘阀,這污染了全局環(huán)境益缠,很容易產(chǎn)生意料不到的后果;
②給很多DOM元素添加onclick事件基公,可能會影響網(wǎng)頁的性能幅慌,畢竟,網(wǎng)頁需要的事件處理函數(shù)越多轰豆,性能就會越低胰伍。
③對于使用onclick的DOM元素齿诞,如果要動態(tài)地從DOM樹中刪掉的話,需要把對應(yīng)的事件處理器注銷骂租,假如忘了注銷祷杈,就可能造成內(nèi)存泄露,這樣的bug很難被發(fā)現(xiàn)渗饮。
上面說的這些問題但汞,在JSX中都不存在。
我們在JSX中看到一個組件使用了onClick互站,但并沒有產(chǎn)生直接使用 onclick(注意是onclick不是onClick)的HTML私蕾,而是使用了事件委托(event delegation)的方式處理點擊事件,無論有多少個onClick出現(xiàn)胡桃,其實最后都只在DOM樹上添加了一個事件處理函數(shù)踩叭,掛在最頂層的DOM節(jié)點上。所有的點擊事件都被這個事件處理函數(shù)捕獲翠胰,然后根據(jù)具體組件分配給特定函數(shù)容贝,使用事件委托的性能當(dāng)然要比為每個onClick都掛載一個事件處理函數(shù)要高。
因為React控制了組件的生命周期之景,在unmount的時間自然能夠清除相關(guān)的所有事件處理函數(shù)嗤疯,內(nèi)存泄露也不再是一個問題。
除了在組件中定義交互行為闺兢,我們還可以在React組件中定義樣式,我們可以修改ClickCounter.js中的render函數(shù)戏罢,代碼如下:
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中復(fù)制給頂層div的style屬性,可以看到這個網(wǎng)頁總這個部分的margin真的變大了龟糕。
你看桐磁,React的組件可以把JavaScript、HTML和CSS的功能在一個文件里讲岁,實現(xiàn)真正的組件封裝我擂。
1.3分解React應(yīng)用? ? ? ? ?
前面我們提到過,React應(yīng)用實際上依賴于一個很大很復(fù)雜的技術(shù)棧缓艳,我們使用create-react-app避免在一開始就費太多精力配置技術(shù)棧校摩,不過現(xiàn)在是時候了解一下這個技術(shù)棧了。
我們啟動React應(yīng)用的命令是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命令實際上是調(diào)用了react-scripts這個命令溪窒,react-scripts是create-react-app添加的一個npm包坤塞,所有的配置文件都藏在node_modules/react-scripts目錄下冯勉,我們當(dāng)然可以鉆進這個目錄去一探究竟,但是也可以使用eject方法來看清楚背后的原理摹芙。
你可以發(fā)現(xiàn)package.json文件和start并列還有其他幾個命令灼狰,其中build可以創(chuàng)建生產(chǎn)環(huán)境優(yōu)化代碼,test用于單元測試浮禾,還有一個eject命令很有意思,這個eject(彈射)命令做的事情交胚,就是把潛藏在react-scripts中的一系列技術(shù)棧配置都“”彈射“”到應(yīng)用的頂層,然后我們就可以研究這些配置細節(jié)了伐厌,而且可以更靈活地定制應(yīng)用的配置承绸。
注意:eject命令是不可逆的,所以挣轨,當(dāng)執(zhí)行eject之前军熏,最好做一下備份。
我們在命令行執(zhí)行下面的命令卷扮,完成“”彈射“”操作:
npm run eject
這個命令會改變一些文件 荡澎,也會添加一些文件。
當(dāng)前目錄下會增加兩個目錄晤锹,一個是scripts摩幔,同時,package.json文件中的scripts部分發(fā)生了變換:
“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所做的構(gòu)造過程,其中有一段關(guān)于babel的定義:
{
test:/\.(js|jsx)$/,
include:paths.appSrc,
loader:'babel',
query:{
cacheDirectory:true
}
},
代碼中paths.appSrc的值就是src舶担,所以這段配置的含義指的是所有以js或者jsx為擴展名的文件坡疼,都會由babel所處理。
并不是所有的瀏覽器都支持所有ES6語法衣陶,但是有了babel柄瑰,我們就可以不用顧忌太多,因為babel會把ES6語法的JavaScript代碼轉(zhuǎn)譯(trranspile)成瀏覽器普遍支持的JavaScript代碼剪况,實際上教沾,在React社區(qū),不使用ES6語法寫代碼才顯得奇怪拯欧。
1.4React的工作方式
在繼續(xù)深入學(xué)習(xí)React的其他知識之前详囤,我們先就這個簡單的ClickCounter組件思考一下React的工作方式,要了解一樣?xùn)|西的特點,最好的方法就是拿這個東西和另一樣?xùn)|西作比較藏姐。我們就拿React和jQuery來比較隆箩。
1.4.1Jquery如何工作
假設(shè)我們用jQuery來實現(xiàn)ClickCounter的功能,該怎么做呢羔杨?首先捌臊,我們要產(chǎn)生一個網(wǎng)頁的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>
實際產(chǎn)品中兜材,產(chǎn)生這樣的HTML可以用PHP理澎、Java、Ruby on Rails或者任何一種服務(wù)器端語音和框架來做曙寡,也可以在瀏覽器中使用Mustache糠爬、Hogan這樣的模板產(chǎn)生,這里我們只是把問題簡化举庶,直接書寫HTML执隧。
上面的HTML只是展示樣式,并沒有任何交互功能户侥,現(xiàn)在我們用jQuery來實現(xiàn)交互功能镀琉,和jQuery的 傳統(tǒng)一樣,我們把JavaScript代碼寫在一個獨立的文件clickCounterr.js里面蕊唐,如下:
$(function(){
$('#clickMe').click(function(){
var clickCounter = $('#clickCount');
var count = parseInt(clickCounter.text(),10);
clickCounter.text(count+1);
})
})
用瀏覽器打開上面創(chuàng)造的index.html屋摔,可以看到實際效果和我們寫的React應(yīng)用一模一樣,但是對比這兩段程序可以看出差異替梨。
在jQuery的解決方案中钓试,首先根據(jù)CSS規(guī)則找到id為clickCounter的按鈕,掛上一個匿名事件處理函數(shù)副瀑,在事件處理函數(shù)中亚侠,選中那個需要被修改的DOM元素,讀取其中的文本值俗扇,加以修改,然后修改這個DOM元素箕别。
選中一些DOM元素铜幽,然后對這些元素做一些操作,這是一種最容易理解的開發(fā)模式串稀。jQuery的發(fā)明人John Resig就是發(fā)現(xiàn)了網(wǎng)頁應(yīng)用開發(fā)者的這個編程模式除抛,才創(chuàng)造出了jQuery,其一問世就得到普遍認可母截,因為這種模式直觀易懂5到忽。但是,對于龐大的項目,這種模式會造成代碼結(jié)構(gòu)復(fù)雜喘漏,難以維護护蝶,每個jQuery的使用者都會有這種體會。
1.4.2React的理念
與jQuery不同翩迈,用React開發(fā)應(yīng)用是另一種體驗持灰,我們回顧一下,用React開發(fā)的ClickCounter組件并沒有像jQuery那樣做“選中一些DOM元素然后做一些事情”的動作负饲。
React的理念堤魁,歸結(jié)為一個公式,就像下面這樣:
UI=render(data)
讓我們來看看這個公式表達的含義返十,用戶看到的界面(UI)妥泉,應(yīng)該是一個函數(shù)(在這里叫render)的執(zhí)行結(jié)果,只接受數(shù)據(jù)(data)作為參數(shù)洞坑。這個函數(shù)是一個純函數(shù)盲链,所謂純函數(shù),指的是沒有任何副作用检诗,輸出完全依賴于輸入的函數(shù)匈仗,兩次函數(shù)調(diào)用如果輸入相同,得到的結(jié)果也絕對相同逢慌。如此一來悠轩,最終的用戶界面,在render函數(shù)確定的情況下完全取決于輸入數(shù)據(jù)攻泼。
對于開發(fā)者來說火架,重要的是區(qū)分分開哪些屬于data,哪些屬于render忙菠,想要更新用戶界面何鸡,要做的就是更新data,用戶界面自然會做出響應(yīng)牛欢,所以React實踐的也是“響應(yīng)式編程“(Reactive Programming)的思想骡男,這也就是React為什么叫做React的原因。
1.4.3Virtual DOM
既然React應(yīng)用就是通過重復(fù)渲染來實現(xiàn)用戶交互傍睹,你可能會有一個疑慮:這樣的重復(fù)渲染會不會效率太低了呢隔盛?畢竟,在jQuery的實現(xiàn)方式中拾稳,我們可以清楚地看到每次只有需要變化的那一個DOM元素被修改了吮炕;可是,在React的實現(xiàn)方式中访得,看起來每次render函數(shù)被調(diào)用龙亲,都要把整個組件重新繪制一次,這樣看起來有點浪費。
事實并不是這樣鳄炉,React利用Virtual DOM,讓每次渲染都只重新渲染最少的DOM元素杜耙。
要了解Virtual DOM,就要先了解DOM,DOM是結(jié)構(gòu)化文本的抽象表達式迎膜,特定于web環(huán)境中泥技,這個結(jié)構(gòu)化文本就是HTML文本,HTML中的每個元素都對于DOM中某個節(jié)點磕仅,這樣珊豹,因為HTML元素的逐級包含關(guān)系,DOM節(jié)點自然就構(gòu)成了一個樹形結(jié)構(gòu)榕订,稱為DOM叔5店茶。
瀏覽器為了渲染HTML格式的網(wǎng)頁细卧,會先HTML文本解析以構(gòu)建DOM樹惦辛,然后根據(jù)DOM樹渲染出用戶看到的界面,當(dāng)要改變界面內(nèi)容的時候乍楚,就去改變DOM樹上的節(jié)點两嘴。
Web前端開發(fā)關(guān)于性能優(yōu)化有一個原則:盡量減少DOM操作丛楚。雖然DOM操作也只是一些簡單的JavaScript語句,但是DOM操作會引起瀏覽器對網(wǎng)頁進行重新布局憔辫,重新繪制趣些,這就是一個比JavaScript語句執(zhí)行慢很多的過程。
雖然JSX看起來很像是一個模板贰您,但是最終會被Babel解析為一條條創(chuàng)建React組件或者HTML元素的語句坏平,神奇之處在于,React并不是通過這些語句直接構(gòu)建DOM樹锦亦,而是首先構(gòu)建Virtual DOM舶替。
既然DOM樹是對HTML的抽象,那Virtual DOM就是對DOM樹的抽象杠园。Virtual DOM不會觸及瀏覽器的部分顾瞪,只是存在于JavaScript空間的樹形結(jié)構(gòu),每次自上而下渲染React組件時抛蚁,會對比這一次產(chǎn)生的Virtual DOM,對比就會發(fā)現(xiàn)差別玲昧,然后修改真正的DOM樹時就只需要觸及差別中的部分就行。
以ClickCounter為例篮绿,一開始計數(shù)為0,用戶點擊按鈕讓點擊計數(shù)變成1吕漂,這一次重新渲染亲配,React通過Virtual DOM的對比發(fā)現(xiàn)其實只是id為clickCount的sapn元素中內(nèi)容從0變成了1而已:
<span id="clickCount">{this.sate.count}</span>
React發(fā)現(xiàn)這次渲染要做的事情就是更換這個span元素的內(nèi)容而已,其他DOM元素都不需要觸及,于是執(zhí)行類似寫了的語句吼虎,就完成了任務(wù):
document.getElementById("clickCount").innerHTML="1";
React對比Virtual DOM尋找差異的過程比較復(fù)雜犬钢,后面第五章我們會詳細介紹對比的過程。
1.4.4React工作方式的優(yōu)點
毫無疑問思灰,jQuery的方式直觀易懂玷犹,對于初學(xué)者十分適用,但是當(dāng)項目逐漸變得龐大時洒疚,用jQuery寫出的代碼往往互相糾纏歹颓,難以維護。
使用React的方式油湖,就可以避免構(gòu)建這樣復(fù)雜的程序結(jié)構(gòu)巍扛,無論何種事件,引發(fā)的都是React組件的重新渲染乏德,至于如何只修改必要的DOM部分撤奸,則完全交給React去操作,開發(fā)者并不需要去關(guān)心喊括。
React利用函數(shù)式編程的思維來姐姐用戶界面渲染的問題胧瓜,最大的優(yōu)勢是開發(fā)者的效率會大大提高,開發(fā)出的代碼可維護性和可讀性也大大增強郑什。
React等于強制所有組件都按照這種由數(shù)據(jù)驅(qū)動渲染的模式來工作府喳,無論應(yīng)用的規(guī)模多大,都能讓程序處于可控范圍內(nèi)蹦误。
1.5本章小結(jié)
在這一章里劫拢,我們用create-react-app創(chuàng)造了一個簡單的React應(yīng)用,在一開始强胰,我們就按照 組件的思想來開發(fā)應(yīng)用舱沧,React的主要理念之一就是基于組件來開發(fā)應(yīng)用。
通過和同樣功能的jQuery實現(xiàn)方式對比偶洋,我們了解了React的工作方式熟吏,React利用聲明式的語法,讓開發(fā)者專注于描述用戶界面“”顯示成什么樣子“”玄窝,而不是重復(fù)思考“”如何去顯示“”牵寺,這樣可以大大提高開發(fā)效率,也讓代碼? ? 更加容易管理? 恩脂。
雖然React是通過重復(fù)渲染來實現(xiàn)動態(tài)更新效果帽氓,但是借助Virtual DOM技術(shù),實際上這個過程并不牽扯太多的DOM操作俩块,所以渲染效率更高黎休。
更多內(nèi)容浓领,請訪問的我的個人博客:[https://liugezhou.github.io/blog](https://liugezhou.github.io/blog)
您也可以關(guān)注我的個人公眾號:【W(wǎng)akaka】