翻譯:瘋狂的技術(shù)宅
https://www.toptal.com/react/react-tutorial-pt1
前端和 JavaScript 是一個(gè)奇怪的世界。大量不斷推出的新技術(shù)的同時(shí),也在被不需要它們的人嘲笑奏甫,往往很多人都會(huì)這樣做扶供。我們有時(shí)會(huì)對不斷涌現(xiàn)的信息囤耳、庫和討論感到不知所措暂论,總希望能有一些穩(wěn)定的東西面褐,就像能讓我們可以休整一段時(shí)間的避風(fēng)港。最近 React 似乎有變成 JavaScript 演變海洋中溫暖港灣的趨勢空另。
正是考慮到這一點(diǎn)盆耽,我們決定制作這個(gè) React 系列教程,展示它的功能扼菠,并看看它與 Angular 和 VueJS 相比有什么特點(diǎn)摄杂。
React 是一座聳立在 JavaScript 代碼海上醒目的燈塔
當(dāng)然 React 并不是唯一的選擇,但目前它是最受歡迎循榆、最穩(wěn)定析恢、最具有創(chuàng)新性的解決方案之一,雖然它仍然在不斷升級秧饮,但更多的是在改進(jìn)映挂,而不是增加功能。
2019年的 React
React 是一個(gè)視圖庫盗尸,可以一直追溯到2011年柑船,當(dāng)時(shí)它的第一個(gè)原型名為 FaxJs,并出現(xiàn)在 Facebook 上泼各,React 是由 Jordan Walke(他也是上述原型的作者)于2013年5月29日在 JSConfUS 推出的鞍时,并于2013年7月2日在 GitHub 上公開發(fā)布。
在2014年扣蜻,當(dāng)開始擴(kuò)大社區(qū)并推廣 React 時(shí)逆巍,它受到持續(xù)歡迎。然而從我的角度來看莽使,2015年是大型公司(例如 Airbnb 和 Netflix )開始喜歡并采用 React 的里程碑年锐极。此外,當(dāng)年還出現(xiàn)了React Native芳肌。 React Native背后的想法并不是什么全新的東西灵再,不過看起來很有趣,尤其是因?yàn)樗玫搅?Facebook 的支持亿笤。
另一個(gè)重大變化是 Redux檬嘀,一個(gè) Flux 實(shí)現(xiàn)。這使?fàn)顟B(tài)管理方式更加簡單友好责嚷,使其成為迄今為止最成功的實(shí)現(xiàn)鸳兽。
從其出現(xiàn)一直到現(xiàn)在,還有很多其他的東西供我們使用罕拂,包括 React tools【https://www.toptal.com/react/navigating-the-react-ecosystem】揍异,重寫了核心算法全陨;Fiber 用于語義轉(zhuǎn)換版本控制等等。到了今天衷掷,我們處于 v16.6.3辱姨,幾周后可能就會(huì)發(fā)布支持 Hooks 的新版本(它應(yīng)該在 16.7.0 得到支持,但由于對 React.lazy 做了一些修復(fù)戚嗅,就先發(fā)布了一個(gè)版本)雨涛。React 由于其名氣和穩(wěn)定性獲得了廣泛好評。
但 React 到底是什么懦胞?
好吧替久,如果你身為前端開發(fā)人員但是從來都沒有聽說過,那么我就要說聲“恭喜你”躏尉,因?yàn)檫@是一個(gè)了不起的壯舉蚯根。
開個(gè)玩笑而已。React 是一個(gè)聲明式的基于組件的視圖庫胀糜,可以幫助你構(gòu)建 UI颅拦。它是一個(gè)庫而不是一個(gè)框架,雖然最初很多人把它描述為后者教藻。
顯然距帅,如果我們要把 Redux 和 React Router 等添加到 React,它就擁有了制作常規(guī)單頁應(yīng)用程序所需的所有東西括堤,這可能這就是它有時(shí)被錯(cuò)誤地描述為框架而不是庫的原因 碌秸。如果一定要這樣認(rèn)為的話,將該環(huán)境的所有組件放在一起痊臭,術(shù)語“框架”可能有點(diǎn)適合它哮肚,但就其本身而言登夫,React 僅僅是一個(gè)庫广匙。
不要再糾結(jié)改怎么對其分類了,先關(guān)注 React 有什么獨(dú)特之處恼策,一些之前沒有注意到的東西鸦致。首先,當(dāng)你第一次看到 React 時(shí)涣楷,就會(huì)想到 JSX【https://reactjs.org/docs/jsx-in-depth.html】分唾,因?yàn)檫@是你看到代碼時(shí)的第一個(gè)感受。 JSX是一種 JavaScript 語法擴(kuò)展狮斗,有點(diǎn)類似于 HTML/XML绽乔。說到 React 和 JSX,它們與 HTML 有一些區(qū)別碳褒,例如折砸,React 中的類是?className看疗,沒有tabindex 但是有?tabIndex,樣式接受具有駝峰命名的屬性的 JavaScript 對象睦授,依此類推两芳。
有一些細(xì)微的差別,但是任何人都應(yīng)該立即接受它們去枷。事件處理是通過例如?onChange?和?onClick?屬性實(shí)現(xiàn)的怖辆,這些屬性可以用來附加一些函數(shù)來處理事件。此外删顶,以后的組件可以通過使用 props 自由重用和自定義竖螃,因此沒有理由多次編寫相同的代碼。
1import?React,?{?Component?}?from?'react';
2
3export?default?class?App?extends?Component?{
4render()?{
5return?(
6
Hello?World,?{this.props.name}
7);
8}
9}
但是實(shí)際上 JSX 在 React 中并不是非常必要的翼闹。你可以編寫常規(guī)函數(shù)來創(chuàng)建元素斑鼻,而無需使用JSX。上面的代碼可以像下面這樣去用猎荠。
1import?React,?{?Component?}?from?'react';
2
3export?default?class?App?extends?Component?{
4render()?{
5return?React.createElement(
6'div',
7null,
8'Hello?World,?',
9this.props.name
10);
11}
12}
很顯然我并不是建議你用這樣的語法坚弱,盡管有些情況下它有可能會(huì)派上用場(例如你想要引入一個(gè)非常小的東西但是又不想更改構(gòu)建環(huán)境)。
實(shí)際上我展示上述代碼還有另一個(gè)原因关摇。通常荒叶,開發(fā)人員不理解我們?yōu)槭裁葱枰獔?zhí)行以下操作:
1import?React?from?'react';
代碼片段應(yīng)該是能夠自解釋的。即使我們正在提取?Component输虱,仍然需要 React些楣,因?yàn)?Babel 在 JSX 之上轉(zhuǎn)換為?React.createElement。所以如果我們不導(dǎo)入 React 就會(huì)失效宪睹。前面我提到了 Babel愁茁,這是一個(gè)工具,可以幫助我們預(yù)覽那些尚未在 JavaScript 中(更確切地說是在瀏覽器中)支持的東西亭病,或者以某種方式對 ?JavaScript 進(jìn)行擴(kuò)展(或者類似于 TypeScript鹅很,Babel 從 Babel 7 開始支持的不同語言)。感謝Babel:
JSX 將被轉(zhuǎn)化為成瀏覽器可以理解的代碼罪帖。
我們可以使用尚未在瀏覽器中實(shí)現(xiàn)的新功能(例如類屬性)促煮。
我們可以支持新瀏覽器中的特性,同時(shí)在舊瀏覽器中支持較舊的功能整袁。
簡而言之菠齿,在 JavaScript 中,就是今天的代碼明天仍然能用坐昙;這可能需要為此專門再寫一篇文章绳匀。值得一提的是,React 的導(dǎo)入也可以被一些其他技術(shù)繞過(比如通過 Webpack 引入 ProvidePlugin 等),但是由于篇幅有限疾棵,我們將避免使用這種方式盗飒,并假設(shè)用戶將使用 Create React App( CRA)(稍后將提到有關(guān)此工具的更多信息)。
另一點(diǎn)比 JSX 本身更重要陋桂,那就是 React 基于虛擬 DOM逆趣。簡而言之,虛擬 DOM 是用 JavaScript 編寫的在內(nèi)存中的理想樹結(jié)構(gòu)嗜历,稍后我們會(huì)把它與真實(shí) DOM 進(jìn)行比較宣渗。
怎樣與 Angular 和 Vue 進(jìn)行比較?
我很不喜歡對庫進(jìn)行比較梨州,特別是當(dāng)我們被迫把梨和蘋果放在一起進(jìn)行比較時(shí)痕囱。
因此,我將嘗試使用一系列簡短的問題和答案將 React 與 Angular 和 Vue 進(jìn)行比較暴匠。這種比較與技術(shù)相關(guān)鞍恢,而不是主觀的作出?“X比Y更好,因?yàn)樗褂?JSX 而不是模板每窖“锏簦”?這種出于個(gè)人偏好的對比。另外在速度和內(nèi)存分配等方面 React 與其主要競爭對手(Angular 和 Vue 都能想得到)非常相似窒典,有一篇關(guān)于這個(gè)問題的文章很不錯(cuò)蟆炊,但請記住這一點(diǎn):絕大多數(shù)程序并不會(huì)做這種處理上萬行數(shù)據(jù)的事。因此瀑志,這些結(jié)果也是純粹的速度實(shí)驗(yàn)涩搓。實(shí)際上你也不會(huì)把這放在首位。
React vs. Angular vs. Vue.js
那么讓我們來看看關(guān)于 React 的問題以及它與競爭對手的比較:
我想擁有更多的工作機(jī)會(huì)劈猪。 React 到底有多受歡迎昧甘?
嗯,這很容易回答 —— 選擇 React战得。實(shí)際上充边,我會(huì)說 React 的工作機(jī)會(huì)大約其它的 6 到 10?倍(可能出入比較大,在一些大網(wǎng)站是 50 倍贡避,也有些網(wǎng)站是 6 倍)痛黎,是 ?Vue 的 2 到 4倍予弧,比 Angular 更多刮吧。對 React experts?【https://www.toptal.com/react】的需求很大,但是為什么 Vue 在 GitHub 上非常受歡迎(實(shí)際上它獲得了比 React 更多的star)卻沒有更多的職位空缺掖蛤?這點(diǎn)我不知道杀捻。【譯者注:作者是美國人蚓庭,這里指的是美國的就業(yè)市場】
我想要一個(gè)很大的社區(qū)致讥,還有大量的庫仅仆,能夠快速解決可能出現(xiàn)的問題。
選 React垢袱,不要再猶豫了墓拜。
它是否容易使用,開發(fā)過程是否令人愉快请契?
2018年和2017年的 JS 狀態(tài)報(bào)告顯示咳榜,React 和 Vue 都享有良好的聲譽(yù),大多數(shù)開發(fā)人員表示會(huì)再次使用爽锥。另一方面Angular 有一種趨勢涌韩,每年都會(huì)有越來越多的人說不會(huì)再次使用它。
我想創(chuàng)建一個(gè)新的單面應(yīng)用氯夷,但我不想額外去找這種支持庫臣樱。
我認(rèn)為這大概是 Angular 值得選擇的唯一原因。
我不是大公司腮考。但是希望盡可能獨(dú)立雇毫,應(yīng)該選擇哪個(gè)?
Vue —— 它是我們?nèi)揞^中唯一獨(dú)立的一個(gè)踩蔚。 ( Facebook 支持 React嘴拢,而 Google 支持 Angular。)
上手最簡單和最快的學(xué)習(xí)曲線寂纪?
Vue/React席吴。在這里我更傾向于 Vue,但這只是我個(gè)人的意見捞蛋。至于為什么孝冒?因?yàn)槟悴恍枰?JSX(它是可選的),它基本上只是 HTML + CSS + JavaScript拟杉。
React Tutorial:開始你的第一個(gè)程序
React tutorial:成功創(chuàng)建 React 應(yīng)用后的屏幕截圖
目前上手 React 最簡單方法是使用 CRA庄涡,這是一個(gè)為你創(chuàng)建項(xiàng)目的 CLI 工具,可幫助你避免配置 Webpack / Babel 等環(huán)境搬设。你只需要依賴默的認(rèn)配置方式穴店,并隨著時(shí)間的推移更新包含在內(nèi)的內(nèi)容。多虧了這一點(diǎn)拿穴,你無需關(guān)心某些關(guān)鍵庫的主要更新泣洞。
當(dāng)然,稍后默色,你可以通過運(yùn)行?npm run eject?來“彈出”自己并自己處理每個(gè)細(xì)節(jié)球凰。這種方法有其自身的優(yōu)點(diǎn),因?yàn)槟憧梢栽黾釉瓉聿豢捎玫臇|西(例如裝飾器)來增強(qiáng)你的應(yīng)用,但它也可能是令人頭疼的問題呕诉,因?yàn)樗枰ㄙM(fèi)更多的時(shí)間去配置許多額外的文件缘厢。
所以,首先要做的是:
1npx?create-react-app?{app-name}
然后?npm run start?就完成了甩挫。
類組件與函數(shù)組件
我們應(yīng)該先解釋這些組件的不同之處贴硫。基本上每個(gè)組件可以是?function?或?class伊者。它們之間的主要區(qū)別在于夜畴,類組件有函數(shù)組件中沒有的一些功能:它們有 state 并使用 refs、生命周期等删壮。從 v16.7 開始我們可以使用 hooks贪绘,因此可以使用 hooks 來進(jìn)行 state 和 refs。
類組件有兩種類型:Component?和?PureComponent央碟。它們唯一的區(qū)別是?PureComponent?可以對 props 和 state 進(jìn)行淺層比較 —— 這在你不想“浪費(fèi)”渲染資源的情況下有獨(dú)到的好處税灌,一個(gè)組件及其子組件恰好在渲染后處于相同狀態(tài)。不過它只有一個(gè)淺層比較亿虽;如果你想實(shí)現(xiàn)自己的比較操作(假如你傳遞的是復(fù)雜的 props)菱涤,只需要用?Component?并覆蓋?shouldComponentUpdate(默認(rèn)情況下返回true)。從 16.6 + 開始洛勉,在函數(shù)組件中也可以用類似的東西 —— 全靠?React.memo?這個(gè)更高階的組件粘秆,在默認(rèn)情況下表現(xiàn)得像?PureComponent(淺層比較),在你進(jìn)行自定義的 props 比較時(shí)它還需要第二個(gè)參數(shù)收毫。
一般來說如果你能用函數(shù)組件(假設(shè)你不需要類功能)那么就用它攻走。不過從 16.7.0 開始,由于生命周期方法此再,只能用類組件昔搂。但是我認(rèn)為函數(shù)組件更透明,更容易推理和理解输拇。
React 生命周期方法
安裝摘符、更新和卸載組件
Constructor(props)
可選,CRA 使其變得受歡迎策吠,默認(rèn)包含 JavaScript 的類字段聲明逛裤。聲明是否通過類中的箭頭函數(shù)去綁定方法是沒有意義的。類似的狀態(tài)也可以初始化為類屬性猴抹。
僅用于 ES6 類中初始化對象的本地狀態(tài)和綁定方法带族。
componentDidMount()
在這里進(jìn)行 Ajax 調(diào)用。
如果你需要事件監(jiān)聽器洽糟,訂閱等功能炉菲,可以在此處添加。
你可以在這里使用?setState(但是它會(huì)使組件重新渲染)坤溃。
componentWillUnmount()
清除所有仍在進(jìn)行的東西 —— 例如拍霜,Ajax應(yīng)該被中斷,取消訂閱薪介,清除定時(shí)器等等祠饺。
不要調(diào)用?setState,因?yàn)樗鼪]有意義汁政,因?yàn)榻M件將會(huì)被卸載(并且你會(huì)得到一個(gè)警告)道偷。
componentDidUpdate(prevProps, prevState, snapshot)
在組件剛剛更新完畢時(shí)執(zhí)行(在開始渲染時(shí)不會(huì))。
有三個(gè)可選的參數(shù)(以前的props记劈,以前的 state 和只有在你的組件實(shí)現(xiàn)?getSnapshotBeforeUpdate?時(shí)才會(huì)出現(xiàn)的快照 )勺鸦。
僅當(dāng)?shouldComponentUpdate?返回true時(shí)才會(huì)執(zhí)行。
If you use?setState?here, you should guard it or you will land in an infinite loop.
如果你在這里用到了?setState目木,應(yīng)該保護(hù)它换途,否則將會(huì)陷入無限循環(huán)。
shouldComponentUpdate(nextProps, nextState)
僅用于性能優(yōu)化刽射。
如果返回 false军拟,則不會(huì)調(diào)用渲染器。
如果重寫的 SCO 只是對 props/state的淺層比較誓禁,可以使用?PureComponent懈息。
getSnapshotBeforeUpdate()
可用于保存一些與當(dāng)前 DOM 有關(guān)的信息,例如當(dāng)前的滾動(dòng)位置摹恰,稍后可在?componentDidUpdate?中重用辫继,用來恢復(fù)滾動(dòng)的位置。
componentDidCatch(error, info)
應(yīng)該發(fā)生日志記錄錯(cuò)誤的地方俗慈。
可以調(diào)用?setState骇两,但在以后的版本中,將會(huì)在靜態(tài)方法getDerivedStateFromError(error)?中被刪除姜盈,它將通過返回一個(gè)值來更新狀態(tài)低千。
還有兩種靜態(tài)方法,在其他的段落中提到過
static getDerivedStateFromError(error)
此處提供錯(cuò)誤信息馏颂。
應(yīng)返回一個(gè)對象值示血,該值將會(huì)更新可用于處理錯(cuò)誤的狀態(tài)(通過顯示內(nèi)容)。
由于它是靜態(tài)的救拉,因此無法訪問組件實(shí)例本身难审。
static getSnapshotBeforeUpdate(props, state)
應(yīng)該在 props 隨時(shí)間變化的情況下使用 —— 例如根據(jù) React docs,它可能用于轉(zhuǎn)換組件亿絮。
由于它是靜態(tài)的告喊,因此無法訪問組件實(shí)例本身麸拄。
注意,目前還有更多可用的方法黔姜,但它們可能會(huì)在 React 17.0 中被刪除拢切,所以就不在這里沒有提起了。
State vs. Props
我們先從?Props?開始秆吵,因?yàn)樗菀捉忉尰匆rops 是傳給組件的屬性,以后可以在組件顯示信息或業(yè)務(wù)邏輯時(shí)重用它 纳寂。
1import?React,?{?Component?}?from?'react';
2
3export?default?class?App?extends?Component?{
4render()?{
5
6return?(
7
8
9
10);
11}
12}
13
14const?HelloWorld?=?(props)?=>?
Hello?{props.name}
在上面的例子中主穗,name?是一個(gè) prop。prop 是只讀元素毙芜,不能直接在子組件中更改忽媒。很多人有一種不太好的習(xí)慣,那就是把 prop 復(fù)制到 state 腋粥,然后再對 state 進(jìn)行操作猾浦。當(dāng)然有時(shí)候你希望執(zhí)行類似 “在提交之后去更新父組件的初始狀態(tài)” 這樣的操作,但這種情況非常少見 —— 在這種情況下灯抛,更新初始狀態(tài)可能有意義金赦。另外不僅可以給子組件傳遞字符串這樣的 prop ,還可以傳遞數(shù)字对嚼、對象夹抗、函數(shù)等。
prop 還有一個(gè)更有用的東西叫做?defaultProps纵竖,這是一個(gè)靜態(tài)字段漠烧,它可以告訴你組件的默認(rèn) prop 是什么(比如當(dāng)它們沒有傳遞給組件時(shí))。
在“狀態(tài)提升”的情況下靡砌,其中一個(gè)組件(父組件)具有稍后由其子組件重用的狀態(tài)(例如已脓,一個(gè)子組件用來顯示而另一個(gè)用來編輯),那么我們需要將該功能從父組件傳遞給子組件通殃。 它允許我們更新父級的本地狀態(tài)度液。
另一方面,狀態(tài)是一個(gè)可以修改的本地狀態(tài)画舌,但是通過?this.setState?間接修改堕担。如果直接去改變狀態(tài),組件將不會(huì)感知到曲聂,更不會(huì)因?yàn)闋顟B(tài)的改變而重新渲染霹购。
**SetState **是一種更改本地狀態(tài)對象的方法(通過執(zhí)行淺層合并),之后組件通過重新渲染自己來響應(yīng)它朋腋。請注意齐疙,在使用?setState?之后膜楷,this.state?屬性不會(huì)立即對更改(它具有異步性質(zhì))作出反應(yīng),因?yàn)閮?yōu)化的原因贞奋,可能會(huì)將?setState?的幾個(gè)實(shí)例一起進(jìn)行批處理赌厅。調(diào)用它的方式有好幾種,其中一種方式允許我們在對狀態(tài)進(jìn)行更新能夠后立即對組件執(zhí)行某些操作:
setState({value: ‘5’})
setState((state, props) => ({value: state.name + “‘s”}))
setState([object / function like above], () => {})?—— 這個(gè)表單允許我們附加?callback忆矛,當(dāng) state 顯示我們想要的數(shù)據(jù)時(shí)被調(diào)用(在第一個(gè)參數(shù))察蹲。
1import?React,?{?Component?}?from?'react';
2
3export?default?class?App?extends?Component?{
4state?=?{
5name:?'Someone?:)'
6}
7
8onClick?=?()?=>?this.setState({?name:?'You'?})
9
10render()?{
11return?(
12
13
14
15);
16}
17}
18
19const?HelloWorld?=?(props)?=>?Hello?{props.name}
React Context
React 最近穩(wěn)定的 Context API(已經(jīng)在 React 中存在了相當(dāng)長的時(shí)間请垛,盡管被 Redux 等一些最受歡迎的庫廣泛使用催训,卻是一個(gè)實(shí)驗(yàn)性功能)有助于我們解決一個(gè)問題:Props drilling。簡而言之 Props drilling 是在結(jié)構(gòu)中深入傳遞 props 的一種方式 —— 例如宗收,它可以是組件的某種主題漫拭、針對特定語言的本地化、用戶信息等混稽。在 Context出現(xiàn)之前(或者更確切地說采驻,在它變成非實(shí)驗(yàn)功能之前),它是通過遞歸方式從父級一直傳遞到到子級的最后一級的來進(jìn)行鉆取的(顯然還有可以解決這個(gè)問題的 Redux)匈勋。請注意礼旅,此功能僅僅用于解決 Props drilling 的問題,并且不能替代 Redux 或 Mobx 等洽洁。當(dāng)然如果你只使用狀態(tài)管理庫痘系,則可以隨意替把它替換掉。
總結(jié)
這是我們的React教程的第一部分饿自。在后續(xù)的文章中汰翠,我們會(huì)設(shè)計(jì)更多高級主題,包括樣式和類型檢查昭雌,以及生產(chǎn)部署和性能優(yōu)化复唤。