React 教程:如何開始以及如何比較

已發表: 2022-03-11

前端和 JavaScript 尤其是一個奇怪的世界。 每天推出的新事物的數量經常被不與他們合作的人所嘲笑,而且很多人都在嘲笑他們。 儘管如此,有時我們還是對新的信息、圖書館和討論感到有點不知所措,我們想要一些穩定的東西,比如可以在船上停留更長時間的避風港。 最近,React 似乎是動態 JavaScript 進化海洋中的這個溫順的港灣。

考慮到這一點,我們決定製作這個由多部分組成的 React 教程,以展示它的功能並看看它與 Angular 和 VueJS 的比較。

React 作為燈塔的插圖清楚地顯示在 JavaScript 代碼的海洋上

當然,React 不是我們可以使用的唯一港口,但目前,它是最流行、最穩定、最創新的解決方案之一,雖然它仍然有很多升級,但它們更像是一種改進的選擇,而不是而不是功能的必需品。

2019 年的 React 狀態

React 是一個視圖庫,我們可以追溯到 2011 年,當它的第一個原型 FaxJs 出現在其 Facebook 頁面上時,React 本身是由 Jordan Walke(他也是上述原型的作者)在 JSConfUS 上介紹的2013 年 5 月 29 日,並於 2013 年 7 月 2 日在 GitHub 上公開發布。

React 在 2014 年繼續流行,當時會議開始出現擴大社區並普及 React。 不過,在我看來,2015 年對於 React 來說是具有里程碑意義的一年——大公司(例如 Airbnb 和 Netflix)開始喜歡並採用 React 解決方案。 此外,React Native 也在當年出現。 React Native 背後的想法並不是什麼新鮮事物,但觀看起來很有趣,尤其是在它得到 Facebook 支持的情況下。

另一個重大變化是 Redux,一個 Flux 實現。 這使得狀態管理方式更加平易近人和更容易,使其成為迄今為止最成功的實施方式。

從那時到現在,還有很多其他的東西可用,包括 React 工具、核心算法重寫、Fiber、語義版本控制的更改等等。 快進到今天,我們在 16.6.3 上,可能在帶有 Hooks 的新版本可用前幾週(它應該是 16.7.0,但由於對 React.lazy 的一些修復,該版本已經發布)。 React 是眾所周知且穩定的,並且獲得了很好的意見。

但是什麼反應?

好吧,如果您是前端開發人員並且您還沒有聽說過它,那麼我需要說恭喜您,因為這是一項了不起的壯舉。

撇開玩笑不談,React 是一個基於聲明式組件的視圖庫,可幫助您構建 UI。 它是一個庫,而不是一個框架,儘管起初很多人將其描述為後者。

顯然,如果我們要添加 Redux、React Router 等,它開始擁有製作常規單頁應用程序所需的所有東西,這可能是它有時被錯誤描述為框架而不是庫的原因. 如果有的話,可以說,將環境的所有組件放在一起,“框架”一詞有點合適,但就其本身而言,React 只是一個庫。

讓我們停止命名,關注 React 中的不同之處,以及我們在它開始之前沒有的東西。 首先,當您第一次想到 React 時,您會想到 JSX,因為它是您查看代碼時首先映入眼簾的東西。 JSX 是一種 JavaScript 語法擴展,有點類似於 HTML/XML。 說到 React 和 JSX,我們與 HTML 有一些區別,例如,React 中的一個類是className ,沒有 tabindex 而是tabIndex ,樣式接受具有 camelCased 屬性的 JavaScript 對像等等。

有一些細微的差異,但任何人都應該立即了解它們。 事件處理是通過例如onChangeonClick屬性來實現的,這些屬性可以用來附加一些函數來處理事件。 此外,以後的組件可以通過使用 props 自由重用和自定義,因此沒有理由多次編寫相同的代碼。

 import React, { Component } from 'react'; export default class App extends Component { render() { return ( <div>Hello World, {this.props.name}</div> ); } }

然而,JSX 在 React 中實際上並不是絕對必要的。 您可以編寫常規函數來創建元素,而無需使用 JSX。 上面相同的代碼,可以像下面一樣使用。

 import React, { Component } from 'react'; export default class App extends Component { render() { return React.createElement( 'div', null, 'Hello World, ', this.props.name ); } }

