不折騰的前端,和鹹魚有什麼區別html
返回目錄
React 是現現在流行的前端框架,也是不少大廠面試必備。前端
React 與 Vue 雖有不一樣,但一樣做爲一款 MV*
框架,雖然實現可能不同,但在一些理念上仍是有類似的,例如數據驅動、組件化、虛擬 DOM 等。vue
固然,還有一些問題,可能最近都無法搞清楚的了,畢竟羅馬不是一天能建成的:react
這些每每要更深刻挖掘方可得知本身的結論,任重道遠,不停歇。webpack
返回目錄
[x] React 和原生對比git
[x] React 和 Vue 對比github
[x] React 生命週期web
constructor
、getDerivedStateFromProps
、render
、componentDidMount
getDerivedStateFromProps
、shouldComponentUpdate
、render
、getSnapshotBeforeUpdate
、componentDidUpdate
componentWillUnmount
[x] setState
面試
[x] this 指向問題算法
bind
修正bind
和箭頭函數的區別value
和 defaultValue
[x] 組件通信
props
Context
Redux
Redux
:Redux
、React-Redux
以及 Redux-Saga
工做流Mixin
、HOC
和 Hook
[x] 性能優化
prerender-spa-plugin
react-placeholder
html-webpack-plugin
Tree Shaking
SplitChunkPlugin
Code Splitting
react-lazyload
返回目錄
經過 Ajax
從後端獲取數據,而後經過 jQuery 生成 DOM 結果更新到頁面中。
可是隨着業務發展,項目愈來愈複雜,交互性愈來愈強,每每用戶在某個時刻可能操做好幾塊內容,從而 DOM 的操做愈來愈頻繁,頁面性能逐步下降,用戶也不滿意這樣卡慢的現狀了。
這時候有了 MVVM,雙向數據綁定讓數據在修改的時候同步 DOM 的更新,反之亦可。
這個設定大大下降手動維護 DOM 的成本,而 MVVM 爲 React 的特性之一,雖然 React 屬於單項數據流,須要咱們手動實現雙向數據綁定。
光靠綁定是不夠的,這樣無法解決頻繁操做 DOM 的問題。
因此 React 內部實現了一套虛擬 DOM 的更新,它將真實 DOM 在 JS 中作一套緩存,每次有數據更新的時候,先內部經過 Diff
算法進行比對,而後收集一籮筐更新後,纔對 DOM 進行更新,這樣就大大下降了 DOM 的操做次數。
那麼,Diff
怎麼運做呢?
Diff
獲取虛擬 DOM 節點變動的 4 種狀況比較:節點類型變了、節點類型同樣,僅僅屬性或者屬性值變了、文本變了、增長、刪除或者移動了子節點。
React 不一樣於 Vue,能夠經過 v-model
的形式,讓用戶的操做和 JavaScript 存儲的數據同步更新,它須要經過 setState
來更新組件內容。
可是,若是想經過一個組件來渲染它兄弟組件,React 一開始在這塊作得並非那麼好,因此就須要引入一個狀態管理中心,來幫助咱們管理狀態(state
),於是就有了 Redux
。
在 Redux
中,當 state
有變化的時候,依賴這個 state
的組件就會從新渲染,這樣就解決了組件間數據傳遞的問題。
Redux
有個問題,就是修改某個 state
的時候,須要通過 action.js
、types.js
、reducers.js
這一系列文件,這樣子 Redux
的數據流雖然很是正規,可是寫起來複雜啊。
因此,社區又出現了另外一套解決方案,也就是 Mobx
。
Mobx
推崇代碼簡約移動,只須要定義一個可貫徹的對象,而後在哪一個組件中使用到了這個可觀察對象,而且這個對象的數據有更改,那就會從新渲染,這使得 Mobx
開發項目的時候能夠簡單快速地完成不少功能。
可是 Mobx
也有缺點,就是數據流太過隨意,出了 Bug 很差定位。
因此,針對於小項目來講,社區推薦使用 MobX,對大項目推薦使用 Redux。
本段文本參考的內容已記錄在參考文獻中
返回目錄
返回目錄
返回目錄
Webpack
+ Babel
去搭建腳手架。Vue-router
和 Vuex
,而 React 有 React-router
和 React-Redux
。返回目錄
HTML
的模板進行渲染,而 React 推薦 JSX 的書寫方式。v-model
綁定的數據,用戶改變輸入值後對應的值也相應改變。而 React 須要經過 setState
進行設置變化。Diff
隊列保存須要更新的 DOM,獲得 patch
樹,再統一批量更新 DOM。實話實說是看別人說的異同,擔憂有 「小夥伴」 有本身觀點,而後在這裏被噴, jsliang 不敢持有任何本身的觀點,可是面試我老是要這麼回答面試官的 /滑稽
返回目錄
React 的核心流程能夠分爲兩個部分:
reconciliation
(調度算法,也可稱爲 render
):
state
與 props
diff
算法,獲取 VDOM change
commit
爲何須要 Fiber
?
隨着應用變得愈來愈龐大,整個更新渲染的過程開始變得吃力,大量的組件渲染會致使主進程長時間被佔用,致使一些動畫或高頻操做出現卡頓和掉幀的狀況。
而關鍵點,即是 同步阻塞。
在以前的調度算法中,React 須要實例化每一個類組件,生成一顆組件樹,使用 同步遞歸 的方式進行遍歷渲染,而這個過程最大的問題就是沒法 暫停和恢復。
因此,爲了解決這個問題(同步阻塞),一般有兩種方法: 異步 與 任務分割。
而 React Fiber
即是爲了實現任務分割而誕生的。
React Fiber
簡述:
stack reconciler
重構成新版的 fiber reconciler
,變成了具備鏈表和指針的 單鏈表樹遍歷算法。經過指針映射,每一個單元都記錄着遍歷當下的上一步與下一步,從而使遍歷變得能夠被暫停和重啓。React Fiber
核心:
返回目錄
React 逐漸廢棄的生命週期方法:
componentWillMount
componentWillReceiveProps
componentWillUpdate
返回目錄
返回目錄
返回目錄
constructor
:構造函數,最早被執行,一般在構造函數中初始化 state
對象或者給自定義方法綁定 this
getDerivedStateFromProps
:static getDerivedStateFromProps(nextProps, prevState)
,這是個靜態方法,當咱們接收到新的屬性想去修改咱們 state
,可使用 getDerivedStateFromProps
。render
:render
函數是個純函數,只返回須要渲染的東西,不該該包含其餘的業務邏輯,能夠返回原生 DOM、React 組件、Fragment
、Portals
、字符串和數字等內容。componentDidMount
:組件裝載以後調用,此時咱們能夠獲取到 DOM 節點並操做,好比對 Canvas
、SVG
等操做。服務器請求、訂閱均可以寫這個裏面,可是記得在 componentWillUnmount
中取消訂閱。React 的接口請求是放在 componentDidMount
裏面比較合適,舊版本有人放在 componentWillMount
裏面,從而致使屢次請求,如今 componentWillMount
不推薦使用了,因此轉 componentDidMount
就很是科學了。
存在如下問題:
getDerivedStateFromProps
是靜態的?當它設置爲靜態函數,代表這個函數不能經過 this
訪問到 class
的屬性,也並不推薦直接訪問屬性。
setState
?能夠在 componentDidMount
和 componentDidUpdate
中使用,此時 DOM 已經穩定下來了,能夠進行數據的操做了。
返回目錄
getDerivedStateFromProps
:此方法在更新階段也會被調用。shouldComponentUpdate
:shouldComponentUpdate(nextProps, nextState)
,有兩個參數,表示新的屬性和變化以後的 state
,返回一個布爾值。若是是 true
表示會觸發從新渲染,false
表示不會觸發從新渲染,默認返回 true
。能夠利用這個生命週期來優化 React 程序性能。render
:同掛載階段 render
。getSnapshotBeforeUpdate
:getSnapshotBeforeUpdate(prevProps, prevState)
,這個方法會在 render
以後,componentDidUpdate
以前調用,有兩個參數,表示以前屬性和以前的 state
。這個函數有一個返回值,會做爲第三個參數傳給 componentDidUpdate
,若是不須要返回值,能夠返回 null
,這個方法必須和 componentDidUpdate
配合使用。componentDidUpdate
:componentDidUpdate(prevProps, prevState, snapshot)
,在 getSnapshotBeforeUpdate
props
,以前的 state
,以及 snapshot
。參數 snapshot
是 getSnapshotBeforeUpdate
返回的,若是觸發某些回調函數時須要用到 DOM
元素的狀態,則將對比或者計算過程遷移到 getSnapshotBeforeUpdate
,而後在 componentDidUpdate
中統一觸發回調或者更新狀態。返回目錄
componentWillUnmount
:當組件被卸載或者銷燬時會被調用,在這裏清除定時器,或者取消網絡請求,用來清理無效的 DOM 元素等垃圾回收工做。返回目錄
setState
是 React 中用於修改狀態,更新視圖的方法。
返回目錄
在代碼中調用 setState
以後,React 會將傳入的參數對象與組件當前的狀態合併,觸發所謂的調和過程(Reconciliation
)。
通過調和過程,React 會以相對高效的方式根據新的狀態構建 React 元素樹而且着手從新渲染整個 UI 界面。
在 React 獲得元素樹以後,React 會自動計算新樹和老樹之間的節點差別,而後根據差別對界面進行最小化從新渲染。
在差別計算算法(Diff
)中,React 可以相對精確地知道哪些位置發生了改變以及英國如何改變,保證了按需更新,而不是所有從新渲染。
簡單來講:
Diff
)返回目錄
回答:有時候同步,有時候異步。
setState
在合成事件和鉤子函數中是異步的,在原生事件和 setTimeout
是同步的。setState
的異步,並非說內部由異步代碼實現,它自己執行的過程和代碼是同步的,只是合成事件和鉤子函數的調用順序在更新以前,致使在合成事件和鉤子函數中無法立馬拿到更新後的值,從而造成了所謂的異步。setState
能夠經過第二個參數 setState(partialState, callback)
,在回調方法中拿到更新後的結果。返回目錄
在 React 中有幾種方法能夠修正 this
的指向,這裏例舉 4 種方法:
import React, { Component } from 'react' class App extends Component { constructor (props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick () { console.log('jsliang 2020'); } handleClick2 = () => { console.log('jsliang 2021'); } render () { // 四種綁定方法 return ( <div className='App'> {/* 方法一:經過 constructor 中進行 bind 綁定 */} <button onClick={this.handleClick}>btn 1</button> {/* 方法二:在裏邊綁定 this */} <button onClick={this.handleClick.bind(this)}>btn 2</button> {/* 方法三:經過箭頭函數返回事件 */} <button onClick={() => this.handleClick()}>btn 3</button> {/* 方法四:讓方法變成箭頭函數 */} <button onClick={this.handleClick2}>btn 4</button> {/* 額外:直接調用不須要綁定 this */} {this.handleClick()} </div> ) } } export default App;
那麼,使用 bind
和箭頭函數有什麼區別嗎?
箭頭函數除了代碼少,與普通函數最大的不一樣就是:this
是由聲明該函數時候定義的,通常是隱性定義爲聲明該函數時的做用域 this
。
經過 bind
的話,至關於:Foo.prototype.a = function() {}
,是經過原型鏈的一個指正綁定。
而經過箭頭函數的話,就至關於:
class Foo { constructor() { this.a = () => {}; } }
返回目錄
在 Ant Design
中,對 Input
輸入框進行操做,若是是改變 defaultValue
會發現毫無做用。
這是由於 React 的 form
表單組件中的 defaultValue
一經傳遞值後,後續改變 defaultValue
都將不起做用,被忽略了。
具體來講這是一種 React 非受控組件,其狀態是在 input
的 React 內部控制,不受調用者控制。
因此受控組件就是能夠被 React 狀態控制的組件。雙向數據綁定就是受控組件,你能夠爲 form
中某個輸入框添加 value
屬性,而後控制它的一個改變。而非受控組件就是沒有添加 value
屬性的組件,你並不能對它的固定值進行操做。
返回目錄
props
方式,向子組件進行通信。props
中傳遞方法,而後子組件調用這個方法,將自身須要傳遞的信息,傳遞到父組件的做用域中。Context
,或者 Redux
進行數據通信。返回目錄
網上有挺多關於 Redux
、React-Redux
、Redux-Saga
的使用,這裏就不廢話介紹了,仍是講講 jsliang 在工做中的一個使用吧。
工做目錄
- 某個頁面文件夾 - View.jsx 當前頁面主入口 - Child.jsx 子組件 - Brother.jsx 兄弟組件 - action.js 動做 - types.js 類型 - saga.js 調用接口 - reducers.js 處理數據
正常的一個工做目錄如上所示,咱們工做中是怎麼個使用方式呢?
首先,在 View.jsx
中經過 React-Redux
鏈接 React
和 Redux
而後,假設如今 Child.jsx
須要調用接口(異步處理),那麼會:
action.js
中定義這個方法,會傳遞什麼參數。types.js
是輔助 action.js
的一個內容,爲了防止方法體的重複,咱們會在 types.js
中定義大寫的 action
名字。View.jsx
中經過 dispatch
觸發方法,例如 dispatch(getPage(page, perPage))
。reducers.js
中和 sage.js
中都能監聽到這個方法,可是咱們是在 sage.js
中調用接口並處理數據。sage.js
中的傳遞給 reducers.js
中,讓它去處理數據。接着,若是 Brother.jsx
只是單純地想處理數據並在 Child.jsx
中使用,那麼咱們處理方式是跟上面同樣的,只是直接在 reducers.js
中處理,而不須要再在 sage.js
中調用接口而已。
最後,咱們再看看 redux
和 react-reduxt
的工做流程加深印象:
Redux
React-Redux
返回目錄
前端發展速度很是之快,頁面和組件變得愈來愈複雜,如何更好的實現 狀態邏輯複用 一直都是應用程序中重要的一部分,這直接關係着應用程序的質量以及維護的難易程度。
Mixin
、HOC
和 Hook
是 React 採用的 3 種 狀態邏輯複用 的技術,Mixin
已被拋棄,HOC
正當壯年,Hook
初露鋒芒,掌握它迭代因素和規律很是重要。
返回目錄
Mixin
(混入)是一種經過擴展收集功能的方式,它本質上是將一個對象的屬性拷貝到另外一個對象上面去。
不過你能夠拷貝任意多個對象的任意個方法到一個新對象上去,這是繼承所不能實現的。
它的出現主要就是爲了解決代碼複用問題。
可是,它會帶來一些危害:
Mixin
相互依賴、相互耦合,不利於代碼維護Mixin
中的方法可能會互相沖突Mixin
很是多時,組件是能夠感知到的,甚至還要爲其作相關處理,這樣給代碼形成滾雪球式的複雜性。返回目錄
基於 Mixin
的問題,React 推出對裝飾模式的一種實現:高階組件(HOC
)。
高階組件接收一個組件做爲參數,並返回一個新的組件。
高階組件(
HOC
)是 React 中的高級技術,用來重用組件邏輯。但高階組件自己並非 React API。它只是一種模式,這種模式是由 React 自身的組合性質必然產生的。
function visible(WrappedComponent) { return class extends Component { render() { const { visible, ...props } = this.props; if (visible === false) return null; return <WrappedComponent {...props} />; } } }
高階組件能夠應用於 日誌打點、可用權限控制、雙向綁定、表單校驗等。
高階組件解決了 Mixin
帶來的問題:
可是,有光的地方總有暗,高階組件也存在一些缺陷:
HOC
須要在原組件上進行包裹或者嵌套,若是大量使用 HOC
,將會產生很是多的嵌套,這讓調試變得很是困難。HOC
能夠劫持 props
,在不遵照約定的狀況下也可能形成衝突。返回目錄
Hook
是 React v16.7.0-alpha
中加入的新特性。它可讓你在 class
之外使用 state
和其餘 React 特性。
使用 Hook
,你能夠在將含有 state
的邏輯從組件中抽象出來,這將可讓這些邏輯容易被測試。
同時,Hook
能夠幫助你在不重寫組件結構的狀況下複用這些邏輯。
因此,它也能夠做爲一種實現狀態邏輯複用的方案。
Hook
使用帶來的好處:
Hook
和 Mixin
在用法上有必定的類似之處,可是 Mixin
引入的邏輯和狀態是能夠相互覆蓋的,而多個 Hook
之間互不影響,這讓咱們不須要在把一部分精力放在防止避免邏輯複用的衝突上。HOC
的狀況下讓咱們的代碼變得嵌套層級很是深,使用 Hook
,咱們能夠實現扁平式的狀態邏輯複用,而避免了大量的組件嵌套。class
組件構建咱們的程序時,他們各自擁有本身的狀態,業務邏輯的複雜使這些組件變得愈來愈龐大,各個生命週期中會調用愈來愈多的邏輯,愈來愈難以維護。使用 Hook
,可讓你更大限度的將公用邏輯抽離,將一個組件分割成更小的函數,而不是強制基於生命週期方法進行分割。class
可能須要掌握更多的知識,須要注意的點也越多,好比 this
指向、綁定事件等等。另外,計算機理解一個函數比理解一個 class
更快。Hooks
讓你能夠在 class
以外使用更多 React 的新特性。返回目錄
<div id="root"> SVG </div>
,也可使用插件 prerender-spa-plugin
插件進行首屏渲染。html-webpack-plugin
插件自動插入 loading
,這樣切換的時候,就不須要在每一個頁面都寫一套 loading
。Tree Shaking
來減小一些代碼。SplitChunkPlugin
自動拆分業務基礎庫,減小大文件的存在。Code Splitting
來懶加載代碼,提升用戶的加載體驗。例如經過 React Loadable
來將組件改寫成支持動態 import
的形式。react-lazyload
這種成熟組件來進行懶加載的支持。react-placeholder
能夠解決這種狀況。返回目錄
本系列有 67 篇參考文獻。
返回目錄
2019:
2018:
2017:
返回目錄
2017:
返回目錄
2020:
2017:
返回目錄
最新:
2019:
2018:
2017:
返回目錄
2019:
2016:
返回目錄
2019:
2018:
2017:
2015:
返回目錄
2018:
2017:
2015:
返回目錄
2015:
返回目錄
2019:
2017:
返回目錄
2019:
2018:
返回目錄
2018:
返回目錄
返回目錄
2019:
2018:
2017:
返回目錄
2019:
2018:
2017:
2015:
jsliang 的文檔庫由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議 進行許可。<br/>基於 https://github.com/LiangJunrong/document-library 上的做品創做。<br/>本許可協議受權以外的使用權限能夠從 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 處得到。