學(xué)習(xí) React.js 比你想象的要簡(jiǎn)單

學(xué)習(xí) React.js 比你想象的要簡(jiǎn)單

通過(guò) Medium 中的一篇文章來(lái)學(xué)習(xí) React.js 的基本原理

你有沒(méi)有注意到在 React 的 logo 中隱藏著一個(gè)六角星?只是順便提下...
去年我寫(xiě)了一本簡(jiǎn)短的關(guān)于學(xué)習(xí) React.js 的書(shū),有 100 頁(yè)左右。今年,我要挑戰(zhàn)自己 —— 將其總結(jié)成一篇文章,并向 Medium 投稿。

這篇文章不是講什么是 React 或者 你該怎樣學(xué)習(xí) React。這是在面向那些已經(jīng)熟悉了 JavaScript 和 DOM API 的人的 React.js 基本原理介紹

本文采用嵌入式 jsComplete 代碼段,所以為了方便閱讀,你需要一個(gè)合適的屏幕寬度。

下面所有的代碼都僅供參考。它們也純粹是為了表達(dá)概念而提供的例子。它們中的大多數(shù)有更好的實(shí)踐方式。

您可以編輯和執(zhí)行下面的任何代碼段。使用 Ctrl+Enter 執(zhí)行代碼。每一段的右下角有一個(gè)點(diǎn)擊后可以在 jsComplete/repl 進(jìn)行全屏模式編輯或運(yùn)行代碼的鏈接。


1 React 全部都是組件化的

React 是圍繞可重用組件的概念設(shè)計(jì)的。你定義小組件并將它們組合在一起形成更大的組件。

無(wú)論大小,所有組件都是可重用的,甚至在不同的項(xiàng)目中也是如此。

React 組件最簡(jiǎn)單的形式,就是一個(gè)普通的 JavaScript 函數(shù):

function Button (props) {
  // 這里返回一個(gè) DOM 元素,例如:
  return <button type="submit">{props.label}</button>;
}
// 將按鈕組件呈現(xiàn)給瀏覽器
ReactDOM.render(<Button label="Save" />, mountNode)

例 1:編輯上面的代碼并按 Ctrl+Enter 鍵執(zhí)行(譯者注:譯文暫時(shí)沒(méi)有這個(gè)功能,請(qǐng)?jiān)L問(wèn)原文使用此功能,下同)

括號(hào)中的 button 標(biāo)簽將稍后解釋?,F(xiàn)在不要擔(dān)心它們。ReactDOM 也將稍后解釋?zhuān)绻阆霚y(cè)試這個(gè)例子和所有接下來(lái)的例子,上述 render 函數(shù)是必須的。(React 將要接管和控制的是 ReactDOM.render 的第 2 個(gè)參數(shù)即目標(biāo) DOM 元素)。在 jsComplete REPL 中,你可以使用特殊的變量 mountNode。

例 1 的注意事項(xiàng):

  • 組件名稱(chēng)首字母大寫(xiě),Button。必須要這樣做是因?yàn)槲覀儗⑻幚?HTML 元素和 React 元素的混合。小寫(xiě)名稱(chēng)是為 HTML 元素保留的。事實(shí)上,將 React 組件命名為 “button” 然后你就會(huì)發(fā)現(xiàn) ReactDOM 會(huì)忽略這個(gè)函數(shù),僅僅是將其作為一個(gè)普通的空 HTML 按鈕來(lái)渲染。
  • 每個(gè)組件都接收一個(gè)屬性列表,就像 HTML 元素一樣。在 React 中,這個(gè)列表被稱(chēng)為屬性。雖然你可以將一個(gè)函數(shù)隨意命名。
  • 在上面 Button 函數(shù)組件的返回輸出中,我們奇怪地寫(xiě)了段看上去像 HTML 的代碼。這實(shí)際上既不是 JavaScript 也不是 HTML,老實(shí)說(shuō),這甚至不是 React.js。然而它非常流行,以至于成為 React 應(yīng)用程序中的默認(rèn)值。這就是所謂的 JSX,這是一個(gè)JavaScript 的擴(kuò)展。JSX 也是一個(gè)折中方案!繼續(xù)嘗試并在上面的函數(shù)中返回其他 HTML 元素,看看它們是如何被支持的(例如,返回一個(gè)文本輸入元素)。