顯然,我不建議您使用這種語法,儘管在某些情況下它可能會派上用場(例如,您想引入一個非常小的東西並且不想更改構建環境)。

實際上,我展示上面的代碼片段還有另一個原因。 通常,開發人員不明白為什麼我們需要執行以下操作:

 import React from 'react';

該片段應該是不言自明的。 即使我們正在提取Component ,我們仍然需要 React,因為 Babel 將 JSX 之上的轉換為React.createElement 。 所以,如果我們不導入 React,它對我們來說只會失敗。 我提到了 Babel,它是一個工具,它可以幫助我們引入 JavaScript 中(或者更確切地說是瀏覽器)中還沒有的東西,或者以某種方式對其進行擴展(或者 Babel 7 支持的不同語言,如 TypeScript)。 感謝巴別塔:

  • JSX 會被做成瀏覽器可以理解的函數。
  • 我們可以使用瀏覽器中還沒有的新特性(例如,類屬性)。
  • 我們可以添加新瀏覽器中存在但舊瀏覽器中沒有的功能,同時保持舊瀏覽器支持。

簡而言之,JavaScript 的明天就是今天; 這可能需要它自己的文章。 值得一提的是,React 導入也可以通過其他一些技術繞過(比如通過 Webpack 引入 ProvidePlugin 等),但由於篇幅有限,我們將避免它,假設用戶會使用 Create React App ( CRA)(有關此工具的更多信息將在後面提到)。

第二個重要的事情,比 JSX 本身更重要的是 React 基於虛擬 DOM。 簡而言之,虛擬 DOM 是由開發人員編寫的 JavaScript 表示的理想樹的內存,稍後將其與真實 DOM 進行比較,並在稱為協調的過程中與之同步。

React 與 Angular 和 Vue 相比如何?

我非常不喜歡比較庫,尤其是當我們被迫將梨與蘋果進行比較時(庫與框架等)。

因此,我將嘗試使用一系列與技術無關的簡短問題和答案來比較 React 與 Angular 和 Vue,而不是說“X 比 Y 更好,因為它使用 JSX 而不是模板。 ” 諸如此類的點通常是個人喜好,一個人的主觀選擇。 此外,速度、內存分配等在 React 及其所有主要競爭對手(想到 Angular 和 Vue)中都非常相似。 關於這個問題有一個非常好的報告,但請記住這一點:絕大多數應用程序看起來不像在 10k 表中交換行的非常大的表。 因此,這些結果也是純速度實驗。 在現實世界中,你一開始就不會做這樣的事情。

React 與 Angular 與 Vue.js 的圖示

因此,讓我們看一下與 React 有關的一些問題以及它與競爭對手的比較:

我想有很多工作機會。 React 有多受歡迎?

嗯,這很容易回答——選擇 React。 實際上,我會說 Rea​​ct 的職位空缺大約是 Vue 的 6-10(相當大的傳播,但有些門戶網站是 1:50,有些是 1:6),比 Vue 多 2-4比角。 對 React 專家的需求很旺盛,那麼為什麼 Vue 在 GitHub 上如此受歡迎(事實上它比 React 擁有更多的星星)而職位空缺卻更少? 我不知道。

我想要一個大社區,大量圖書館,為可能出現的問題提供快速解決方案。

做出反應。 不要再看了。

它是否易於使用,是否讓開髮變得愉快?

再一次,根據 2018 年和 2017 年的 JS 報告,React 和 Vue 都享有非常好的聲譽,大多數開發人員表示他們會再次使用它們。 另一方面,Angular 有一種趨勢,年復一年,讓越來越多的人說他們不會使用它了。

我想創建一個新的單頁應用程序,但我不想搜索庫。

這可能是我認為 Angular 是更好選擇的唯一地方。

沒有大公司。 我想盡可能獨立,我應該選擇哪個?

Vue——​​它是我們大三人組中唯一獨立的。 (Facebook 支持 React,而谷歌支持 Angular。)

最簡單的開始和最快的學習曲線?

Vue/反應。 我在這裡傾向於 Vue,但這只是我個人的看法。

為什麼? 因為你甚至不需要知道 JSX(它是可選的),它基本上只是 HTML + CSS + JavaScript。

React 教程:開始你的第一個應用程序

