React 教程:快速上手指南

前端先鋒發表於2019-02-27

翻譯:瘋狂的技術宅 原文:www.toptal.com/react/react…

前端和 JavaScript 是一個奇怪的世界。大量不斷推出的新技術的同時,也在被不需要它們的人嘲笑,往往很多人都會這樣做。我們有時會對不斷湧現的資訊、庫和討論感到不知所措,總希望能有一些穩定的東西,就像能讓我們可以休整一段時間的避風港。最近 React 似乎有變成 JavaScript 演變海洋中溫暖港灣的趨勢。

正是考慮到這一點,我們決定製作這個 React 系列教程,展示它的功能,並看看它與 Angular 和 VueJS 相比有什麼特點。

React 是一座聳立在 JavaScript 程式碼海上醒目的燈塔

當然 React 並不是唯一的選擇,但目前它是最受歡迎、最穩定、最具有創新性的解決方案之一,雖然它仍然在不斷升級,但更多的是在改進,而不是增加功能。

2019年的 React

React 是一個檢視庫,可以一直追溯到2011年,當時它的第一個原型名為 FaxJs,並出現在 Facebook 上,React 是由 Jordan Walke(他也是上述原型的作者)於2013年5月29日在 JSConfUS 推出的,並於2013年7月2日在 GitHub 上公開發布。

在2014年,當開始擴大社群並推廣 React 時,它受到持續歡迎。然而從我的角度來看,2015年是大型公司(例如 Airbnb 和 Netflix )開始喜歡並採用 React 的里程碑年。此外,當年還出現了React Native。 React Native背後的想法並不是什麼全新的東西,不過看起來很有趣,尤其是因為它得到了 Facebook 的支援。

另一個重大變化是 Redux,一個 Flux 實現。這使狀態管理方式更加簡單友好,使其成為迄今為止最成功的實現。

從其出現一直到現在,還有很多其他的東西供我們使用,包括 React tools,重寫了核心演算法;Fiber 用於語義轉換版本控制等等。到了今天,我們處於 v16.6.3,幾周後可能就會發布支援 Hooks 的新版本(它應該在 16.7.0 得到支援,但由於對 React.lazy 做了一些修復,就先發布了一個版本)。React 由於其名氣和穩定性獲得了廣泛好評。

但 React 到底是什麼?

好吧,如果你身為前端開發人員但是從來都沒有聽說過,那麼我就要說聲“恭喜你”,因為這是一個了不起的壯舉。

開個玩笑而已。React 是一個宣告式的基於元件的檢視庫,可以幫助你構建 UI。它是一個庫而不是一個框架,雖然最初很多人把它描述為後者。

顯然,如果我們要把 Redux 和 React Router 等新增到 React,它就擁有了製作常規單頁應用程式所需的所有東西,這可能這就是它有時被錯誤地描述為框架而不是庫的原因 。如果一定要這樣認為的話,將該環境的所有元件放在一起,術語“框架”可能有點適合它,但就其本身而言,React 僅僅是一個庫。

不要再糾結改怎麼對其分類了,先關注 React 有什麼獨特之處,一些之前沒有注意到的東西。首先,當你第一次看到 React 時,就會想到 JSX,因為這是你看到程式碼時的第一個感受。 JSX是一種 JavaScript 語法擴充套件,有點類似於 HTML/XML。說到 React 和 JSX,它們與 HTML 有一些區別,例如,React 中的類是 className,沒有tabindex 但是有 tabIndex,樣式接受具有駝峰命名的屬性的 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 中(更確切地說是在瀏覽器中)支援的東西,或者以某種方式對 JavaScript 進行擴充套件(或者類似於 TypeScript,Babel 從 Babel 7 開始支援的不同語言)。感謝Babel:

  • JSX 將被轉化為成瀏覽器可以理解的程式碼。
  • 我們可以使用尚未在瀏覽器中實現的新功能(例如類屬性)。
  • 我們可以支援新瀏覽器中的特性,同時在舊瀏覽器中支援較舊的功能。

簡而言之,在 JavaScript 中,就是今天的程式碼明天仍然能用;這可能需要為此專門再寫一篇文章。值得一提的是,React 的匯入也可以被一些其他技術繞過(比如通過 Webpack 引入 ProvidePlugin 等),但是由於篇幅有限,我們將避免使用這種方式,並假設使用者將使用 Create React App( CRA)(稍後將提到有關此工具的更多資訊)。

另一點比 JSX 本身更重要,那就是 React 基於虛擬 DOM。簡而言之,虛擬 DOM 是用 JavaScript 編寫的在記憶體中的理想樹結構,稍後我們會把它與真實 DOM 進行比較。

怎樣與 Angular 和 Vue 進行比較?

我很不喜歡對庫進行比較,特別是當我們被迫把梨和蘋果放在一起進行比較時。