2 JSX 輸出的是什么?

上面的例 1 可以用沒(méi)有 JSX 的純 React.js 編寫(xiě),如下:

function Button (props) {
  return React.createElement(
    "button",
    { type: "submit" },
    props.label
  );
}

// 要使用 Button,你可以這么做
ReactDOM.render(
  React.createElement(Button, { label: "Save" }),
  mountNode
);

例 2:不使用 JSX 編寫(xiě) React 組件

在 React 頂級(jí) API 中,createElement 函數(shù)是主函數(shù)。這是你需要學(xué)習(xí)的 7 個(gè) API 中的 1 個(gè)。React 的 API 就是這么小。

就像 DOM 自身有一個(gè) document.createElement 函數(shù)來(lái)創(chuàng)建一個(gè)由標(biāo)簽名指定的元素一樣,React 的 createElement 函數(shù)是一個(gè)高級(jí)函數(shù),有和 document.createElement 同樣的功能,但它也可以用于創(chuàng)建一個(gè)表示 React 組件的元素。當(dāng)我們使用上面例 2 中的按鈕組件時(shí),我們使用的是后者。

不像 document.createElement,React 的 createElement 在接收第二個(gè)參數(shù)后,接收一個(gè)動(dòng)態(tài)參數(shù),它表示所創(chuàng)建元素的子元素。所以 createElement 實(shí)際上創(chuàng)建了一個(gè)樹(shù)。

這里就是這樣的一個(gè)例子:

const InputForm = React.createElement(
  "form",
  { target: "_blank", action: "https://google.com/search" },
  React.createElement("div", null, "Enter input and click Search"),
  React.createElement("input", { className: "big-input" }),
  React.createElement(Button, { label: "Search" })
);

// InputForm 使用 Button 組件,所以我們需要這樣做:
function Button (props) {
  return React.createElement(
    "button",
    { type: "submit" },
    props.label
  );
}

// 然后我們可以通過(guò) .render 方法直接使用 InputForm
ReactDOM.render(InputForm, mountNode);

例 3:React 創(chuàng)建元素的 API

上面例子中的一些事情值得注意:

  • InputForm 不是一個(gè) React 組件;它僅僅是一個(gè) React 元素。這就是為什么我們可以在 ReactDOM.render 中直接使用它并且可以在調(diào)用時(shí)不使用 <InputForm /> 的原因。
  • React.createElement 函數(shù)在前兩個(gè)參數(shù)后接收了多個(gè)參數(shù)。從第3個(gè)參數(shù)開(kāi)始的參數(shù)列表構(gòu)成了創(chuàng)建元素的子項(xiàng)列表。
  • 我們可以嵌套 React.createElement 調(diào)用,因?yàn)樗?JavaScript。
  • 當(dāng)這個(gè)元素不需要屬性時(shí),React.createElement 的第二個(gè)參數(shù)可以為空或者是一個(gè)空對(duì)象。
  • 我們可以在 React 組件中混合 HTML 元素。你可以將 HTML 元素作為內(nèi)置的 React 組件。
  • React 的 API 試圖和 DOM API 一樣,這就是為什么我們?cè)?input 元素中使用 className 代替 class 的原因。我們都希望如果 React 的 API 成為 DOM API 本身的一部分,因?yàn)?,你知道,它要好得多?/li>

