摘要:是流行的框架之一,在年及以后將會(huì)更加流行。于年首次發(fā)布,多年來廣受歡迎。下面是另一個(gè)名為的高階函數(shù)示例,該函數(shù)接受另外兩個(gè)函數(shù),分別是和。將所有較小的函數(shù)組合成更大的函數(shù),最終,得到一個(gè)應(yīng)用程序,這稱為組合。
React是流行的javascript框架之一,在2019年及以后將會(huì)更加流行。React于2013年首次發(fā)布,多年來廣受歡迎。它是一個(gè)聲明性的、基于組件的、用于構(gòu)建用戶界面的高效javascript庫(kù)。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!
以下是面試前必須了解的話題。
什么是聲明式編程
聲明式編程 vs 命令式編程
什么是函數(shù)式編程
什么是組件設(shè)計(jì)模式
React 是什么
React 和 Angular 有什么不同
什么是虛擬DOM及其工作原理
什么是JSX
組件和不同類型
Props 和 State
什么是 PropTypes
如何更新狀態(tài)和不更新狀態(tài)
組件生命周期方法
超越繼承的組合
如何在React中應(yīng)用樣式
什么是Redux及其工作原理
什么是React路由器及其工作原理
什么是錯(cuò)誤邊界
什么是 Fragments
什么是傳送門(Portals)
什么是 Context
什么是 Hooks
如何提高性能
如何在重新加載頁(yè)面時(shí)保留數(shù)據(jù)
如何從React中調(diào)用API
總結(jié)
什么是聲明式編程聲明式編程是一種編程范式,它關(guān)注的是你要做什么,而不是如何做。它表達(dá)邏輯而不顯式地定義步驟。這意味著我們需要根據(jù)邏輯的計(jì)算來聲明要顯示的組件。它沒有描述控制流步驟。聲明式編程的例子有HTML、SQL等
HTML file
// HTMLDeclarative Programming
SQL file
select * from studens where firstName = "declarative";聲明式編程 vs 命令式編程
聲明式編程的編寫方式描述了應(yīng)該做什么,而命令式編程描述了如何做。在聲明式編程中,讓編譯器決定如何做事情。聲明性程序很容易推理,因?yàn)榇a本身描述了它在做什么。
下面是一個(gè)例子,數(shù)組中的每個(gè)元素都乘以 2,我們使用聲明式map函數(shù),讓編譯器來完成其余的工作,而使用命令式,需要編寫所有的流程步驟。
const numbers = [1,2,3,4,5]; // 聲明式 const doubleWithDec = numbers.map(number => number * 2); console.log(doubleWithDec) // 命令式 const doubleWithImp = []; for(let i=0; i什么是函數(shù)式編程 函數(shù)式編程是聲明式編程的一部分。javascript中的函數(shù)是第一類公民,這意味著函數(shù)是數(shù)據(jù),你可以像保存變量一樣在應(yīng)用程序中保存、檢索和傳遞這些函數(shù)。
函數(shù)式編程有些核心的概念,如下:
不可變性(Immutability)
純函數(shù)(Pure Functions)
數(shù)據(jù)轉(zhuǎn)換(Data Transformations)
高階函數(shù) (Higher-Order Functions)
遞歸
組合
不可變性(Immutability)不可變性意味著不可改變。 在函數(shù)式編程中,你無法更改數(shù)據(jù),也不能更改。 如果要改變或更改數(shù)據(jù),則必須復(fù)制數(shù)據(jù)副本來更改。
例如,這是一個(gè)student對(duì)象和changeName函數(shù),如果要更改學(xué)生的名稱,則需要先復(fù)制 student 對(duì)象,然后返回新對(duì)象。
在javascript中,函數(shù)參數(shù)是對(duì)實(shí)際數(shù)據(jù)的引用,你不應(yīng)該使用 student.firstName =“testing11”,這會(huì)改變實(shí)際的student 對(duì)象,應(yīng)該使用Object.assign復(fù)制對(duì)象并返回新對(duì)象。
let student = { firstName: "testing", lastName: "testing", marks: 500 } function changeName(student) { // student.firstName = "testing11" //should not do it let copiedStudent = Object.assign({}, student); copiedStudent.firstName = "testing11"; return copiedStudent; } console.log(changeName(student)); console.log(student);純函數(shù)純函數(shù)是始終接受一個(gè)或多個(gè)參數(shù)并計(jì)算參數(shù)并返回?cái)?shù)據(jù)或函數(shù)的函數(shù)。 它沒有副作用,例如設(shè)置全局狀態(tài),更改應(yīng)用程序狀態(tài),它總是將參數(shù)視為不可變數(shù)據(jù)。
我想使用 appendAddress 的函數(shù)向student對(duì)象添加一個(gè)地址。 如果使用非純函數(shù),它沒有參數(shù),直接更改 student 對(duì)象來更改全局狀態(tài)。
使用純函數(shù),它接受參數(shù),基于參數(shù)計(jì)算,返回一個(gè)新對(duì)象而不修改參數(shù)。
let student = { firstName: "testing", lastName: "testing", marks: 500 } // 非純函數(shù) function appendAddress() { student.address = {streetNumber:"0000", streetName: "first", city:"somecity"}; } console.log(appendAddress()); // 純函數(shù) function appendAddress(student) { let copystudent = Object.assign({}, student); copystudent.address = {streetNumber:"0000", streetName: "first", city:"somecity"}; return copystudent; } console.log(appendAddress(student)); console.log(student);數(shù)據(jù)轉(zhuǎn)換我們講了很多關(guān)于不可變性的內(nèi)容,如果數(shù)據(jù)是不可變的,我們?nèi)绾胃淖償?shù)據(jù)。如上所述,我們總是生成原始數(shù)據(jù)的轉(zhuǎn)換副本,而不是直接更改原始數(shù)據(jù)。
再介紹一些 javascript內(nèi)置函數(shù),當(dāng)然還有很多其他的函數(shù),這里有一些例子。所有這些函數(shù)都不改變現(xiàn)有的數(shù)據(jù),而是返回新的數(shù)組或?qū)ο蟆?/p>
let cities = ["irving", "lowell", "houston"]; // we can get the comma separated list console.log(cities.join(",")) // irving,lowell,houston // if we want to get cities start with i const citiesI = cities.filter(city => city[0] === "i"); console.log(citiesI) // [ "irving" ] // if we want to capitalize all the cities const citiesC = cities.map(city => city.toUpperCase()); console.log(citiesC) // [ "IRVING", "LOWELL", "HOUSTON" ]高階函數(shù)高階函數(shù)是將函數(shù)作為參數(shù)或返回函數(shù)的函數(shù),或者有時(shí)它們都有。 這些高階函數(shù)可以操縱其他函數(shù)。
Array.map,Array.filter和Array.reduce是高階函數(shù),因?yàn)樗鼈儗⒑瘮?shù)作為參數(shù)。
const numbers = [10,20,40,50,60,70,80] const out1 = numbers.map(num => num * 100); console.log(out1); // [ 1000, 2000, 4000, 5000, 6000, 7000, 8000 ] const out2 = numbers.filter(num => num > 50); console.log(out2); // [ 60, 70, 80 ] const out3 = numbers.reduce((out,num) => out + num); console.log(out3); // 330下面是另一個(gè)名為isPersonOld的高階函數(shù)示例,該函數(shù)接受另外兩個(gè)函數(shù),分別是 message和isYoung 。
const isYoung = age => age < 25; const message = msg => "He is "+ msg; function isPersonOld(age, isYoung, message) { const returnMessage = isYoung(age)?message("young"):message("old"); return returnMessage; } // passing functions as an arguments console.log(isPersonOld(13,isYoung,message)) // He is young遞歸遞歸是一種函數(shù)在滿足一定條件之前調(diào)用自身的技術(shù)。只要可能,最好使用遞歸而不是循環(huán)。你必須注意這一點(diǎn),瀏覽器不能處理太多遞歸和拋出錯(cuò)誤。
下面是一個(gè)演示遞歸的例子,在這個(gè)遞歸中,打印一個(gè)類似于樓梯的名稱。我們也可以使用for循環(huán),但只要可能,我們更喜歡遞歸。
function printMyName(name, count) { if(count <= name.length) { console.log(name.substring(0,count)); printMyName(name, ++count); } } console.log(printMyName("Bhargav", 1)); /* B Bh Bha Bhar Bharg Bharga Bhargav */ // withotu recursion var name = "Bhargav" var output = ""; for(let i=0; i組合 在React中,我們將功能劃分為小型可重用的純函數(shù),我們必須將所有這些可重用的函數(shù)放在一起,最終使其成為產(chǎn)品。 將所有較小的函數(shù)組合成更大的函數(shù),最終,得到一個(gè)應(yīng)用程序,這稱為組合。
實(shí)現(xiàn)組合有許多不同方法。 我們從Javascript中了解到的一種常見方法是鏈接。 鏈接是一種使用點(diǎn)表示法調(diào)用前一個(gè)函數(shù)的返回值的函數(shù)的方法。
這是一個(gè)例子。 我們有一個(gè)name,如果firstName和lastName大于5個(gè)單詞的大寫字母,剛返回,并且打印名稱的名稱和長(zhǎng)度。
const name = "Bhargav Bachina"; const output = name.split(" ") .filter(name => name.length > 5) .map(val => { val = val.toUpperCase(); console.log("Name:::::"+val); console.log("Count::::"+val.length); return val; }); console.log(output) /* Name:::::BHARGAV Count::::7 Name:::::BACHINA Count::::7 [ "BHARGAV", "BACHINA" ] */在React中,我們使用了不同于鏈接的方法,因?yàn)槿绻?0個(gè)這樣的函數(shù),就很難進(jìn)行鏈接。這里的目的是將所有更簡(jiǎn)單的函數(shù)組合起來生成一個(gè)更高階的函數(shù)。
const name = compose( splitmyName, countEachName, comvertUpperCase, returnName ) console.log(name);什么是 ReactReact是一個(gè)簡(jiǎn)單的javascript UI庫(kù),用于構(gòu)建高效、快速的用戶界面。它是一個(gè)輕量級(jí)庫(kù),因此很受歡迎。它遵循組件設(shè)計(jì)模式、聲明式編程范式和函數(shù)式編程概念,以使前端應(yīng)用程序更高效。它使用虛擬DOM來有效地操作DOM。它遵循從高階組件到低階組件的單向數(shù)據(jù)流。
React 與 Angular 有何不同?Angular是一個(gè)成熟的MVC框架,帶有很多特定的特性,比如服務(wù)、指令、模板、模塊、解析器等等。React是一個(gè)非常輕量級(jí)的庫(kù),它只關(guān)注MVC的視圖部分。
Angular遵循兩個(gè)方向的數(shù)據(jù)流,而React遵循從上到下的單向數(shù)據(jù)流。React在開發(fā)特性時(shí)給了開發(fā)人員很大的自由,例如,調(diào)用API的方式、路由等等。我們不需要包括路由器庫(kù),除非我們需要它在我們的項(xiàng)目。
什么是Virtual DOM及其工作原理React 使用 Virtual DOM 來更新真正的 DOM,從而提高效率和速度。 我們來詳細(xì)討論這些。
什么是Virtual DOM瀏覽器遵循HTML指令來構(gòu)造文檔對(duì)象模型(DOM)。當(dāng)瀏覽器加載HTML并呈現(xiàn)用戶界面時(shí),HTML文檔中的所有元素都變成DOM元素。
DOM是從根元素開始的元素層次結(jié)構(gòu)。例如,看看下面的HTML。
This is heading
this is paragraph
This is just a paragraon
This is heading
this is paragraph
This is just a paragraon
This is heading
this is paragraph
This is just a paragraon
當(dāng)在瀏覽器中加載這個(gè)HTML時(shí),所有這些HTML元素都被轉(zhuǎn)換成DOM元素,如下所示
當(dāng)涉及到SPA應(yīng)用程序時(shí),首次加載index.html,并在index.html本身中加載更新后的數(shù)據(jù)或另一個(gè)html。當(dāng)用戶瀏覽站點(diǎn)時(shí),我們使用新內(nèi)容更新相同的index.html。每當(dāng)DOM發(fā)生更改時(shí),瀏覽器都需要重新計(jì)算CSS、進(jìn)行布局并重新繪制web頁(yè)面。
React 使用 Virtual DOM 有效地重建 DOM。 對(duì)于我們來說,這使得DOM操作的一項(xiàng)非常復(fù)雜和耗時(shí)的任務(wù)變得更加容易。 React從開發(fā)人員那里抽象出所有這些,以便在Virtual DOM的幫助下構(gòu)建高效的UI。
虛擬DOM是如何工作的虛擬DOM只不過是真實(shí) DOM 的 javascript對(duì)象表示。 與更新真實(shí) DOM 相比,更新 javascript 對(duì)象更容易,更快捷。 考慮到這一點(diǎn),讓我們看看它是如何工作的。
React將整個(gè)DOM副本保存為虛擬DOM
每當(dāng)有更新時(shí),它都會(huì)維護(hù)兩個(gè)虛擬DOM,以比較之前的狀態(tài)和當(dāng)前狀態(tài),并確定哪些對(duì)象已被更改。 例如,段落文本更改為更改。
現(xiàn)在,它通過比較兩個(gè)虛擬DOM 差異,并將這些變化更新到實(shí)際DOM
一旦真正的DOM更新,它也會(huì)更新UI
什么是 JSXJSX是javascript的語法擴(kuò)展。它就像一個(gè)擁有javascript全部功能的模板語言。它生成React元素,這些元素將在DOM中呈現(xiàn)。React建議在組件使用JSX。在JSX中,我們結(jié)合了javascript和HTML,并生成了可以在DOM中呈現(xiàn)的react元素。
下面是JSX的一個(gè)例子。我們可以看到如何將javascript和HTML結(jié)合起來。如果HTML中包含任何動(dòng)態(tài)變量,我們應(yīng)該使用表達(dá)式{}。
import React from "react"; export const Header = () => { const heading = "TODO App" return(組件和不同類型) }{heading}
React 中一切都是組件。 我們通常將應(yīng)用程序的整個(gè)邏輯分解為小的單個(gè)部分。 我們將每個(gè)多帶帶的部分稱為組件。 通常,組件是一個(gè)javascript函數(shù),它接受輸入,處理它并返回在UI中呈現(xiàn)的React元素。
在React中有不同類型的組件。讓我們?cè)敿?xì)看看。
函數(shù)/無狀態(tài)/展示組件函數(shù)或無狀態(tài)組件是一個(gè)純函數(shù),它可接受接受參數(shù),并返回react元素。這些都是沒有任何副作用的純函數(shù)。這些組件沒有狀態(tài)或生命周期方法,這里有一個(gè)例子。
import React from "react"; import Jumbotron from "react-bootstrap/Jumbotron"; export const Header = () => { return(類/有狀態(tài)組件) } TODO App
類或有狀態(tài)組件具有狀態(tài)和生命周期方可能通過 setState()方法更改組件的狀態(tài)。類組件是通過擴(kuò)展React創(chuàng)建的。它在構(gòu)造函數(shù)中初始化,也可能有子組件,這里有一個(gè)例子。
import React from "react"; import "../App.css"; import { ToDoForm } from "./todoform"; import { ToDolist } from "./todolist"; export class Dashboard extends React.Component { constructor(props){ super(props); this.state = { } } render() { return (受控組件); } }受控組件是在 React 中處理輸入表單的一種技術(shù)。表單元素通常維護(hù)它們自己的狀態(tài),而react則在組件的狀態(tài)屬性中維護(hù)狀態(tài)。我們可以將兩者結(jié)合起來控制輸入表單。這稱為受控組件。因此,在受控組件表單中,數(shù)據(jù)由React組件處理。
這里有一個(gè)例子。當(dāng)用戶在 todo 項(xiàng)中輸入名稱時(shí),調(diào)用一個(gè)javascript函數(shù)handleChange捕捉每個(gè)輸入的數(shù)據(jù)并將其放入狀態(tài),這樣就在 handleSubmit中的使用數(shù)據(jù)。
import React from "react"; import Form from "react-bootstrap/Form"; import Button from "react-bootstrap/Button"; import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; export class ToDoForm extends React.Component { constructor(props) { super(props); this.state = {value: ""}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert("A name was submitted: " + this.state.value); event.preventDefault(); } render() { return (非受控組件); } }Item 大多數(shù)情況下,建議使用受控組件。有一種稱為非受控組件的方法可以通過使用Ref來處理表單數(shù)據(jù)。在非受控組件中,Ref用于直接從DOM訪問表單值,而不是事件處理程序。
我們使用Ref構(gòu)建了相同的表單,而不是使用React狀態(tài)。 我們使用React.createRef() 定義Ref并傳遞該輸入表單并直接從handleSubmit方法中的DOM訪問表單值。
import React from "react"; import Form from "react-bootstrap/Form"; import Button from "react-bootstrap/Button"; import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; export class ToDoForm extends React.Component { constructor(props) { super(props); this.state = {value: ""}; this.input = React.createRef(); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { alert("A name was submitted: " + this.input.current.value); event.preventDefault(); } render() { return (容器組件); } }Item 容器組件是處理獲取數(shù)據(jù)、訂閱 redux 存儲(chǔ)等的組件。它們包含展示組件和其他容器組件,但是里面從來沒有html。
高階組件高階組件是將組件作為參數(shù)并生成另一個(gè)組件的組件。 Redux connect是高階組件的示例。 這是一種用于生成可重用組件的強(qiáng)大技術(shù)。
Props 和 StateProps 是只讀屬性,傳遞給組件以呈現(xiàn)UI和狀態(tài),我們可以隨時(shí)間更改組件的輸出。
下面是一個(gè)類組件的示例,它在構(gòu)造函數(shù)中定義了props和state,每當(dāng)使用this.setState() 修改狀態(tài)時(shí),將再次調(diào)用 render( ) 函數(shù)來更改UI中組件的輸出。
import React from "react"; import "../App.css"; export class Dashboard extends React.Component { constructor(props){ super(props); this.state = { name: "some name" } } render() { // reading state const name = this.state.name; //reading props const address = this.props.address; return (什么是PropTypes{name} {address}); } }隨著時(shí)間的推移,應(yīng)用程序會(huì)變得越來越大,因此類型檢查非常重要。PropTypes為組件提供類型檢查,并為其他開發(fā)人員提供很好的文檔。如果react項(xiàng)目不使用 Typescript,建議為組件添加 PropTypes。
如果組件沒有收到任何 props,我們還可以為每個(gè)組件定義要顯示的默認(rèn) props。這里有一個(gè)例子。UserDisplay有三個(gè) prop:name、address和age,我們正在為它們定義默認(rèn)的props 和 prop類型。
import React from "react"; import PropTypes from "prop-types"; export const UserDisplay = ({name, address, age}) => { UserDisplay.defaultProps = { name: "myname", age: 100, address: "0000 onestreet" }; return ( <>如何更新狀態(tài)以及如何不更新Name:{name}Address:{address}> ) } UserDisplay.propTypes = { name: PropTypes.string.isRequired, address: PropTypes.objectOf(PropTypes.string), age: PropTypes.number.isRequired }Age:{age}你不應(yīng)該直接修改狀態(tài)。可以在構(gòu)造函數(shù)中定義狀態(tài)值。直接使用狀態(tài)不會(huì)觸發(fā)重新渲染。React 使用this.setState()時(shí)合并狀態(tài)。
// 錯(cuò)誤方式 this.state.name = "some name" // 正確方式 this.setState({name:"some name"})使用this.setState()的第二種形式總是更安全的,因?yàn)楦碌膒rops和狀態(tài)是異步的。這里,我們根據(jù)這些 props 更新狀態(tài)。
// 錯(cuò)誤方式 this.setState({ timesVisited: this.state.timesVisited + this.props.count }) // 正確方式 this.setState((state, props) => { timesVisited: state.timesVisited + props.count });組件生命周期方法組件在進(jìn)入和離開DOM時(shí)要經(jīng)歷一系列生命周期方法,下面是這些生命周期方法。
componentWillMount()在渲染前調(diào)用,在客戶端也在服務(wù)端,它只發(fā)生一次。
componentDidMount()在第一次渲染后調(diào)用,只在客戶端。之后組件已經(jīng)生成了對(duì)應(yīng)的DOM結(jié)構(gòu),可以通過this.getDOMNode()來進(jìn)行訪問。 如果你想和其他JavaScript框架一起使用,可以在這個(gè)方法中調(diào)用setTimeout, setInterval或者發(fā)送AJAX請(qǐng)求等操作(防止異部操作阻塞UI)。
componentWillReceiveProps()在組件接收到一個(gè)新的 prop (更新后)時(shí)被調(diào)用。這個(gè)方法在初始化render時(shí)不會(huì)被調(diào)用。
shouldComponentUpdate()返回一個(gè)布爾值。在組件接收到新的props或者state時(shí)被調(diào)用。在初始化時(shí)或者使用forceUpdate時(shí)不被調(diào)用。 可以在你確認(rèn)不需要更新組件時(shí)使用。
componentWillUpdate()在組件接收到新的props或者state但還沒有render時(shí)被調(diào)用。在初始化時(shí)不會(huì)被調(diào)用。
componentDidUpdate()在組件完成更新后立即調(diào)用。在初始化時(shí)不會(huì)被調(diào)用。
componentWillUnMount()件從 DOM 中移除的時(shí)候立刻被調(diào)用。
getDerivedStateFromError()這個(gè)生命周期方法在ErrorBoundary類中使用。實(shí)際上,如果使用這個(gè)生命周期方法,任何類都會(huì)變成ErrorBoundary。這用于在組件樹中出現(xiàn)錯(cuò)誤時(shí)呈現(xiàn)回退UI,而不是在屏幕上顯示一些奇怪的錯(cuò)誤。
componentDidCatch()這個(gè)生命周期方法在ErrorBoundary類中使用。實(shí)際上,如果使用這個(gè)生命周期方法,任何類都會(huì)變成ErrorBoundary。這用于在組件樹中出現(xiàn)錯(cuò)誤時(shí)記錄錯(cuò)誤。
超越繼承的組合在React中,我們總是使用組合而不是繼承。我們已經(jīng)在函數(shù)式編程部分討論了什么是組合。這是一種結(jié)合簡(jiǎn)單的可重用函數(shù)來生成高階組件的技術(shù)。下面是一個(gè)組合的例子,我們?cè)?dashboard 組件中使用兩個(gè)小組件todoForm和todoList。
import React from "react"; import "../App.css"; import { ToDoForm } from "./todoform"; import { ToDolist } from "./todolist"; export class Dashboard extends React.Component { render() { return (如何在React中應(yīng)用樣式); } }將樣式應(yīng)用于React組件有三種方法。
外部樣式表在此方法中,你可以將外部樣式表導(dǎo)入到組件使用類中。 但是你應(yīng)該使用className而不是class來為React元素應(yīng)用樣式, 這里有一個(gè)例子。
import React from "react"; import "./App.css"; import { Header } from "./header/header"; import { Footer } from "./footer/footer"; import { Dashboard } from "./dashboard/dashboard"; import { UserDisplay } from "./userdisplay"; function App() { return (內(nèi)聯(lián)樣式); } export default App;在這個(gè)方法中,我們可以直接將 props 傳遞給HTML元素,屬性為style。這里有一個(gè)例子。這里需要注意的重要一點(diǎn)是,我們將javascript對(duì)象傳遞給style,這就是為什么我們使用 backgroundColor 而不是CSS方法backbackground -color。
import React from "react"; export const Header = () => { const heading = "TODO App" return(定義樣式對(duì)象并使用它) }{heading}
因?yàn)槲覀儗avascript對(duì)象傳遞給style屬性,所以我們可以在組件中定義一個(gè)style對(duì)象并使用它。下面是一個(gè)示例,你也可以將此對(duì)象作為 props 傳遞到組件樹中。
import React from "react"; const footerStyle = { width: "100%", backgroundColor: "green", padding: "50px", font: "30px", color: "white", fontWeight: "bold" } export const Footer = () => { return(什么是Redux及其工作原理All Rights Reserved 2019) }Redux 是 React的一個(gè)狀態(tài)管理庫(kù),它基于flux。 Redux簡(jiǎn)化了React中的單向數(shù)據(jù)流。 Redux將狀態(tài)管理完全從React中抽象出來。
它是如何工作的在React中,組件連接到 redux ,如果要訪問 redux,需要派出一個(gè)包含 id和負(fù)載(payload) 的 action。action 中的 payload 是可選的,action 將其轉(zhuǎn)發(fā)給 Reducer。
當(dāng)reducer收到action時(shí),通過 swithc...case 語法比較 action 中type。 匹配時(shí),更新對(duì)應(yīng)的內(nèi)容返回新的 state。
當(dāng)Redux狀態(tài)更改時(shí),連接到Redux的組件將接收新的狀態(tài)作為props。當(dāng)組件接收到這些props時(shí),它將進(jìn)入更新階段并重新渲染 UI。
Redux 循環(huán)細(xì)節(jié)讓我們?cè)敿?xì)看看整個(gè)redux 循環(huán)細(xì)節(jié)。
Action: Action 只是一個(gè)簡(jiǎn)單的json對(duì)象,type 和有payload作為鍵。type 是必須要有的,payload是可選的。下面是一個(gè) action 的例子。
// action { type:"SEND_EMAIL", payload: data };Action Creators:這些是創(chuàng)建Actions的函數(shù),因此我們?cè)谂砂l(fā)action時(shí)不必在組件中手動(dòng)編寫每個(gè) action。 以下是 action creator 的示例。
// action creator export function sendEamil(data) { return { type:"SEND_EMAIL", payload: data}; }Reducers:Reducers 是純函數(shù),它將 action和當(dāng)前 state 作為參數(shù),計(jì)算必要的邏輯并返回一個(gè)新r的state。 這些 Reducers 沒有任何副作用。 它不會(huì)改變 state 而是總是返回 state 。
export default function emailReducer(state = [], action){ switch(action.type) { case "SEND_EMAIL": return Object.assign({}, state, { email: action.payload }); default: return state; } }組件如何與 redux 進(jìn)行連接mapStateToProps:此函數(shù)將state映射到 props 上,因此只要state發(fā)生變化,新 state 會(huì)重新映射到 props。 這是訂閱store的方式。
mapDispatchToProps:此函數(shù)用于將 action creators 綁定到你的props 。以便我們可以在第12行中使用This . props.actions.sendemail()來派發(fā)一個(gè)動(dòng)作。
connect和bindActionCreators來自 redux。 前者用于連接 store ,如第22行,后者用于將 action creators 綁定到你的 props ,如第20行。
// import connect import { connect } from "react-redux" import { bindActionCreators } from "redux" // import action creators import * as userActions from "../../../actions/userActions"; export class User extends React.Component { handleSubmit() { // dispatch an action this.props.actions.sendEmail(this.state.email); } } // you are mapping you state props const mapStateToProps = (state, ownProps) => ({user: state.user}) // you are binding your action creators to your props const mapDispatchToProps = (dispatch) => ({actions: bindActionCreators(userActions, dispatch)}) export default connect(mapStateToProps, mapDispatchToProps)(User);什么是 React Router Dom 及其工作原理react-router-dom是應(yīng)用程序中路由的庫(kù)。 React庫(kù)中沒有路由功能,需要多帶帶安裝react-router-dom。
react-router-dom 提供兩個(gè)路由器BrowserRouter和HashRoauter。前者基于rul的pathname段,后者基于hash段。
前者:http://127.0.0.1:3000/article/num1 后者:http://127.0.0.1:3000/#/article/num1(不一定是這樣,但#是少不了的)react-router-dom 組件BrowserRouter 和 HashRouter 是路由器。
Route 用于路由匹配。
Link 組件用于在應(yīng)用程序中創(chuàng)建鏈接。 它將在HTML中渲染為錨標(biāo)記。
NavLink是突出顯示當(dāng)前活動(dòng)鏈接的特殊鏈接。
Switch 不是必需的,但在組合路由時(shí)很有用。
Redirect 用于強(qiáng)制路由重定向
下面是組件中的Link、NavLink和Redirect 的例子
// normal link Home // link which highlights currentlu active route with the given class nameReact // you can redirect to this url以下是 react router 組件的示例。 如果你查看下面的示例,我們將匹配路徑并使用Switch和Route呈現(xiàn)相應(yīng)的組件。
import React from "react" // import react router DOM elements import { Switch, Route, Redirect } from "react-router-dom" import ComponentA from "../common/compa" import ComponentB from "../common/compb" import ComponentC from "../common/compc" import ComponentD from "../common/compd" import ComponentE from "../common/compe" const Layout = ({ match }) => { return(什么是錯(cuò)誤邊界)} export default Layout在 React 中,我們通常有一個(gè)組件樹。如果任何一個(gè)組件發(fā)生錯(cuò)誤,它將破壞整個(gè)組件樹。沒有辦法捕捉這些錯(cuò)誤,我們可以用錯(cuò)誤邊界優(yōu)雅地處理這些錯(cuò)誤。
錯(cuò)誤邊界有兩個(gè)作用
如果發(fā)生錯(cuò)誤,顯示回退UI
記錄錯(cuò)誤
下面是ErrorBoundary類的一個(gè)例子。如果類實(shí)現(xiàn)了 getDerivedStateFromError或componentDidCatch 這兩個(gè)生命周期方法的任何一下,,那么這個(gè)類就會(huì)成為ErrorBoundary。前者返回{hasError: true}來呈現(xiàn)回退UI,后者用于記錄錯(cuò)誤。
import React from "react" export class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, info) { // You can also log the error to an error reporting service console.log("Error::::", error); } render() { if (this.state.hasError) { // You can render any custom fallback UI returnOOPS!. WE ARE LOOKING INTO IT.
; } return this.props.children; } }以下是我們?nèi)绾卧谄渲幸粋€(gè)組件中使用ErrorBoundary。使用ErrorBoundary類包裹 ToDoForm和ToDoList。 如果這些組件中發(fā)生任何錯(cuò)誤,我們會(huì)記錄錯(cuò)誤并顯示回退UI。
import React from "react"; import "../App.css"; import { ToDoForm } from "./todoform"; import { ToDolist } from "./todolist"; import { ErrorBoundary } from "../errorboundary"; export class Dashboard extends React.Component { render() { return (什么是 Fragments); } }在React中,我們需要有一個(gè)父元素,同時(shí)從組件返回React元素。有時(shí)在DOM中添加額外的節(jié)點(diǎn)會(huì)很煩人。使用 Fragments,我們不需要在DOM中添加額外的節(jié)點(diǎn)。我們只需要用 React.Fragment 或才簡(jiǎn)寫 <> 來包裹內(nèi)容就行了。如下 所示:
// Without Fragments return (什么是傳送門(Portals)) // With Fragments return () // shorthand notation Fragments return ( <> > ) 默認(rèn)情況下,所有子組件都在UI上呈現(xiàn),具體取決于組件層次結(jié)構(gòu)。Portal 提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的 DOM 節(jié)點(diǎn)的優(yōu)秀的方案。
這里有一個(gè)例子。默認(rèn)情況下,父組件在DOM層次結(jié)構(gòu)中有子組件。
我們可以將 children 組件移出parent 組件并將其附加 id 為 someid 的 Dom 節(jié)點(diǎn)下。
首先,獲取 id 為 someid,我們?cè)赾onstrcutorand中創(chuàng)建一個(gè)元素div,將child附加到componentDidMount中的someRoot。 最后,我們?cè)赗eactDOM.createPortal(this.props.childen),domnode的幫助下將子節(jié)點(diǎn)傳遞給該特定DOM節(jié)點(diǎn)。
首先,先獲取 id 為someid DOM元素,接著在構(gòu)造函數(shù)中創(chuàng)建一個(gè)元素div,在 componentDidMount方法中將 someRoot 放到 div 中 。 最后,通過
ReactDOM.createPortal(this.props.childen), domnode)將 children 傳遞到對(duì)應(yīng)的節(jié)點(diǎn)下。const someRoot = document.getElementById("someid"); class Modal extends React.Component { constructor(props) { super(props); this.el = document.createElement("div"); } componentDidMount() { someRoot.appendChild(this.el); } componentWillUnmount() { someRoot.removeChild(this.el); } render() { return ReactDOM.createPortal( this.props.children, this.el, ); } }什么是上下文有時(shí)我們必須將props 傳遞給組件樹,即使所有中間組件都不需要這些props 。上下文是一種傳遞props 的方法,而不用在每一層傳遞組件樹。
什么是 HooksHooks 是React版本16.8中的新功能。 請(qǐng)記住,我們不能在函數(shù)組件中使用state ,因?yàn)樗鼈儾皇穷惤M件。Hooks 讓我們?cè)诤瘮?shù)組件中可以使用state 和其他功能。
目前沒有重大變化,我們不必放棄類組件。
Hook 不會(huì)影響你對(duì) React 概念的理解。 恰恰相反,Hook 為已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我們將看到,Hook 還提供了一種更強(qiáng)大的方式來組合他們。
我們可以使用一些鉤子,例如useState,useEffect,useContext,useReducer等。
下面是 Hooks 的基本規(guī)則
Hooks 應(yīng)該在外層使用,不應(yīng)該在循環(huán),條件或嵌套函數(shù)中使用
Hooks 應(yīng)該只在函數(shù)組件中使用。
讓我們看一個(gè)例子來理解 hooks。 這是一個(gè)函數(shù)組件,它采用props并在UI上顯示這些props。 在useState鉤子的幫助下,我們將這個(gè)函數(shù)組件轉(zhuǎn)換為有狀態(tài)組件。 首先,我們?cè)诘?行定義狀態(tài),這相當(dāng)于
constructor(props) { super(props); this.state = { name:"myname", age:10, address:"0000 one street" } }useState返回兩個(gè)項(xiàng),一個(gè)是user,另一個(gè)是setUser函數(shù)。 user 是一個(gè)可以在沒有 this關(guān)鍵字的情況下直接使用的對(duì)象,setUser是一個(gè)可以用來設(shè)置用戶點(diǎn)擊第21行按鈕的狀態(tài)的函數(shù),該函數(shù)等效于以下內(nèi)容。
this.setState({name:"name changed"})
1 import React, { useState } from "react"; 2 3 export const UserDisplay = ({name, address, age}) => { 4 5 const [user, setUser] = useState({ name: "myname", age: 10, address: "0000 onestreet" }); 6 7 return ( 8 <> 9如何提高性能1013Name:11{user.name}121417Address:15{user.address}161821 24 > 25 ) 26 }Age:19{user.age}20我們可以通過多種方式提高應(yīng)用性能,以下這些比較重要:
適當(dāng)?shù)厥褂?b>shouldComponentUpdate生命周期方法。 它避免了子組件的不必要的渲染。 如果樹中有100個(gè)組件,則不重新渲染整個(gè)組件樹來提高應(yīng)用程序性能。
使用create-react-app來構(gòu)建項(xiàng)目,這會(huì)創(chuàng)建整個(gè)項(xiàng)目結(jié)構(gòu),并進(jìn)行大量?jī)?yōu)化。
不可變性是提高性能的關(guān)鍵。不要對(duì)數(shù)據(jù)進(jìn)行修改,而是始終在現(xiàn)有集合的基礎(chǔ)上創(chuàng)建新的集合,以保持盡可能少的復(fù)制,從而提高性能。
在顯示列表或表格時(shí)始終使用 Keys,這會(huì)讓 React 的更新速度更快
代碼分離是將代碼插入到多帶帶的文件中,只加載模塊或部分所需的文件的技術(shù)。
如何在重新加載頁(yè)面時(shí)保留數(shù)據(jù)單頁(yè)應(yīng)用程序首先在DOM中加載index.html,然后在用戶瀏覽頁(yè)面時(shí)加載內(nèi)容,或者從同一index.html中的后端API獲取任何數(shù)據(jù)。
如果通過點(diǎn)擊瀏覽器中的重新加載按鈕重新加載頁(yè)面index.html,整個(gè)React應(yīng)用程序?qū)⒅匦录虞d,我們將丟失應(yīng)用程序的狀態(tài)。 如何保留應(yīng)用狀態(tài)?
每當(dāng)重新加載應(yīng)用程序時(shí),我們使用瀏覽器localstorage來保存應(yīng)用程序的狀態(tài)。我們將整個(gè)存儲(chǔ)數(shù)據(jù)保存在localstorage中,每當(dāng)有頁(yè)面刷新或重新加載時(shí),我們從localstorage加載狀態(tài)。
如何在React進(jìn)行API調(diào)用我們使用redux-thunk在React中調(diào)用API。因?yàn)?b>reduce是純函數(shù),所以沒有副作用,比如調(diào)用API。
因此,我們必須使用redux-thunk從 action creators 那里進(jìn)行 API 調(diào)用。Action creator 派發(fā)一個(gè)action,將來自API的數(shù)據(jù)放入action 的 payload 中。Reducers 接收我們?cè)谏厦娴?b>redux循環(huán)中討論的數(shù)據(jù),其余的過程也是相同的。
redux-thunk是一個(gè)中間件。一旦它被引入到項(xiàng)目中,每次派發(fā)一個(gè)action時(shí),都會(huì)通過thunk傳遞。如果它是一個(gè)函數(shù),它只是等待函數(shù)處理并返回響應(yīng)。如果它不是一個(gè)函數(shù),它只是正常處理。
這里有一個(gè)例子。sendEmailAPI是從組件中調(diào)用的函數(shù),它接受一個(gè)數(shù)據(jù)并返回一個(gè)函數(shù),其中dispatch作為參數(shù)。我們使用redux-thunk調(diào)用API apiservice,并等待收到響應(yīng)。一旦接收到響應(yīng),我們就使用payload 派發(fā)一個(gè)action 。
import apiservice from "../services/apiservice"; export function sendEmail(data) { return { type:"SEND_EMAIL", payload: data }; } export function sendEmailAPI(email) { return function(dispatch) { return apiservice.callAPI(email).then(data => { dispatch(sendEmail(data)); }); } }總結(jié)要想有把握的面試,必須充分了解上述所有主題。 即使你目前正在使用React,理解這些概念也能增強(qiáng)你在職場(chǎng)中信心。
代碼部署后可能存在的BUG沒法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。
交流這篇來來回回整理了快五天,大家給個(gè)贊唄!
干貨系列文章匯總?cè)缦拢X得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/104279.html
摘要:分享一個(gè)比較完整的項(xiàng)目供大家交流學(xué)習(xí),這個(gè)項(xiàng)目的英文簡(jiǎn)介項(xiàng)目地址在線演示翻譯過來呢就是一個(gè)涉及面較廣的使用豆瓣作為數(shù)據(jù)源的。 分享一個(gè)比較完整的Vue2+項(xiàng)目供大家交流學(xué)習(xí),這個(gè)項(xiàng)目的英文簡(jiǎn)介:Awesome douban DEMO created with Vue2.x + Vuex + Vue-router + vue-resource 項(xiàng)目地址:https://github.co...
摘要:技術(shù)一面一面主要考察基礎(chǔ),有些會(huì)有技術(shù)筆試,比如騰訊,。騰訊的面試官就很喜歡問,安全,瀏覽器緩存方面的問題,計(jì)算機(jī)基礎(chǔ),但是要懂為什么。 這篇文章簡(jiǎn)單總結(jié)下2018年內(nèi)我的一些前端面試經(jīng)歷, 在這簡(jiǎn)單分享一下,希望對(duì)大家有所啟發(fā)。 樓主在深圳,畢業(yè)兩年。面的主要是深圳的幾家公司。 包括: 騰訊, 螞蟻金服, Lazada, Shopee, 有贊 等 。 樓主在準(zhǔn)備面試前, 想著復(fù)習(xí)一...
摘要:整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí)原文協(xié)作規(guī)范中文技術(shù)文檔協(xié)作規(guī)范阮一峰編程風(fēng)格凹凸實(shí)驗(yàn)室前端代碼規(guī)范風(fēng)格指南這一次,徹底弄懂執(zhí)行機(jī)制一次弄懂徹底解決此類面試問題瀏覽器與的事件循環(huán)有何區(qū)別筆試題事件循環(huán)機(jī)制異步編程理解的異步 better-learning 整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí) 原文:https://www.ahwgs.cn/youxiuwenzhan...
摘要:實(shí)際上是格林威治標(biāo)準(zhǔn)時(shí)間的同義詞默認(rèn)情況下,中的幾乎每個(gè)日期方法除了一個(gè)都是本地時(shí)間。如果你住在格林威治標(biāo)準(zhǔn)時(shí)間晚的的地區(qū),你會(huì)得到一個(gè)日期是月日。需要知道對(duì)象日期方法。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! JS中的 Date 很奇怪。當(dāng)我們需要處理日期和時(shí)間的時(shí)候比較麻煩,經(jīng)常借助像date-fns和 Mom...
摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關(guān)問題,其他公司壓根沒問。我自己回答的是自己開發(fā)組件面臨的問題。完全不用擔(dān)心對(duì)方到時(shí)候打電話核對(duì)的問題。 2019的5月9號(hào),離發(fā)工資還有1天的時(shí)候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經(jīng)是第2個(gè)月沒工資了。 公...
閱讀 2706·2021-11-11 16:54
閱讀 2329·2021-10-09 09:44
閱讀 2548·2019-08-30 15:54
閱讀 1936·2019-08-30 11:24
閱讀 1175·2019-08-29 17:03
閱讀 2107·2019-08-29 16:22
閱讀 2086·2019-08-29 13:11
閱讀 1044·2019-08-29 12:14