React項目開發(fā)中函數(shù)組件與函數(shù)式編程關(guān)系
函數(shù)組件 和 函數(shù)式編程 有關(guān)系么?
長期使用React
的同學(xué)應(yīng)該知道,React
中存在兩種組件:
Class Component
,類組件Function Component
,函數(shù)組件
既然提到類和函數(shù),那么很自然的,我們會進(jìn)一步思考:
- 類組件和
OOP
(面向?qū)ο缶幊蹋┯嘘P(guān)系么? - 函數(shù)組件和
FP
(函數(shù)式編程)有關(guān)系么?
畢竟,如果類組件和OOP
有關(guān),那么OOP
中的思想(繼承、封裝、多態(tài)...)也能指導(dǎo)類組件的業(yè)務(wù)開發(fā)(函數(shù)組件與FP
的關(guān)系同理)。換言之,我們可以直接用這些編程范式的最佳實(shí)踐指導(dǎo)React
項目開發(fā)。
編程范式與DSL
首先,我們應(yīng)該明確,框架語法本質(zhì)是一種DSL
(領(lǐng)域相關(guān)語言),他是為了某個特定領(lǐng)域的開發(fā)量身定制的。
比如,React
作為一款針對view開發(fā)的DSL
,雖然不同的view
使用的框架不同,比如:
- 對于
web
,框架為ReactDOM
- 對于小程序,框架為
Taro
- 對于原生開發(fā),字節(jié)內(nèi)部有個叫
React Lynx
的框架
但這些框架都大體遵循同一套DSL
(React
語法),這套DSL
并不屬于某一種編程范式,而應(yīng)該被視為不同編程范式中,更符合view開發(fā)的語言特性的集合。
所以,作為React DSL
的一部分,函數(shù)組件可以體現(xiàn)OOP
的思想,類組件也能體現(xiàn)FP
的思想。只要這些思想有利于view開發(fā),就可以納入DSL
的語法中。
比如,下面的函數(shù)組件Header
,是由WelcomeMessage
與LogoutButton
組件組合而成,這是OOP
中的組合優(yōu)于繼承思想:
function Header(props) { return ( <div> <WelcomeMessage name={props.name} /> <LogoutButton onClick={props.onLogout} /> </div> ); }
再比如,下面的類組件Cpn
中,要改變狀態(tài)count
,并不是通過突變(類似this.state.count++
),而是調(diào)用this.setState
,傳入不可變數(shù)據(jù):
class Cpn extends React.Component { // ... onClick() { const count = this.state.count; this.setState({count: count + 1}); } render() { // ... } }
使用不可變數(shù)據(jù)屬于FP
中的思想。
所以,當(dāng)我們要深入了解某個React
特性時,應(yīng)該以如下順序遞進(jìn)的思考:
React
的開發(fā)理念是什么?- 為了實(shí)現(xiàn)這套理念,吸收了哪些編程范式中的思想
- 這些思想如何在
React
中落地
如果我們用上述思考過程研究函數(shù)組件與函數(shù)式編程的關(guān)系,會發(fā)現(xiàn):
- 函數(shù)組件屬于落地的產(chǎn)物(上述思考的第三步)
- 函數(shù)式編程屬于編程范式(上述思考的第二步)
這就是兩者的關(guān)系 —— 函數(shù)組件屬于多種編程范式(主要是OOP
與FP
)在React
中最終的落地產(chǎn)物,其中借鑒了一部分FP
的思想。
我們不應(yīng)該將函數(shù)組件單純視為FP
在React
中的具象體現(xiàn)。
那么,函數(shù)組件究竟是如何演進(jìn)而來的呢?
函數(shù)組件的演進(jìn)
讓我們按照上述三步演進(jìn)順序思考。首先,React
的開發(fā)理念踐行了如下公式(即:UI
是數(shù)據(jù)快照經(jīng)過函數(shù)映射而來):
UI = fn(snapshot)
要落地這個理念,有兩個要素需要實(shí)現(xiàn):
- 數(shù)據(jù)快照
- 函數(shù)映射
在這里,FP
中不可變數(shù)據(jù)更適合作為數(shù)據(jù)快照的載體,所以React
中狀態(tài)是不可變的,因?yàn)闋顟B(tài)的本質(zhì)是快照。
而函數(shù)映射的載體則沒有特殊要求。在React
中,每次觸發(fā)更新,所有組件都會重新render
,render
的過程就是函數(shù)映射的過程,輸入是props
與state
,輸出是JSX
。
與React
相對的,Vue
中組件則更符合OOP
的理念,考慮如下App
組件:
const App = { setup(initialProps) { const count = reactive({count: 0}) const add = () => { count.value++ } return {count, add} } template: "...省略" }
組件的setup
方法只會在初始化時執(zhí)行一次,后續(xù)觸發(fā)更新時操作的都是同一個閉包中的數(shù)據(jù)。這里面的閉包就是OOP
思想中的實(shí)例。
既然React
對函數(shù)映射的載體沒有特殊要求,那么類組件、函數(shù)組件都是可以的。
那為什么函數(shù)組件最終替代了類組件成為React
開發(fā)的主流呢?很多同學(xué)認(rèn)為函數(shù)組件的Hooks可以更好的復(fù)用邏輯這一點(diǎn),是函數(shù)組件優(yōu)于類組件的主要原因。
但實(shí)際上,基于裝飾器的類開發(fā)模式早已被驗(yàn)證是優(yōu)秀的邏輯復(fù)用模式,類組件配合TS
裝飾器的模式是行得通的。
主要原因還是 —— 函數(shù)組件能夠更好的落地UI = fn(snapshot)
這一理念。
剛才說過,公式中的snapshot
是快照的含義。在React
中,快照主要包括三類數(shù)據(jù):
state
props
context
對于同一個組件,根據(jù)公式UI = fn(snapshot)
,相同的快照輸入應(yīng)該獲得相同輸出(JSX
)。
但狀態(tài)更新也可能觸發(fā)副作用,比如請求數(shù)據(jù)、操作DOM
...
在類組件中,這些副作用邏輯被分散在各個生命周期鉤子函數(shù)中,React
無法掌控。
而在函數(shù)組件中:
- 副作用受限在
useEffect
中。每次render
,React
都會保證上次的副作用效果已經(jīng)被清除(通過useEffect
回調(diào)的返回值函數(shù)) ref
的傳播也需要借由forwardRef
,這進(jìn)一步限制了ref
可能的影響范圍- 數(shù)據(jù)請求的副作用被交給
Suspense
處理,考慮下面組件:
function UserList({id}) { // 異步請求數(shù)據(jù) const data = use(fetchUser(id)); // ... }
使用時:
<Suspense fallback={<div>加載中...</div>}> <UserList id={1}/> </Suspense>
總而言之,使用函數(shù)組件時,所有副作用都處于一種受到管控的狀態(tài),可以盡可能保證每次更新時相同的快照輸入,獲得相同的JSX輸出,所以函數(shù)組件在React
中才會發(fā)揚(yáng)光大。
同時,這也契合了FP
中的純函數(shù)思想。
總結(jié)
函數(shù)組件并不是函數(shù)式編程在React
中的具體實(shí)現(xiàn),而是React
的設(shè)計理念UI = fn(snapshot)
落地的最好載體。
在React
中,還吸收了其他編程范式中的優(yōu)秀思想。FP
只是其中影響React
最深的一種。畢竟,一切落地都是為了踐行最初的設(shè)計理念。
以上就是React項目開發(fā)中函數(shù)組件與函數(shù)式編程關(guān)系的詳細(xì)內(nèi)容,更多關(guān)于React函數(shù)組件函數(shù)式編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React+Electron快速創(chuàng)建并打包成桌面應(yīng)用的實(shí)例代碼
這篇文章主要介紹了React+Electron快速創(chuàng)建并打包成桌面應(yīng)用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12React使用hook如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定
這篇文章主要介紹了React使用hook如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03使用webpack配置react-hot-loader熱加載局部更新
這篇文章主要介紹了使用webpack配置react-hot-loader熱加載局部更新,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01React Native之ListView實(shí)現(xiàn)九宮格效果的示例
本篇文章主要介紹了React Native之ListView實(shí)現(xiàn)九宮格效果的示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08在 React 中使用 Redux 解決的問題小結(jié)
在 React 中組件通信的數(shù)據(jù)流是單向的,頂層組件可以通過 props 屬性向下層組件傳遞數(shù)據(jù),而下層組件不能直接向上層組件傳遞數(shù)據(jù),這篇文章主要介紹了使用react+redux實(shí)現(xiàn)彈出框案例,需要的朋友可以參考下2022-10-10