上述的代碼是當(dāng)你引入 React 庫(kù)的時(shí)候?yàn)g覽器是怎樣理解的。瀏覽器不會(huì)處理任何 JSX 業(yè)務(wù)。然而,我們更喜歡看到和使用 HTML,而不是那些 createElement 調(diào)用(想象一下只使用 document.createElement 構(gòu)建一個(gè)網(wǎng)站?。_@就是 JSX 存在的原因。取代上述調(diào)用 React.createElement 的方式,我們可以使用一個(gè)非常簡(jiǎn)單類(lèi)似于 HTML 的語(yǔ)法:

const InputForm =
  <form target="_blank" action="https://google.com/search">
    <div>Enter input and click Search</div>
    <input className="big-input" name="q" />
    <Button label="Search" />
  </form>;

// InputForm “仍然”使用 Button 組件,所以我們也需要這樣。
// JXS 或者普通的表單都會(huì)這樣做
function Button (props) {
  // 這里返回一個(gè) DOM 元素。例如:
  return <button type="submit">{props.label}</button>;
}

// 然后我們可以直接通過(guò) .render 使用 InputForm
ReactDOM.render(InputForm, mountNode);

例 4:為什么在 React 中 JSX 受歡迎(和例 3 相比)

注意上面的幾件事:

  • 這不是 HTML 代碼。比如,我們?nèi)匀豢梢允褂?className 代替 class。
  • 我們?nèi)栽诳紤]怎樣讓上述的 JavaScript 看起來(lái)像是 HTML??匆幌挛以谧詈笫窃鯓犹砑拥摹?/li>

我們?cè)谏厦妫ɡ?4)中寫(xiě)的就是 JSX。然而,我們要將編譯后的版本(例 3)給瀏覽器。要做到這一點(diǎn),我們需要使用一個(gè)預(yù)處理器將 JSX 版本轉(zhuǎn)換為 React.createElement 版本。

這就是 JSX。這是一種折中的方案,允許我們用類(lèi)似 HTML 的語(yǔ)法來(lái)編寫(xiě)我們的 React 組件,這是一個(gè)很好的方法。

“Flux” 在頭部作為韻腳來(lái)使用,但它也是一個(gè)非常受歡迎的 應(yīng)用架構(gòu),由 Facebook 推廣。最出名的是 Redux,F(xiàn)lux 和 React 非常合適。

JSX,可以單獨(dú)使用,不僅僅適用于 React。

3 你可以在 JavaScript 的任何地方使用 JSX

在 JSX 中,你可以在一對(duì)花括號(hào)內(nèi)使用任何 JavaScript 表達(dá)式。

const RandomValue = () =>
  <div>
    { Math.floor(Math.random() * 100) }
  </div>;

// 使用:
ReactDOM.render(<RandomValue />, mountNode);

例 5:在 JSX 中使用 JavaScript 表達(dá)式

任何 JavaScript 表達(dá)式可以直接放在花括號(hào)中。這相當(dāng)于在 JavaScript 中插入 ${} 模板

這是 JSX 內(nèi)唯一的約束:只有表達(dá)式。例如,你不能使用 if 語(yǔ)句,但三元表達(dá)式是可以的。

JavaScript 變量也是表達(dá)式,所以當(dāng)組件接受屬性列表時(shí)(不包括 RandomValue 組件,props 是可選擇的),你可以在花括號(hào)里使用這些屬性。我們?cè)谏鲜觯ɡ?1)的 Button 組件是這樣使用的。

JavaScript 對(duì)象也是表達(dá)式。有些時(shí)候我們?cè)诨ɡㄌ?hào)中使用 JavaScript 對(duì)象,這看起來(lái)像是使用了兩個(gè)花括號(hào),但是在花括號(hào)中確實(shí)只有一個(gè)對(duì)象。其中一個(gè)用例就是將 CSS 樣式對(duì)象傳遞給響應(yīng)中的特殊樣式屬性:

const ErrorDisplay = ({message}) =>
  <div style={ { color: 'red', backgroundColor: 'yellow' } }>
    {message}
  </div>;

// 使用
ReactDOM.render(
  <ErrorDisplay
    message="These aren't the droids you're looking for"
  />,
  mountNode
);

例 6:一個(gè)對(duì)象傳遞特殊的 React 樣式參數(shù)

注意我解構(gòu)的只是在屬性參數(shù)之外的信息。這只是 JavaScript。還要注意上面的樣式屬性是一個(gè)特殊的屬性(同樣,它不是 HTML,它更接近 DOM API)。我們使用一個(gè)對(duì)象作為樣式屬性的值并且這個(gè)對(duì)象定義樣式就像我們使用 JavaScript 那樣(我們可以這樣做)。

你可以在 JSX 中使用 React 元素。因?yàn)檫@也是一個(gè)表達(dá)式(記住,一個(gè) React 元素只是一個(gè)函數(shù)調(diào)用):