因此,我將嘗試使用一系列簡短的問題和答案將 React 與 AngularVue 進行比較。這種比較與技術相關,而不是主觀的作出 “X比Y更好,因為它使用 JSX 而不是模板。” 這種出於個人偏好的對比。另外在速度和記憶體分配等方面 React 與其主要競爭對手(Angular 和 Vue 都能想得到)非常相似,有一篇關於這個問題的文章很不錯,但請記住這一點:絕大多數程式並不會做這種處理上萬行資料的事。因此,這些結果也是純粹的速度實驗。實際上你也不會把這放在首位。

React vs. Angular vs. Vue.js

那麼讓我們來看看關於 React 的問題以及它與競爭對手的比較:

我想擁有更多的工作機會。 React 到底有多受歡迎?

嗯,這很容易回答 —— 選擇 React。實際上,我會說 React 的工作機會大約其它的 6 到 10 (可能出入比較大,在一些大網站是 50 倍,也有些網站是 6 倍),是 Vue 的 2 到 4倍,比 Angular 更多。對 React experts 的需求很大,但是為什麼 Vue 在 GitHub 上非常受歡迎(實際上它獲得了比 React 更多的star)卻沒有更多的職位空缺?這點我不知道。【譯者注:作者是美國人,這裡指的是美國的就業市場】

我想要一個很大的社群,還有大量的庫,能夠快速解決可能出現的問題。 選 React,不要再猶豫了。

它是否容易使用,開發過程是否令人愉快?

2018年和2017年的 JS 狀態報告顯示,React 和 Vue 都享有良好的聲譽,大多數開發人員表示會再次使用。另一方面Angular 有一種趨勢,每年都會有越來越多的人說不會再次使用它。

我想建立一個新的單面應用,但我不想額外去找這種支援庫。

我認為這大概是 Angular 值得選擇的唯一原因。

我不是大公司。但是希望儘可能獨立,應該選擇哪個?

Vue —— 它是我們三巨頭中唯一獨立的一個。 ( Facebook 支援 React,而 Google 支援 Angular。)

上手最簡單和最快的學習曲線?

Vue/React。在這裡我更傾向於 Vue,但這只是我個人的意見。至於為什麼?因為你不需要懂 JSX(它是可選的),它基本上只是 HTML + CSS + JavaScript。

React Tutorial:開始你的第一個程式

React tutorial:成功建立 React 應用後的螢幕截圖

目前上手 React 最簡單方法是使用 CRA,這是一個為你建立專案的 CLI 工具,可幫助你避免配置 Webpack / Babel 等環境。你只需要依賴默的認配置方式,並隨著時間的推移更新包含在內的內容。多虧了這一點,你無需關心某些關鍵庫的主要更新。

當然,稍後,你可以通過執行 npm run eject 來“彈出”自己並自己處理每個細節。這種方法有其自身的優點,因為你可以增加原來不可用的東西(例如裝飾器)來增強你的應用,但它也可能是令人頭疼的問題,因為它需要花費更多的時間去配置許多額外的檔案。

所以,首先要做的是:

npx create-react-app {app-name}
複製程式碼

然後 npm run start 就完成了。

類元件與函式元件

我們應該先解釋這些元件的不同之處。基本上每個元件可以是 functionclass。它們之間的主要區別在於,類元件有函式元件中沒有的一些功能:它們有 state 並使用 refs、生命週期等。從 v16.7 開始我們可以使用 hooks,因此可以使用 hooks 來進行 state 和 refs。

類元件有兩種型別:ComponentPureComponent。它們唯一的區別是 PureComponent 可以對 props 和 state 進行淺層比較 —— 這在你不想“浪費”渲染資源的情況下有獨到的好處,一個元件及其子元件恰好在渲染後處於相同狀態。不過它只有一個淺層比較;如果你想實現自己的比較操作(假如你傳遞的是複雜的 props),只需要用 Component 並覆蓋 shouldComponentUpdate(預設情況下返回true)。從 16.6 + 開始,在函式元件中也可以用類似的東西 —— 全靠 React.memo 這個更高階的元件,在預設情況下表現得像 PureComponent(淺層比較),在你進行自定義的 props 比較時它還需要第二個引數。

一般來說如果你能用函式元件(假設你不需要類功能)那麼就用它。不過從 16.7.0 開始,由於生命週期方法,只能用類元件。但是我認為函式元件更透明,更容易推理和理解。

React 生命週期方法

安裝、更新和解除安裝元件

Constructor(props)

  • 可選,CRA 使其變得受歡迎,預設包含 JavaScript 的類欄位宣告。宣告是否通過類中的箭頭函式去繫結方法是沒有意義的。類似的狀態也可以初始化為類屬性。
  • 僅用於 ES6 類中初始化物件的本地狀態和繫結方法。

