歷史
React在2013年開源,在2015引入函數式組件,不過在平常開發中常常被忽略。
ReactJS Core Team 確實大部分紅員都曾在推特上公開誇讚過對函數式編程 與 ML 系語言(或其特性)的優勢:Sebastian 平常提到 OCaml,Sophie 至少提過 Rust/Kotlin/Swift 而且嫌棄過 Dart 沒有 ADT 和 non-nullability,Dan 和 Andrew 的 Redux 主要受 Elm 影響,Dan 曾提過學習高階函數後省去了一半設計模式,Andrew 的 Recompose 主要利用了函數的組合性,其餘成員未知。前端
爲何引入class組件:vue
Chenglou: God forbid React api led newcomers into something familiar instead of FP purists’ agendas lol。
難道還不容許 React API 設計得對新人更友好了?
Cristiano: So I guess also making React in JS in the first place was the ultimate trolling.
咱們先把 React 作成 JS 就是找罵啊……
Jordan: They're onto us.
這怎麼怪到咱們頭上了……
事實是,即便在 FB 內部,也顯然不是全部程序員都熟悉函數式編程的概念。作一個前端框架是拿來用的,若是有你們熟悉的概念能夠對照,爲何刻意要去用你們不熟悉的概念呢?對於遷移至 ES6 Class,主要的目的是爲了減小 React 特有的 API,畢竟 並不由於它「接受一個函數」就比 ES6 class 更函數式了……react
在以前class組件一般被認爲有更多的功能,例如能夠擁有state,能夠調用生命週期,而如今在hooks引入以後這個說法就不成立了。之前Function組件沒有state,因此也叫SFC(stateless functional component),如今更新叫作FC(functional component)。或許你也據說過,這兩類組件中,有一類的性能更好。哪一類呢?不少這方面的性能測試,都是 有缺陷的 ,所以要從這些測試中 得出結論 ,不得不謹慎一點。性能主要取決於你代碼要實現的功能(以及你的具體實現邏輯),和使用函數組件仍是類組件,沒什麼關係。咱們觀察發現,儘管函數組件和類組件的性能優化策略 有些不一樣 ,可是他們性能上的差別是很微小的。程序員
先看下面兩個組件:
函數式組件在父組件中執行的函數的方法:編程
function Greeting() { return <p>Hello</p>; } // Inside React const result = Greeting(props); // <p>Hello</p>
類組件在父組件中先實例化一個對象,而後執行這個對象上的render方法:設計模式
class Greeting extends React.Component { render() { return <p>Hello</p>; } } // Inside React const instance = new Greeting(props); // Greeting {} const result = instance.render(); // <p>Hello</p>
再來看兩段代碼
一個兩秒後執行alter的按鈕的函數式組件api
function ProfilePage(props) { const showMessage = () => { alert('我是 ' + props.user); }; const handleClick = () => { setTimeout(showMessage, 2000); }; return ( <button onClick={handleClick}>Function</button> ); }
一個兩秒後執行alter的按鈕的函數式組件數組
class ProfilePage extends React.Component { showMessage = () => { alert('我是' + this.props.user); }; handleClick = () => { setTimeout(this.showMessage, 2000); }; render() { return <button onClick={this.handleClick}>Class</button>; } }
通常咱們會認爲上面兩個組件是徹底等價的。開發者常常像上面這樣,在函數組件和類組件之間重構代碼,卻沒有意識到他們隱含的差別。然而這兩個是有一些差異,你有看出這兩個的區別嗎?
咱們用一個組件將上面兩個組件包裹緩存
function App() { const [user, setUser] = useState('小碼王') return ( <div className="App"> <p>{user}</p> <button onClick={() => setUser('小碼世界')}>changeName</button> <br/> <ClassButton user={user}/> <FunctionButton user={user}/> </div> ); }
當咱們先點擊Class按鈕而後再點擊changName按鈕,alert出的我是小碼世界,而咱們先點擊Function再點擊changName,alter出的我是小碼王。
爲何會這樣呢?
咱們能夠看到在函數式組件中user是在props中讀取的,而在類組件中user是從this.props中讀取的,區別就在this上,在react中props是不可變,而this是可變的,因此當父組件的值改變時this也改變了。
有什麼方法使類組件也實現函數式組件的效果呢?性能優化
class ProfilePage extends React.Component { render() { // Capture the props! const props = this.props; const showMessage = () => { alert('我是 ' + props.user); }; const handleClick = () => { setTimeout(showMessage, 2000); }; return <button onClick={handleClick}>Class</button>; } }
在render函數中創造了一個props的閉包,這個問題就解決了,是否是感受段代碼比較熟悉,其實在render函數裏面的代碼抽離出來就是函數式的組件了。因此當函數式組件有render方法之外的功能,就能夠實現類組件的任何功能了,這就是hooks了。
在 Hooks 的協議設計考量上,與本來的 React HoC Design 其實會有不少的不一樣,主要緣由也許就在於 HoC 總的來講是 Props 導向的設計,而 Hooks 則是更 functional 風格的調用形態。而且,HoC 可能會傾向於避免產生過多的 HoC 層級,而產生過深的 VDOM Tree。Hooks 大能夠更自由地講更多的 use 方法分離式的調用,也可以獲得更大的自由度;
從react team 公佈 hooks的概念時,淘寶內部作了一些簡單的研究與調研,結論是hooks是類react體系的將來主流編程模式,不管是基於hooks後業務代碼會更簡練仍是複用更容易,這些都是下降了react 編程的門檻。
尤雨溪也稱vue3.0的特性吸收了不少hooks的靈感,並在最新的RFC(意見徵求稿)中發佈了Function API + Hooks 。
React Hook讓無狀態組件擁有了許多隻有有狀態組件的能力,如自更新能力(setState,使用useState),訪問ref(使用useRef或useImperativeMethods),訪問context(使用useContext),使用更高級的setState設置(useReducer),及進行相似生命週期的階段性方法(useEffect或useLayoutEffect)。
function useReducer(reducer, initialArg,init){ var initialState = void 0; if (init !== undefined) { initialState = init(initialArg); } else { initialState = initialArg; } function dispatch(action){ memoizedState = reducer(memoizedState,action); render(); } memoizedState = memoizedState||initialState; return [memoizedState, dispatch]; } function useState(initialState){ return useReducer((oldState, newState)=>newState, initialState); }
useRef() === useState({current: initialValue })[0]
useCallback和useMemo的參數跟useEffect一致,他們之間最大的區別有是useEffect會用於處理反作用,而前兩個hooks不能。
useMemo和useCallback都會在組件第一次渲染的時候執行,以後會在其依賴的變量發生改變時再次執行;而且這兩個hooks都返回緩存的值,useMemo返回緩存的變量,useCallback返回緩存的函數。