const MaybeError = ({errorMessage}) =>
  <div>
    {errorMessage && <ErrorDisplay message={errorMessage} />}
  </div>;

// MaybeError 組件使用 ErrorDisplay 組件
const ErrorDisplay = ({message}) =>
  <div style={ { color: 'red', backgroundColor: 'yellow' } }>
    {message}
  </div>;

// 現(xiàn)在我們使用 MaybeError 組件:
ReactDOM.render(
  <MaybeError
    errorMessage={Math.random() > 0.5 ? 'Not good' : ''}
  />,
  mountNode
);

例 7:一個(gè) React 元素是一個(gè)可以通過(guò) {} 使用的表達(dá)式

上述 MaybeError 組件只會(huì)在有 errorMessage 傳入或者另外有一個(gè)空的 div 才會(huì)顯示 ErrorDisplay 組件。React 認(rèn)為 {true}{false}
{undefined}{null} 是有效元素,不呈現(xiàn)任何內(nèi)容。

我們也可以在 JSX 中使用所有的 JavaScript 的集合方法(map、reduce 、filter、 concat 等)。因?yàn)樗麄兎祷氐囊彩潜磉_(dá)式:

const Doubler = ({value=[1, 2, 3]}) =>
  <div>
    {value.map(e => e * 2)}
  </div>;

// 使用下面內(nèi)容 
ReactDOM.render(<Doubler />, mountNode);

例 8:在 {} 中使用數(shù)組

請(qǐng)注意我是如何給出上述 value 屬性的默認(rèn)值的,因?yàn)檫@全部都只是 JavaScript。注意我只是在 div 中輸出一個(gè)數(shù)組表達(dá)式。React 是完全可以的。它只會(huì)在文本節(jié)點(diǎn)中放置每一個(gè)加倍的值。

4 你可以使用 JavaScript 類(lèi)寫(xiě) React 組件

簡(jiǎn)單的函數(shù)組件非常適合簡(jiǎn)單的需求,但是有的時(shí)候我們需要的更多。React 也支持通過(guò)使用 JavaScript 類(lèi)來(lái)創(chuàng)建組件。這里 Button 組件(在例 1 中)就是使用類(lèi)的語(yǔ)法編寫(xiě)的。

class Button extends React.Component {
  render() {
    return <button>{this.props.label}</button>;
  }
}

// 使用(相同的語(yǔ)法)
ReactDOM.render(<Button label="Save" />, mountNode);

例 9:使用 JavaScript 類(lèi)創(chuàng)建組件

類(lèi)的語(yǔ)法是非常簡(jiǎn)單的:定義一個(gè)擴(kuò)展的 React.Component 類(lèi)(另一個(gè)你需要學(xué)習(xí)的 React 的頂級(jí) API)。該類(lèi)定義了一個(gè)單一的實(shí)例函數(shù) —— render(),并使函數(shù)返回虛擬 DOM 對(duì)象。每一次我們使用基于類(lèi)的 Button 組件(例如,通過(guò) <Button ... />),React 將從這個(gè)基于類(lèi)的組件中實(shí)例化對(duì)象,并在 DOM 樹(shù)中使用該對(duì)象。

這就是為什么上面的例子中我們可以在 JSX 中使用 this.props.label 渲染輸出的原因,因?yàn)槊恳粋€(gè)組件都有一個(gè)特殊的稱(chēng)為 props實(shí)例 屬性,這讓所有的值傳遞給該組件時(shí)被實(shí)例化。

由于我們有一個(gè)與組件的單個(gè)使用相關(guān)聯(lián)的實(shí)例,所以我們可以按照自己的意愿定制該實(shí)例。例如,我們可以通過(guò)使用常規(guī) JavaScript 構(gòu)造函數(shù)來(lái)構(gòu)造它:

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.id = Date.now();
  }
  render() {
    return <button id={this.id}>{this.props.label}</button>;
  }
}

// 使用
ReactDOM.render(<Button label="Save" />, mountNode);

例 10:自定義組件實(shí)例