componentDidMount()

  • 在這裡進行 Ajax 呼叫。
  • 如果你需要事件監聽器,訂閱等功能,可以在此處新增。
  • 你可以在這裡使用 setState(但是它會使元件重新渲染)。

componentWillUnmount()

  • 清除所有仍在進行的東西 —— 例如,Ajax應該被中斷,取消訂閱,清除定時器等等。
  • 不要呼叫 setState,因為它沒有意義,因為元件將會被解除安裝(並且你會得到一個警告)。

componentDidUpdate(prevProps, prevState, snapshot)

  • 在元件剛剛更新完畢時執行(在開始渲染時不會)。
  • 有三個可選的引數(以前的props,以前的 state 和只有在你的元件實現 getSnapshotBeforeUpdate 時才會出現的快照 )。
  • 僅當 shouldComponentUpdate 返回true時才會執行。
  • If you use setState here, you should guard it or you will land in an infinite loop.
  • 如果你在這裡用到了 setState,應該保護它,否則將會陷入無限迴圈。

shouldComponentUpdate(nextProps, nextState)

  • 僅用於效能優化。
  • 如果返回 false,則不會呼叫渲染器。
  • 如果重寫的 SCO 只是對 props/state的淺層比較,可以使用 PureComponent

getSnapshotBeforeUpdate()

  • 可用於儲存一些與當前 DOM 有關的資訊,例如當前的滾動位置,稍後可在 componentDidUpdate 中重用,用來恢復滾動的位置。

componentDidCatch(error, info)

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

還有兩種靜態方法,在其他的段落中提到過

static getDerivedStateFromError(error)

  • 此處提供錯誤資訊。
  • 應返回一個物件值,該值將會更新可用於處理錯誤的狀態(通過顯示內容)。
  • 由於它是靜態的,因此無法訪問元件例項本身。

static getSnapshotBeforeUpdate(props, state)

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

注意,目前還有更多可用的方法,但它們可能會在 React 17.0 中被刪除,所以就不在這裡沒有提起了。

State vs. Props

我們先從 Props 開始,因為它更容易解釋。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 是一個 prop。prop 是隻讀元素,不能直接在子元件中更改。很多人有一種不太好的習慣,那就是把 prop 複製到 state ,然後再對 state 進行操作。當然有時候你希望執行類似 “在提交之後去更新父元件的初始狀態” 這樣的操作,但這種情況非常少見 —— 在這種情況下,更新初始狀態可能有意義。另外不僅可以給子元件傳遞字串這樣的 prop ,還可以傳遞數字、物件、函式等。

prop 還有一個更有用的東西叫做 defaultProps,這是一個靜態欄位,它可以告訴你元件的預設 prop 是什麼(比如當它們沒有傳遞給元件時)。

在“狀態提升”的情況下,其中一個元件(父元件)具有稍後由其子元件重用的狀態(例如,一個子元件用來顯示而另一個用來編輯),那麼我們需要將該功能從父元件傳遞給子元件。 它允許我們更新父級的本地狀態。 另一方面,狀態是一個可以修改的本地狀態,但是通過 this.setState 間接修改。如果直接去改變狀態,元件將不會感知到,更不會因為狀態的改變而重新渲染。

**SetState **是一種更改本地狀態物件的方法(通過執行淺層合併),之後元件通過重新渲染自己來響應它。請注意,在使用 setState 之後,this.state 屬性不會立即對更改(它具有非同步性質)作出反應,因為優化的原因,可能會將 setState 的幾個例項一起進行批處理。呼叫它的方式有好幾種,其中一種方式允許我們在對狀態進行更新能夠後立即對元件執行某些操作:

  • setState({value: ‘5’})
  • setState((state, props) => ({value: state.name + “‘s”}))
  • setState([object / function like above], () => {}) —— 這個表單允許我們附加 callback,當 state 顯示我們想要的資料時被呼叫(在第一個引數)。
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

React 最近穩定的 Context API(已經在 React 中存在了相當長的時間,儘管被 Redux 等一些最受歡迎的庫廣泛使用,卻是一個實驗性功能)有助於我們解決一個問題:Props drilling。簡而言之 Props drilling 是在結構中深入傳遞 props 的一種方式 —— 例如,它可以是元件的某種主題、針對特定語言的本地化、使用者資訊等。在 Context出現之前(或者更確切地說,在它變成非實驗功能之前),它是通過遞迴方式從父級一直傳遞到到子級的最後一級的來進行鑽取的(顯然還有可以解決這個問題的 Redux)。請注意,此功能僅僅用於解決 Props drilling 的問題,並且不能替代 Redux 或 Mobx 等。當然如果你只使用狀態管理庫,則可以隨意替把它替換掉。

總結

這是我們的React教程的第一部分。在後續的文章中,我們會設計更多高階主題,包括樣式和型別檢查,以及生產部署和效能優化。

歡迎關注京程一燈公眾號;jingchengyideng,獲取更多前端乾貨內容。

相關文章