您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么學好JavaScript”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
當你開始進入 React 的世界,create-react-app(https://github.com/facebook/create-react-app)通常會是你的***個 React 項目。在搭建好項目后,你將看到下面的這個 React 類組件:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2 className="App-title">Welcome to React</h2> </header> <p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p> </div> ); } } export default App;
React 類組件可能不是***的入手點。新手有許多東西需要消化,不一定與 React 有關:類語句、類方法和繼承。導入語句也會在學習 React 時增加額外的復雜性。盡管主要焦點應該放在 JSX(React 的語法)上,但其他的東西也需要解釋一番。這篇文章主要針對 JavaScript,所以請不用太擔心 React 相關的內容。
關于 React 類組件,需要用到有關 JavaScript 類的先驗知識。JavaScript 類的概念相對較新。之前,只有 JavaScript 的原型鏈可用于實現繼承。JavaScript 類以原型繼承為基礎,讓繼承體系變得更簡單。
定義 React 組件的一種方法是使用 JavaScript 類。
class Developer { constructor(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } getName() { return this.firstname + ' ' + this.lastname; } } var me = new Developer('Robin', 'Wieruch'); console.log(me.getName());
一個類描述了一個實體,用于創建實體的實例。在使用 new 語句創建類的實例時,會調用這個類的構造函數。類的屬性通常位于構造函數中。此外,類方法(例如 getName())用于讀取(或寫入)實例的數據。類的實例在類中使用 this 對象來表示,但在外部,僅指定給 JavaScript 變量。
在面向對象編程中,類通常用來實現繼承。在 JavaScript 中也一樣,extends 語句可用于讓一個類繼承另一個類。一個子類通過 extends 語句繼承了一個父類的所有功能,還可以添加自己的功能。
class Developer { constructor(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } getName() { return this.firstname + ' ' + this.lastname; } } class ReactDeveloper extends Developer { getJob() { return 'React Developer'; } } var me = new ReactDeveloper('Robin', 'Wieruch'); console.log(me.getName()); console.log(me.getJob());
基本上,要理解 React 的類組件,知道這些就夠了。JavaScript 類用于定義 React 組件,React 組件繼承了從 React 包導入的 React Component 類的所有功能。
import React, { Component } from 'react'; class App extends Component { render() { return ( <div> <h2>Welcome to React</h2> </div> ); } } export default App;
這就是為什么 render() 方法在 React 類組件中是必需的:從 React 包導入的 React Component 用它在瀏覽器中顯示某些內容。此外,如果不從 React Component 繼承,將無法使用其他生命周期方法(包括 render() 方法)。例如,如果不繼承,那么 componentDidMount() 生命周期方法就不存在,因為現在這個類只是一個普通 JavaScript 類的實例。除了生命周期方法不可用,React 的 API 方法(例如用于本地狀態管理的 this.setState())也不可用。
我們可以通過 JavaScript 類來擴展通用類的行為。因此,我們可以引入自己的類方法或屬性。
import React, { Component } from 'react'; class App extends Component { getGreeting() { return 'Welcome to React'; } render() { return ( <div> <h2>{this.getGreeting()}</h2> </div> ); } } export default App;
現在你應該知道為什么 React 使用 JavaScript 類來定義 React 類組件。當你需要訪問 React 的 API(生命周期方法、this.state 和 this.setState())時,可以使用它們。接下來,你將看到如何以不同的方式定義 React 組件,比如不使用 JavaScript 類,因為有時候你可能不需要使用類方法、生命周期方法或狀態。
盡管我們可以在 React 中使用 JavaScript 類繼承,但這對于 React 來說不是一個理想的結果,因為 React 更傾向于使用組合而不是繼承。因此,你的 React 組件需要擴展的唯一類應該是 React Component。
在給別人培訓 React 時,我在一開始就向他們解釋 JavaScript 的箭頭函數。箭頭函數是 ES6 新增的語言特性之一,讓 JavaScript 向函數式編程更近了一步。
// JavaScript ES5 function function getGreeting() { return 'Welcome to JavaScript'; } // JavaScript ES6 arrow function with body const getGreeting = () => { return 'Welcome to JavaScript'; } // JavaScript ES6 arrow function without body and implicit return const getGreeting = () => 'Welcome to JavaScript';
在 React 應用程序中使用 JavaScript 箭頭函數通常是為了讓代碼保持簡潔和可讀。我很喜歡箭頭函數,總是嘗試將我的函數從 JavaScript ES5 重構成 ES6。在某些時候,當 JavaScript ES5 函數和 JavaScript ES6 函數之間的差異很明顯時,我會使用 JavaScript ES6 的箭頭函數。不過,對 React 新手來說,太多不同的語法可能會讓人不知所措。因此,在 React 中使用它們之前,我會嘗試解釋 JavaScript 函數的不同特點。在以下部分,你將了解到如何在 React 中使用 JavaScript 箭頭函數。
React 使用了不同的編程范式,這要歸功于 JavaScript 是一門“多面手”編程語言。在面向對象編程方面,React 的類組件可以很好地利用 JavaScript 類(React 組件 API 的繼承、類方法和類屬性,如 this.state)。另一方面,React(及其生態系統)也使用了很多函數式編程的概念。例如,React 的函數無狀態組件是另一種定義 React 組件的方式。那么,如果可以像函數那樣使用組件,將會怎樣?
function (props) { return view; }
這是一個接收輸入(例如 props)并返回 HTML 元素(視圖)的函數。它不需要管理任何狀態(無狀態),也不需要了解任何方法(類方法、生命周期方法)。這個函數只需要使用 React 組件的 render() 方法來進行渲染。
function Greeting(props) { return <h2>{props.greeting}</h2>; }
功能無狀態組件是在 React 中定義組件的***方法。它們的樣板代碼較少,復雜性較低,并且比 React 類組件更易于維護。不過,這兩者都有自己存在的理由。
之前提到了 JavaScript 箭頭函數以及它們可以提升代碼的可讀性,現在讓我們將這些函數應用無狀態組件中。之前的 Greeting 組件在 JavaScript ES5 和 ES6 中的寫法有點不一樣:
// JavaScript ES5 function function Greeting(props) { return <h2>{props.greeting}</h2>; } // JavaScript ES6 arrow function const Greeting = (props) => { return <h2>{props.greeting}</h2>; } // JavaScript ES6 arrow function without body and implicit return const Greeting = (props) => <h2>{props.greeting}</h2>
JavaScript 箭頭函數是讓 React 無狀態組件保持簡潔的一個不錯的方法。
React 定義組件的方式一直在演化。在早期階段,React.createClass() 方法是創建 React 類組件的默認方式。現在不再使用這個方法,因為隨著 JavaScript ES6 的興起,之前的 React 類組件語法成為默認語法。
不過,JavaScript 也在不斷發展,因此 JavaScript 愛好者一直在尋找新的方式。這就是為什么你會發現 React 類組件使用了不同的語法。使用狀態和類方法來定義 React 類組件的一種方法如下:
class Counter extends Component { constructor(props) { super(props); this.state = { counter: 0, }; this.onIncrement = this.onIncrement.bind(this); this.onDecrement = this.onDecrement.bind(this); } onIncrement() { this.setState(state => ({ counter: state.counter + 1 })); } onDecrement() { this.setState(state => ({ counter: state.counter - 1 })); } render() { return ( <div> <p>{this.state.counter}</p> <button onClick={this.onIncrement} type="button">Increment</button> <button onClick={this.onDecrement} type="button">Decrement</button> </div> ); } }
不過,在實現大量的 React 類組件時,構造函數中的類方法綁定和構造函數本身就變成了繁瑣的實現細節。所運的是,有一個簡短的語法可用來擺脫這兩個煩惱:
class Counter extends Component { state = { counter: 0, }; onIncrement = () => { this.setState(state => ({ counter: state.counter + 1 })); } onDecrement = () => { this.setState(state => ({ counter: state.counter - 1 })); } render() { return ( <div> <p>{this.state.counter}</p> <button onClick={this.onIncrement} type="button">Increment</button> <button onClick={this.onDecrement} type="button">Decrement</button> </div> ); } }
通過使用 JavaScript 箭頭函數,可以自動綁定類方法,不需要在構造函數中綁定它們。通過將狀態直接定義為類屬性,在不使用 props 時就可以省略構造函數。(注意:請注意,JavaScript 還不支持類屬性。)因此,你可以說這種定義 React 類組件的方式比其他版本更簡潔。
模板字面量是 JavaScript ES6 附帶的另一種 JavaScript 語言特性。之所以提到這個特性,是因為當 JavaScript 和 React 新手看到它們時,可能會感到困惑。以下面的連接字符串的語法為例:
function getGreeting(what) { return 'Welcome to ' + what; } const greeting = getGreeting('JavaScript'); console.log(greeting); // Welcome to JavaScript
模板字面量可以用于達到相同的目的,被稱為字符串插值:
function getGreeting(what) { return `Welcome to ${what}`; }
你只需使用反引號和 ${}來插入 JavaScript 原語。字符串字面量不僅可用于字符串插值,還可用于多行字符串:
function getGreeting(what) { return ` Welcome to ${what} `; }
這樣就可以格式化多行文本塊。
在向 React 新手教授 JSX 語法時,我通常會先在 render() 方法中定義一個變量,然后將其用在返回代碼塊中。
import React, { Component } from 'react'; class App extends Component { render() { var greeting = 'Welcome to React'; return ( <div> <h2>{greeting}</h2> </div> ); } } export default App;
你只需使用花括號來操作 HTML 中的 JavaScript。不管是渲染字符串還是渲染一個復雜的對象,并沒有太大不同。
import React, { Component } from 'react'; class App extends Component { render() { var user = { name: 'Robin' }; return ( <div> <h2>{user.name}</h2> </div> ); } } export default App;
接下來的問題是:如何渲染項目列表?React 沒有提供特定的 API(例如 HTML 標記的自定義屬性)用于渲染項目列表。我們可以使用純 JavaScript 代碼來迭代項目列表,并返回每個項目的 HTML。
import React, { Component } from 'react'; class App extends Component { render() { var users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <ul> {users.map(function (user) { return <li>{user.name}</li>; })} </ul> ); } } export default App;
通過使用 JavaScript 箭頭函數,你可以擺脫箭頭函數體和 return 語句,讓渲染輸出更加簡潔。
import React, { Component } from 'react'; class App extends Component { render() { var users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <ul> {users.map(user => <li>{user.name}</li>)} </ul> ); } } export default App;
很快,每個 React 開發人員都習慣了 JavaScript 內置的 map() 方法。對數組進行 map 并返回每個項的渲染輸出,這樣做非常有用。在某系情況下,結合使用 filter() 或 reduce() 會更有用,而不只是為每個被 map 的項渲染輸出。
import React, { Component } from 'react'; class App extends Component { render() { var users = [ { name: 'Robin', isDeveloper: true }, { name: 'Markus', isDeveloper: false }, ]; return ( <ul> {users .filter(user => user.isDeveloper) .map(user => <li>{user.name}</li>) } </ul> ); } } export default App;
通常,React 開發人員習慣于使用 JavaScript 的這些內置函數,而不必使用 React 特定的 API。它只是 HTML 中的 JavaScript。
對于 React 的新手來說,使用 var、let 和 const 來聲明變量可能也會給他們造成混淆,雖然它們不是 React 相關的。我會嘗試在教學中盡早介紹 let 和 const,并從在 React 組件中交替使用 const 和 var 開始:
import React, { Component } from 'react'; class App extends Component { render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <ul> {users.map(user => <li>{user.name}</li>)} </ul> ); } } export default App;
然后我給出了一些使用這些變量聲明的經驗法則:
(1)不要使用 var,因為 let 和 const 更具體
(2)默認使用 const,因為它不能被重新分配或重新聲明
(3)如果要重新賦值變量則使用 let
let 通常用于 for 循環中,const 通常用于保持 JavaScript 變量不變。盡管在使用 const 時可以修改對象和數組的內部屬性,但變量聲明表達了保持變量不變的意圖。
如果要通過 if-else 語句進行條件渲染該怎么辦?我們不能直接在 JSX 中使用 if-else 語句,但可以從渲染函數中提前返回。如果不需要顯示內容,返回 null 在 React 中是合法的。
import React, { Component } from 'react'; class App extends Component { render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; const showUsers = false; if (!showUsers) { return null; } return ( <ul> {users.map(user => <li>{user.name}</li>)} </ul> ); } } export default App;
不過,如果要在返回的 JSX 中使用 if-else 語句,可以使用 JavaScript 的三元運算符:
import React, { Component } from 'react'; class App extends Component { render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; const showUsers = false; return ( <div> { showUsers ? ( <ul> {users.map(user => <li>{user.name}</li>)} </ul> ) : ( null ) } </div> ); } } export default App;
如果你只返回條件渲染的一個方面,可以使用 && 運算符:
import React, { Component } from 'react'; class App extends Component { render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; const showUsers = false; return ( <div> { showUsers && ( <ul> {users.map(user => <li>{user.name}</li>)} </ul> ) } </div> ); } } export default App;
詳細的原理我就不說了,但如果你感興趣,可以在這篇文章(https://www.robinwieruch.de/conditional-rendering-react/)里了解到更詳細的信息以及與條件渲染相關的其他技術。React 中的條件渲染告訴我們,大多數 React 都是與 JavaScript 有關,而不是 React 特定的內容。
在 JavaScript 中,我們可以通過 import 和 export 語句來導入和導出在 JavaScript ES6 文件中定義的功能。
在開始你的***個 React 應用程序之前,這些 import 和 export 語句是另一個需要了解的話題。create-react-app 項目已經在使用 import 語句:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2 className="App-title">Welcome to React</h2> </header> <p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p> </div> ); } } export default App;
這對初始項目來說非常棒,因為它為你提供了一個全面的體驗,可以導入和導出其他文件。不過,在剛開始接觸 React 時,我會試著避免這些導入。相反,我會專注于 JSX 和 React 組件。在需要將 React 組件或 JavaScript 函數分離到單獨的文件中時,才需要引入導入和導出語句。
那么這樣使用這些導入和導出語句呢?假設你想要導出一個文件的如下變量:
const firstname = 'Robin'; const lastname = 'Wieruch'; export { firstname, lastname };
然后,你可以通過***個文件的相對路徑將它們導入到另一個文件中:
import { firstname, lastname } from './file1.js'; console.log(firstname); // output: Robin
因此,它不一定只是與導入或導出組件或函數有關,它可以是共享可分配給變量的所有東西(我們只談 JS)。你還可以將另一個文件導出的所有變量作為一個對象導入:
import * as person from './file1.js'; console.log(person.firstname); // output: Robin
導入可以有別名。當從多個文件導入具有相同導出名稱的功能時,就需要用到別名。
import { firstname as username } from './file1.js'; console.log(username); // output: Robin
之前所有的例子都是命名的導入和導出。除此之外,還有默認的導入和導出。它可以用于以下一些場景:
導出和導入單個功能;
強調一個模塊導出 API 的主要功能;
作為導入功能的后備。
const robin = { firstname: 'Robin', lastname: 'Wieruch', }; export default robin;
在使用默認導入時可以省略大括號:
import developer from './file1.js'; console.log(developer); // output: { firstname: 'Robin', lastname: 'Wieruch' }
此外,導入名稱可以與導出的默認名稱不同。你還可以將它與命名的 export 和 import 語句一起使用:
const firstname = 'Robin'; const lastname = 'Wieruch'; const person = { firstname, lastname, }; export { firstname, lastname, }; export default person;
在另一個文件中導入:
import developer, { firstname, lastname } from './file1.js'; console.log(developer); // output: { firstname: 'Robin', lastname: 'Wieruch' } console.log(firstname, lastname); // output: Robin Wieruch
你還可以節省一些行,直接導出命名的變量:
export const firstname = 'Robin'; export const lastname = 'Wieruch';
這些是 ES6 模塊的主要功能。它們可以幫助你更好地組織代碼,并設計出可重用的模塊 API。
React 中的庫
React 只是應用程序的視圖層。React 提供了一些內部狀態管理,但除此之外,它只是一個為瀏覽器渲染 HTML 的組件庫。API(例如瀏覽器 API、DOM API)、JavaScript 或外部庫可以為 React 添加額外的東西。為 React 應用程序選擇合適的庫并不是件容易的事,但一旦你對不同的庫有了很好的了解,就可以選擇最適合你的技術棧的庫。
例如,我們可以使用 React 原生的獲取數據的 API 來獲取數據:
import React, { Component } from 'react'; class App extends Component { state = { data: null, }; componentDidMount() { fetch('https://api.mydomain.com') .then(response => response.json()) .then(data => this.setState({ data })); } render() { ... } } export default App;
但你也可以使用另一個庫來獲取數據,Axios 就是這樣的一個流行庫:
import React, { Component } from 'react'; import axios from 'axios'; class App extends Component { state = { data: null, }; componentDidMount() { axios.get('https://api.mydomain.com') .then(data => this.setState({ data })); } render() { ... } } export default App;
因此,一旦你知道了需要解決什么問題,React 的生態系統就可以為你提供大量的解決方案。這可能與 React 本身無關,而是有關了解如何選擇可用于彌補 React 應用程序的各種 JavaScript 庫。
高階函數是函數式編程中的一個非常棒的概念。在 React 中,了解這些函數是非常有意義的,因為在某些時候你需要處理高階組件,如果已經了解了高階函數,那么就可以更好地了解這些高階組件。
我們假設可以根據一個輸入字段的值對用戶列表進行過濾。
import React, { Component } from 'react'; class App extends Component { state = { query: '', }; onChange = event => { this.setState({ query: event.target.value }); } render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <div> <ul> {users .filter(user => this.state.query === user.name) .map(user => <li>{user.name}</li>) } </ul> <input type="text" onChange={this.onChange} /> </div> ); } } export default App;
我們并不總是希望通過提取函數的方式來實現,因為這樣會增加不必要的復雜性。但是,通過提取函數,我們可以對其進行單獨的測試。因此,讓我們使用內置的 filter 函數來實現這個例子。
import React, { Component } from 'react'; function doFilter(user) { return query === user.name; } class App extends Component { ... render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <div> <ul> {users .filter(doFilter) .map(user => <li>{user.name}</li>) } </ul> <input type="text" onChange={this.onChange} /> </div> ); } } export default App;
這個實現還起不到作用,因為 doFilter() 函數需要知道 state 的 query 屬性。我們可以通過另一個包裝函數來傳遞它,也就是高階函數。
import React, { Component } from 'react'; function doFilter(query) { return function (user) { return query === user.name; } } class App extends Component { ... render() { const users = [ { name: 'Robin' }, { name: 'Markus' }, ]; return ( <div> <ul> {users .filter(doFilter(this.state.query)) .map(user => <li>{user.name}</li>) } </ul> <input type="text" onChange={this.onChange} /> </div> ); } } export default App;
基本上,高階函數是可以返回函數的函數。通過使用 JavaScript ES6 的箭頭函數,你可以讓高階函數變得更簡潔。此外,這種簡化的方式讓將函數組合成函數變得更吸引人。
const doFilter = query => user => query === user.name;
現在可以將 doFilter() 函數從文件中導出,并將其作為純(高階)函數進行單獨的測試。在了解了高階函數之后,就為學習 React 的高階組件奠定了基礎。
將這些函數提取到 React 組件之外的(高階)函數中也助于單獨測試 React 的本地狀態管理。
export const doIncrement = state => ({ counter: state.counter + 1 }); export const doDecrement = state => ({ counter: state.counter - 1 }); class Counter extends Component { state = { counter: 0, }; onIncrement = () => { this.setState(doIncrement); } onDecrement = () => { this.setState(doDecrement); } render() { return ( <div> <p>{this.state.counter}</p> <button onClick={this.onIncrement} type="button">Increment</button> <button onClick={this.onDecrement} type="button">Decrement</button> </div> ); } }
函數式編程非常強大,轉向函數式編程有助于了解 JavaScript 將函數作為一等公民所帶來的好處。
JavaScript 中引入的另一種語言特性稱為解構。通常情況下,你需要在組件的 state 或 props 中訪問大量的屬性。你可以在 JavaScript 中使用解構賦值,而不是逐個將它們分配給變量。
// no destructuring const users = this.state.users; const counter = this.state.counter; // destructuring const { users, counter } = this.state;
這對函數式無狀態組件來說特別有用,因為它們可以在函數簽名中收到 props 對象。通常,你用到的不是 props,而是 props 里的內容,因此你可以對函數簽名中已有的內容進行解構。
// no destructuring function Greeting(props) { return <h2>{props.greeting}</h2>; } // destructuring function Greeting({ greeting }) { return <h2>{greeting}</h2>; }
解構也適用于 JavaScript 數組。另一個很棒的特性是剩余解構。它通常用于拆分對象的一部分屬性,并將剩余屬性保留在另一個對象中。
// rest destructuring const { users, ...rest } = this.state;
uesrs 可以在 React 組件中渲染,而剩余狀態可以用在其他地方。這就是 JavaScript 展開(spread)運算發揮作用的地方,它可以將對象的剩余部分轉到下一個組件。
React 只提供了一個細小的 API 表面區域,因此開發人員必須習慣于 JavaScript 提供的所有功能。這句話并非沒有任何理由:“成為 React 開發者也會讓你成為更好的 JavaScript 開發者”。讓我們通過重構一個高階組件來回顧一下學到的 JavaScript 的一些方面。
function withLoading(Component) { return class WithLoading extends { render() { const { isLoading, ...props } = this.props; if (isLoading) { return <p>Loading</p>; } return <Component { ...props } />; } } }; }
這個高階組件用于顯示條件加載進度條,當 isLoading 被設為 true 時,就可以顯示加載進度條,否則就渲染輸入組件。在這里可以看到(剩余)解構和展開運算符的實際應用。后者可以在渲染的 Component 中看到,因為 props 對象的剩余屬性被傳給了那個 Component。
讓高階組件變得更簡潔的***步是將返回的 React 類組件重構為函數式無狀態組件:
function withLoading(Component) { return function ({ isLoading, ...props }) { if (isLoading) { return <p>Loading</p>; } return <Component { ...props } />; }; }
可以看到,剩余解構也可以被用在函數的簽名中。接下來,使用 JavaScript ES6 箭頭函數讓高階組件變得更加簡潔:
const withLoading = Component => ({ isLoading, ...props }) => { if (isLoading) { return <p>Loading</p>; } return <Component { ...props } />; }
通過使用三元運算符可以將函數體縮短為一行代碼。因此可以省略函數體和 return 語句。
const withLoading = Component => ({ isLoading, ...props }) => isLoading ? <p>Loading</p> : <Component { ...props } />
如你所見,高階組件使用的是各種 JavaScript 而不是 React 相關技術:箭頭函數、高階函數、三元運算符、解構和展開運算符。
“怎么學好JavaScript”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。