我們也可以定義類(lèi)的原型并且在任何我們希望的地方使用,包括在返回的 JSX 輸出的內(nèi)部:

class Button extends React.Component {
  clickCounter = 0;

  handleClick = () => {
    console.log(`Clicked: ${++this.clickCounter}`);
  };

  render() {
    return (
      <button id={this.id} onClick={this.handleClick}>
        {this.props.label}
      </button>
    );
  }
}

// 使用
ReactDOM.render(<Button label="Save" />, mountNode);

例 11:使用類(lèi)的屬性(通過(guò)單擊保存按鈕進(jìn)行測(cè)試)

注意上述例 11 中的幾件事情

  • handleClick 函數(shù)使用 JavaScript 新提出的 class-field syntax 語(yǔ)法。這仍然是 stage-2,但是這是訪問(wèn)組件安裝實(shí)例(感謝箭頭函數(shù))最好的選擇(因?yàn)楹芏嘣颍?。然而,你需要使用?lèi)似 Babel 的編譯器解碼為 stage-2(或者僅僅是類(lèi)字段語(yǔ)法)來(lái)讓上述代碼工作。 jsComplete REPL 有預(yù)編譯功能。
// 錯(cuò)誤:
onClick={this.handleClick()}

// 正確:
onClick={this.handleClick}

5 React 中的事件:兩個(gè)重要的區(qū)別

當(dāng)處理 React 元素中的事件時(shí),我們與 DOM API 的處理方式有兩個(gè)非常重要的區(qū)別:

  • 所有 React 元素屬性(包括事件)都使用 camelCase 命名,而不是 lowercase。例如是 onClick 而不是 onclick。
  • 我們將實(shí)際的 JavaScript 函數(shù)引用傳遞給事件處理程序,而不是字符串。例如是 onClick={handleClick} 而不是 onClick="handleClick"

React 用自己的對(duì)象包裝 DOM 對(duì)象事件以?xún)?yōu)化事件處理的性能,但是在事件處理程序內(nèi)部,我們?nèi)匀豢梢栽L問(wèn) DOM 對(duì)象上所有可以訪問(wèn)的方法。React 將經(jīng)過(guò)包裝的事件對(duì)象傳遞給每個(gè)調(diào)用函數(shù)。例如,為了防止表單提交默認(rèn)提交操作,你可以這樣做:

class Form extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault();
    console.log('Form submitted');
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <button type="submit">Submit</button>
      </form>
    );
  }
}

// 使用
ReactDOM.render(<Form />, mountNode);

例 12:使用包裝過(guò)的對(duì)象

6 每一個(gè) React 組件都有一個(gè)故事:第 1 部分

以下僅適用于類(lèi)組件(擴(kuò)展 React.Component)。函數(shù)組件有一個(gè)稍微不同的故事。

  1. 首先,我們定義了一個(gè)模板來(lái)創(chuàng)建組件中的元素。
  2. 然后,我們?cè)谀程幨褂?React。例如,在 render 內(nèi)部調(diào)用其他的組件,或者直接使用 ReactDOM.render。
  3. 然后,React 實(shí)例化一個(gè)對(duì)象然后給它設(shè)置 props 然后我們可以通過(guò) this.props 訪問(wèn)。這些屬性都是我們?cè)诘?2 步傳入的。
  4. 因?yàn)檫@些全部都是 JavaScript,constructor 方法將會(huì)被調(diào)用(如果定義的話)。這是我們稱(chēng)之為的第一個(gè):組件生命周期方法。
  5. 接下來(lái) React 計(jì)算渲染之后的輸出方法(虛擬 DOM 節(jié)點(diǎn))。
  6. 因?yàn)檫@是 React 第一次渲染元素,React 將會(huì)與瀏覽器連通(代表我們使用 DOM API)來(lái)顯示元素。這整個(gè)過(guò)程稱(chēng)為 mounting。
  7. 接下來(lái) React 調(diào)用另一個(gè)生命周期函數(shù),稱(chēng)為 componentDidMount。我們可以這樣使用這個(gè)方法,例如:在 DOM 上做一些我們現(xiàn)在知道的在瀏覽器中存在的東西。在此生命周期方法之前,我們使用的 DOM 都是虛擬的。
  8. 一些組件的故事到此結(jié)束,其他組件得到卸載瀏覽器 DOM 中的各種原因。在后一種情況發(fā)生時(shí),會(huì)調(diào)用另一個(gè)生命周期的方法,componentWillUnmount。
  9. 任何 mounted 的元素的狀態(tài)都可能會(huì)改變。該元素的父級(jí)可能會(huì)重新渲染。無(wú)論哪種情況,mounted 的元素都可能接收到不同的屬性集。React 的魔力就是這兒,我們實(shí)際上需要的正是 React 的這一點(diǎn)!在這一點(diǎn)之前,說(shuō)實(shí)話,我們并不需要 React。
  10. 組價(jià)的故事還在繼續(xù),但是在此之前,我們需要理解我所說(shuō)的這種狀態(tài)。

