大家好,我卡頌。
長期使用React的同學應該知道,React中存在兩種組件:
既然提到「類」和「函數」,那么很自然的,我們會進一步思考:
畢竟,如果類組件和OOP有關,那么OOP中的思想(繼承、封裝、多態...)也能指導類組件的業務開發(函數組件與FP的關系同理)。換言之,我們可以直接用這些編程范式的最佳實踐指導React項目開發。
那么,「函數組件」和「函數式編程」究竟是什么關系呢?本文會圍繞這個話題展開講解。
首先,我們應該明確,「框架語法」本質是一種DSL(領域相關語言),他是為了「某個特定領域的開發」量身定制的。
比如,React作為一款針對「view開發」的DSL,雖然不同的view使用的框架不同,比如:
但這些框架都大體遵循同一套DSL(React語法),這套DSL并不屬于某一種編程范式,而應該被視為「不同編程范式中,更符合view開發的語言特性的集合」。
所以,作為React DSL的一部分,函數組件可以體現OOP的思想,類組件也能體現FP的思想。只要這些思想有利于「view開發」,就可以納入DSL的語法中。
比如,下面的函數組件Header,是由WelcomeMessage與LogoutButton組件組合而成,這是OOP中的「組合優于繼承」思想:
function Header(props) { return ( <div> <WelcomeMessage name={props.name} /> <LogoutButton onClick={props.onLogout} /> </div> );}
再比如,下面的類組件Cpn中,要改變狀態count,并不是通過突變(類似this.state.count++),而是調用this.setState,傳入不可變數據:
class Cpn extends React.Component { // ... onClick() { const count = this.state.count; this.setState({count: count + 1}); } render() { // ... }}
「使用不可變數據」屬于FP中的思想。
所以,當我們要深入了解某個React特性時,應該以如下順序遞進的思考:
如果我們用上述思考過程研究「函數組件與函數式編程的關系」,會發現:
這就是兩者的關系 —— 函數組件屬于多種編程范式(主要是OOP與FP)在React中最終的落地產物,其中借鑒了一部分FP的思想。
我們不應該將函數組件單純視為FP在React中的具象體現。
那么,函數組件究竟是如何演進而來的呢?
讓我們按照上述三步演進順序思考。首先,React的開發理念踐行了如下公式(即:UI是數據快照經過函數映射而來):
UI = fn(snapshot)
要落地這個理念,有兩個要素需要實現:
在這里,FP中「不可變數據」更適合作為「數據快照」的載體,所以React中狀態是不可變的,因為狀態的本質是快照。
而「函數映射」的載體則沒有特殊要求。在React中,每次觸發更新,所有組件都會重新render,render的過程就是「函數映射」的過程,輸入是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方法只會在初始化時執行一次,后續觸發更新時操作的都是同一個閉包中的數據。這里面的閉包就是OOP思想中的實例。
既然React對「函數映射」的載體沒有特殊要求,那么類組件、函數組件都是可以的。
那為什么函數組件最終替代了類組件成為React開發的主流呢?很多同學認為「函數組件的Hooks可以更好的復用邏輯」這一點,是函數組件優于類組件的主要原因。
但實際上,基于裝飾器的類開發模式早已被驗證是優秀的邏輯復用模式,類組件配合TS裝飾器的模式是行得通的。
主要原因還是 —— 函數組件能夠更好的落地UI = fn(snapshot)這一理念。
剛才說過,公式中的snapshot是「快照」的含義。在React中,快照主要包括三類數據:
對于同一個組件,根據公式UI = fn(snapshot),相同的快照輸入應該獲得相同輸出(JSX)。
但狀態更新也可能觸發「副作用」,比如請求數據、操作DOM...
在類組件中,這些「副作用」邏輯被分散在各個生命周期鉤子函數中,React無法掌控。
而在函數組件中:
function UserList({id}) { // 異步請求數據 const data = use(fetchUser(id)); // ...}
使用時:
<Suspense fallback={<div>加載中...</div>}> <UserList id={1}/></Suspense>
總而言之,使用函數組件時,所有副作用都處于一種「受到管控」的狀態,可以盡可能保證每次更新時「相同的快照輸入,獲得相同的JSX輸出」,所以函數組件在React中才會發揚光大。
同時,這也契合了FP中的純函數思想。
「函數組件」并不是「函數式編程」在React中的具體實現,而是React的設計理念UI = fn(snapshot)落地的最好載體。
在React中,還吸收了其他編程范式中的優秀思想。FP只是其中影響React最深的一種。畢竟,一切落地都是為了踐行最初的設計理念。
本文鏈接:http://www.www897cc.com/showinfo-26-32010-0.html函數組件和函數式編程有關系么?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Go 內存分配:結構體中的優化技巧
下一篇: 揭秘系列:Goroutine調度器