React 教程:創建 React 應用的成功消息截圖

現在開始使用 React 最簡單的方法是使用 CRA,這是一個 CLI 工具,可以為您創建項目並幫助您避免 Webpack/Babel 等的所有必要設置。 相反,您依賴於它的默認配置方式以及隨著時間的推移其中包含的內容。 多虧了這一點,您無需關心某些關鍵庫的重大更新。

當然,稍後,您可以通過運行npm run eject自己“彈出”並處理每個方面。 這種方法有它自己的優點,因為您可以使用原本不可用的東西(例如,裝飾器)來增強您的應用程序,但它也可能是一個令人頭疼的問題,因為它需要許多額外的文件和更多的時間。

所以,首先要做的是:

 npx create-react-app {app-name}

然後npm run start可以開始了。

類與函數組件

我們應該首先解釋這些組件的不同之處。 基本上,每個組件都可以是一個函數。 它們之間的主要區別在於,第一類具有一些功能組件中不可用的特性:它們可以有狀態並使用 refs、生命週期等。這是當前的播放狀態,從 16.7 版本開始(或者無論如何它會由於已經提到的更改而被調用),我們也將有鉤子,因此狀態和引用將可以使用鉤子。

類組件有兩種類型: ComponentPureComponent 。 兩者之間的唯一區別是PureComponent對 props 和 state 進行了淺層比較——在你不想進行“浪費”渲染的情況下,它有自己的好處,組件及其子組件處於完全相同的狀態渲染後。 不過,這只是一個膚淺的比較。 如果您想實現自己的比較(例如,因為您正在傳遞複雜的道具),只需使用Component並覆蓋shouldComponentUpdate (默認情況下返回 true)。 從 16.6+ 開始,函數組件也可以使用類似的東西——這要歸功於React.memo ,它是一個高階組件,默認行為類似於PureComponent (淺比較),但它需要第二個參數,您可以在其中傳遞您自己的自定義 props 比較.

作為一般的經驗法則,如果你可以使用函數組件(你不需要類特性),那麼就使用它。 很快,從 16.7.0 開始,由於生命週期方法,將只需要使用類組件。 我傾向於認為函數組件更透明,更容易推理和理解。

React 生命週期方法

安裝、更新和卸載組件的圖示

構造函數(道具)

  • 可選的,尤其是在 CRA 如此流行的情況下,其中默認包含 JavaScript 的類字段聲明。 如果您在類主體中通過箭頭函數綁定您的方法,那麼聲明是沒有意義的。 類似的狀態也可以初始化為類屬性。
  • 應該只用於初始化 ES6 類中對象和綁定方法的本地狀態。

組件DidMount()

  • 在此處進行 Ajax 調用。
  • 如果您需要事件偵聽器、訂閱等,請在此處添加它們。
  • 您可以在此處使用setState (但它會使組件重新渲染)。

組件WillUnmount()

  • 清除所有仍在進行中的東西——例如,應該中斷 Ajax、取消訂閱、清除計時器等等。
  • 不要調用setState ,因為它沒有意義,因為組件將被卸載(並且您會收到警告)。

componentDidUpdate(prevProps, prevState, 快照)

  • 在組件剛剛完成更新時發生(在初始渲染時不會發生)。
  • 具有三個可選使用的參數(先前的道具、先前的狀態和僅在您的組件實現getSnapshotBeforeUpdate時才會出現的快照)。
  • 僅當shouldComponentUpdate返回 true 時才會發生。
  • 如果你在這裡使用setState ,你應該保護它,否則你將陷入無限循環。

shouldComponentUpdate(nextProps, nextState)

  • 僅用於性能優化。
  • 如果它返回 false,則不會調用渲染。
  • 如果覆蓋的 SCO 只是淺的 props/state 比較,則可以使用PureComponent

getSnapshotBeforeUpdate()

  • 可用於保存有關當前 DOM 的一些信息,例如,當前滾動位置,稍後可以在componentDidUpdate中重用該信息以恢復滾動位置。

componentDidCatch(錯誤,信息)

  • 應該發生日誌記錄錯誤的地方。
  • 可以調用setState ,但在未來的版本中,它將被刪除以支持靜態方法getDerivedStateFromError(error) ,該方法將通過返回一個值來更新狀態以更新狀態。