7 React 組件可以具有私有狀態(tài)

以下只適用于類(lèi)組件。我有沒(méi)有提到有人叫表象而已的部件 dumb?

狀態(tài)類(lèi)是任何 React 類(lèi)組件中的一個(gè)特殊字段。React 檢測(cè)每一個(gè)組件狀態(tài)的變化,但是為了 React 更加有效,我們必須通過(guò) React 的另一個(gè) API 改變狀態(tài)字段,這就是我們要學(xué)習(xí)的另一個(gè) API —— this.setState

class CounterButton extends React.Component {
  state = {
    clickCounter: 0,
    currentTimestamp: new Date(),
  };

  handleClick = () => {
    this.setState((prevState) => {
     return { clickCounter: prevState.clickCounter + 1 };
    });
  };

  componentDidMount() {
   setInterval(() => {
     this.setState({ currentTimestamp: new Date() })
    }, 1000);
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Click</button>
        <p>Clicked: {this.state.clickCounter}</p>
        <p>Time: {this.state.currentTimestamp.toLocaleString()}</p>
      </div>
    );
  }
}

// 使用
ReactDOM.render(<CounterButton />, mountNode);

例 13:setState 的 API

這可能是最重要的一個(gè)例子因?yàn)檫@將是你完全理解 React 基礎(chǔ)知識(shí)的方式。這個(gè)例子之后,還有一些小事情需要學(xué)習(xí),但從那時(shí)起主要是你和你的 JavaScript 技能。

讓我們來(lái)看一下例 13,從類(lèi)開(kāi)始,總共有兩個(gè),一個(gè)是一個(gè)初始化的有初始值為 0clickCounter 對(duì)象和一個(gè)從 new Date() 開(kāi)始的 currentTimestamp

另一個(gè)類(lèi)是 handleClick 函數(shù),在渲染方法中我們給按鈕元素傳入 onClick 事件。通過(guò)使用 setStatehandleClick 方法修改了組件的實(shí)例狀態(tài)。要注意到這一點(diǎn)。

另一個(gè)我們修改狀態(tài)的地方是在一個(gè)內(nèi)部的定時(shí)器,開(kāi)始在內(nèi)部的 componentDidMount 生命周期方法。它每秒鐘調(diào)用一次并且執(zhí)行另一個(gè)函數(shù)調(diào)用 this.setState。

在渲染方法中,我們使用具有正常讀語(yǔ)法的狀態(tài)上的兩個(gè)屬性(沒(méi)有專(zhuān)門(mén)的 API)。

現(xiàn)在,注意我們更新?tīng)顟B(tài)使用兩種不同的方式:

  1. 通過(guò)傳入一個(gè)函數(shù)然后返回一個(gè)對(duì)象。我們?cè)?handleClick 函數(shù)內(nèi)部這樣做。
  2. 通過(guò)傳入一個(gè)正則對(duì)象,我們?cè)谠趨^(qū)間回調(diào)中這樣做。

這兩種方式都是可以接受的,但是當(dāng)你同時(shí)讀寫(xiě)狀態(tài)時(shí),第一種方法是首選的(我們這樣做)。在區(qū)間回調(diào)中,我們只向狀態(tài)寫(xiě)入而不讀它。當(dāng)有問(wèn)題時(shí),總是使用第一個(gè)函數(shù)作為參數(shù)語(yǔ)法。伴隨著競(jìng)爭(zhēng)條件這更安全,因?yàn)?setstate 實(shí)際上是一個(gè)異步方法。

我們應(yīng)該怎樣更新?tīng)顟B(tài)呢?我們返回一個(gè)有我們想要更新的的值的對(duì)象。注意,在調(diào)用 setState 時(shí),我們?nèi)慷紡臓顟B(tài)中傳入一個(gè)屬性或者全都不。這完全是可以的因?yàn)?setState 實(shí)際上 合并 了你通過(guò)它(返回值的函數(shù)參數(shù))與現(xiàn)有的狀態(tài),所以,沒(méi)有指定一個(gè)屬性在調(diào)用 setState 時(shí)意味著我們不希望改變屬性(但不刪除它)。

8 React 將要反應(yīng)

React 的名字是從狀態(tài)改變的反應(yīng)中得來(lái)的(雖然沒(méi)有反應(yīng),但也是在一個(gè)時(shí)間表中)。這里有一個(gè)笑話,React 應(yīng)該被命名為Schedule

然而,當(dāng)任何組件的狀態(tài)被更新時(shí),我們用肉眼觀察到的是對(duì)該更新的反應(yīng),并自動(dòng)反映了瀏覽器 DOM 中的更新(如果需要的話)。

將渲染函數(shù)的輸入視為兩種:

  • 通過(guò)父元素傳入的屬性
  • 以及可以隨時(shí)更新的內(nèi)部私有狀態(tài)

當(dāng)渲染函數(shù)的輸入改變時(shí),輸出可能也會(huì)改變。

React 保存了渲染的歷史記錄,當(dāng)它看到一個(gè)渲染與前一個(gè)不同時(shí),它將計(jì)算它們之間的差異,并將其有效地轉(zhuǎn)換為在 DOM 中執(zhí)行的實(shí)際 DOM 操作。

9 React 是你的代碼

您可以將 React 看作是我們用來(lái)與瀏覽器通信的代理。以上面的當(dāng)前時(shí)間戳顯示為例。取代每一秒我們都需要手動(dòng)去瀏覽器調(diào)用 DOM API 操作來(lái)查找和更新 p#timestamp 元素,我們僅僅改變組件的狀態(tài)屬性,React 做的工作代表我們與瀏覽器的通信。我相信這就是為什么 React 這么受歡迎的真正原因;我們只是不喜歡和瀏覽器先生談話(以及它所說(shuō)的 DOM 語(yǔ)言的很多方言),并且 React 自愿?jìng)鬟f給我們,免費(fèi)的!

10 每一個(gè) React 組件都有一個(gè)故事:第 2 部分

現(xiàn)在我們知道了一個(gè)組件的狀態(tài),當(dāng)該狀態(tài)發(fā)生變化的時(shí)候,我們來(lái)了解一下關(guān)于這個(gè)過(guò)程的最后幾個(gè)概念。

  1. 當(dāng)組件的狀態(tài)被更新時(shí),或者它的父進(jìn)程決定更改它傳遞給組件的屬性時(shí),組件可能需要重新渲染。
  2. 如果后者發(fā)生,React 會(huì)調(diào)用另一個(gè)生命周期方法:componentWillReceiveProps。
  3. 如果狀態(tài)對(duì)象或傳遞的屬性改變了,React 有一個(gè)重要的決定要做:組件是否應(yīng)該在 DOM 中更新?這就是為什么它調(diào)用另一個(gè)重要的生命周期方法 shouldComponentUpdate 的原因 。此方法是一個(gè)實(shí)際問(wèn)題,因此,如果需要自行定制或優(yōu)化渲染過(guò)程,則必須通過(guò)返回 true 或 false 來(lái)回答這個(gè)問(wèn)題。
  4. 如果沒(méi)有自定義 shouldComponentUpdate,React 的默認(rèn)事件在大多數(shù)情況下都能處理的很好。
  5. 首先,這個(gè)時(shí)候會(huì)調(diào)用另一生命周期的方法:componentWillUpdate。然后,React 將計(jì)算新渲染過(guò)的輸出,并將其與最后渲染的輸出進(jìn)行對(duì)比。
  6. 如果渲染過(guò)的輸出和之前的相同,React 不進(jìn)行處理(不需要和瀏覽器先生對(duì)話)。
  7. 如果有不同的地方,React 將不同傳達(dá)給瀏覽器,像我們之前看到的那樣。
  8. 在任何情況下,一旦一個(gè)更新程序發(fā)生了,無(wú)論以何種方式(即使有相同的輸出),React 會(huì)調(diào)用最后的生命周期方法:componentDidUpdate