有兩個額外的方法都是靜態的並且在其他解釋中提到過

靜態 getDerivedStateFromError(錯誤)

  • 此處提供了錯誤信息。
  • 應該返回一個對象值,該值將更新可用於處理錯誤的狀態(通過顯示某些內容)。
  • 由於它是靜態的,因此它無法訪問組件實例本身。

靜態 getSnapshotBeforeUpdate(道具,狀態)

  • 應該在 props 隨時間變化的情況下使用——例如,根據 React 文檔,它可能對轉換組件有用。
  • 由於它是靜態的,因此它無法訪問組件實例本身。

請注意,目前可用的方法很少,但它們應該在 React 17.0 中被刪除,所以這裡沒有提到它們。

狀態與道具

讓我們從Props開始,因為它們更容易和更快地解釋。 道具是傳遞給組件的屬性,稍後可以在其中重用以顯示信息/業務邏輯等。

 import React, { Component } from 'react'; export default class App extends Component { render() { return ( <div> <HelloWorld name="Someone :)"/> </div> ); } } const HelloWorld = (props) => <div>Hello {props.name}</div>

在上面的例子中, name是一個道具。 props 是只讀元素,不能在子組件中直接更改。 另外,人們經常做的一種不好的做法是,將 props 複製到 state 並在之後對 state 進行操作。 當然,在某些情況下,您想要執行諸如“提交後將更新父組件的初始狀態”之類的操作,但這比較少見——在這種情況下,初始狀態饋送可能是有意義的。 此外,不僅可以將字符串等屬性傳遞給子組件,還可以將數字、對象、函數等傳遞給子組件。

Props 還有一個更有用的東西,稱為defaultProps ,它是一個靜態字段,可以告訴您組件的默認 props 是什麼(例如,當它們沒有傳遞給組件時)。

在“提升狀態”的情況下,其中一個組件(父組件)的狀態稍後會被其子組件重用(例如,一個子組件顯示它而另一個允許編輯),那麼我們需要將函數傳遞給子組件parent,它允許我們更新父級的本地狀態。

另一方面, State是可以修改的本地狀態,但可以通過使用this.setState間接修改。 如果有人直接改變狀態,組件將不會意識到更改,並且不會重新渲染以反映提到的狀態更改。

SetState是一種更改本地狀態對象的方法(通過進行淺合併),之後,組件通過重新渲染自身來響應它。 請注意,在使用setState後, this.state屬性不會立即反映函數中提到的更改(它具有異步性質),因為由於優化,可能會將一些setState實例批處理在一起。 它有幾種調用方式,其中一種可能性允許我們在更新狀態後立即對組件執行某​​些操作:

  • setState({value: '5'})
  • setState((state, props) => ({value: state.name + “'s”}))
  • setState([object / function like above], () => {}) - 這種形式允許我們附加callback ,當狀態將反映我們想要擁有的數據時(在第一個參數中)將調用它。
 import React, { Component } from 'react'; export default class App extends Component { state = { name: 'Someone :)' } onClick = () => this.setState({ name: 'You' }) render() { return ( <div> <HelloWorld name={this.state.name} onClick={this.onClick}/> </div> ); } } const HelloWorld = (props) => <div onClick={props.onClick}>Hello {props.name}</div>

反應上下文

React 最近穩定了 Context API(它在 React 中已經存在了很長一段時間,但儘管被 Redux 等一些最流行的庫廣泛使用,但它還是一項實驗性功能),它幫助我們解決了一個問題:道具鑽取。 簡而言之,道具鑽探是一種在結構中深入傳遞道具的方法——例如,它可以是組件的某種主題、特定語言的本地化、用戶信息等。 在 Context 之前(或者更確切地說,在它成為非實驗性之前),它是通過從父級到子級以遞歸方式傳遞到最後一片葉子來深入研究的(顯然 Redux 也可以解決這個問題)。 請注意,此功能僅解決道具鑽孔問題,不能替代 Redux 或 Mobx 之類的東西。 顯然,如果您只是為此使用狀態管理庫,那麼您可以自由替換它。

包起來

我們的 React 教程的第一部分到此結束。 在接下來的文章中,我們希望解決更高級的主題,從樣式和檢查類型,到生產部署和性能優化。

相關:維護控制:Webpack 和 React 指南,Pt。 1