生命周期方法是逃生艙口。如果你沒(méi)有做什么特別的事情,你可以在沒(méi)有它們的情況下創(chuàng)建完整的應(yīng)用程序。它們非常方便地分析應(yīng)用程序中正在發(fā)生的事情,并進(jìn)一步優(yōu)化 React 更新的性能。


信不信由你,通過(guò)上面所學(xué)的知識(shí)(或部分知識(shí)),你可以開(kāi)始創(chuàng)建一些有趣的 React 應(yīng)用程序。如果你渴望更多,看看我的 Pluralsight 的 React.js 入門(mén)課程。

感謝閱讀。如果您覺(jué)得這篇文章有幫助,請(qǐng)點(diǎn)擊原文中的 ??。請(qǐng)關(guān)注我的更多關(guān)于 React.js 和 JavaScript 的文章。


PluralsightLynda 創(chuàng)建了在線課程。我最新的文章在Advanced React.js、 Advanced Node.jsLearning Full-stack JavaScript中。我也做小組的在線和現(xiàn)場(chǎng)培訓(xùn),覆蓋初級(jí)到高級(jí)的 JavaScript、 Node.js、 React.js、GraphQL。如果你需要一個(gè)導(dǎo)師,請(qǐng)來(lái)找我 。如果你對(duì)此篇文章或者我寫(xiě)的其他任何文章有疑問(wèn),通過(guò)這個(gè)聯(lián)系我,并且在 #questions 中提問(wèn)。


感謝很多檢驗(yàn)和改進(jìn)這篇文章的讀者,?ukasz Szewczak、Tim Broyles、 Kyle Holden、 Robert Axelse、 Bruce Lane、Irvin Waldman 和 Amie Wilt.

特別要感謝“驚人的” Amie,經(jīng)驗(yàn)是一個(gè)實(shí)際的 Unicorn。謝謝你所有的幫助,Anime,真的非常感謝你。


掘金翻譯計(jì)劃 是一個(gè)翻譯優(yōu)質(zhì)互聯(lián)網(wǎng)技術(shù)文章的社區(qū),文章來(lái)源為 掘金 上的英文分享文章。內(nèi)容覆蓋 Android、iOS、React、前端、后端、產(chǎn)品、設(shè)計(jì) 等領(lǐng)域,想要查看更多優(yōu)質(zhì)譯文請(qǐng)持續(xù)關(guān)注 掘金翻譯計(jì)劃、官方微博、知乎專(zhuān)欄

最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 本筆記基于React官方文檔,當(dāng)前React版本號(hào)為15.4.0。 1. 安裝 1.1 嘗試 開(kāi)始之前可以先去co...
    Awey閱讀 7,913評(píng)論 14 128
  • 3. JSX JSX是對(duì)JavaScript語(yǔ)言的一個(gè)擴(kuò)展語(yǔ)法, 用于生產(chǎn)React“元素”,建議在描述UI的時(shí)候...
    pixels閱讀 2,971評(píng)論 0 24
  • 原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記,個(gè)人覺(jué)得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,931評(píng)論 1 18
  • 以下內(nèi)容是我在學(xué)習(xí)和研究React時(shí),對(duì)React的特性、重點(diǎn)和注意事項(xiàng)的提取、精練和總結(jié),可以做為React特性...
    科研者閱讀 8,396評(píng)論 2 21
  • 庭有枇杷樹(shù),吾妻死之年所手植也,今已亭亭如蓋矣。 吾妻乃吾垂髫之時(shí)青梅矣,相伴甚久,漸生情愫。 于其及笄之年...
    楚云端閱讀 702評(píng)論 0 2

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