【温故知新】梳理React中HOC的点点滴滴
前言
由于不可避免的遺忘曲線和最初潦草學(xué)習(xí)等諸多因素,我獲得的某些技術(shù)點會存在沒有吃透或者出現(xiàn)了被遺忘的情況。幸運的是,我有經(jīng)驗積累這個增益buff,能夠不時的有新的開發(fā)思路誕生。
此消彼長,也不是長久之計。于是我想通過溫故知新的方式,重拾部分技術(shù)知識或者提煉一些有趣的“奇思妙想”。
本篇主要重新梳理一下React的HOC。
溫故而知新,可以為師矣。
帶著問題去尋找答案
如果我還是單純的看一遍文檔,到最后估計收獲不大。所以,我在開始前,列了一些自己的疑問以及帶著這些疑問要找到答案。
- 為什么提起HOC總會看到Mixins的身影?
- 橫切關(guān)注點到底是什么?為什么文檔里面提到HOC解決了橫切關(guān)注點問題?
- React為什么用HOC替代Mixins?
- 為什么我平時用不上HOC?
- 我怎樣才能用的上HOC?
…
HOC和它的兄弟Mixins
先來聊一下HOC和Mixins這對兄弟的前情提要。
為了解決橫切關(guān)注點問題,早先React也是采用Mixins方式,但是隨著組件的增加,Mixins方式帶來了一系列的問題,比如隱式依賴、名稱沖突、代碼復(fù)雜性增加等。
所以React采用HOC的設(shè)計模式替代Mixins解決橫切關(guān)注問題。
名詞解釋時間
橫切關(guān)注點
React文檔里面提到
如果完全不同的組件有相似的功能,這就會產(chǎn)生“橫切關(guān)注點(cross-cutting concerns)“問題。
知乎文章 《面向?qū)ο罄Ь持?—— 橫切關(guān)注點》 ,介紹了關(guān)注點和橫切關(guān)注點,并舉了日志功能的例子,寫的非常好,我把前面的介紹貼出來,幫助大家更好理解。
什么是關(guān)注點(Concern)?
A Concern is a term that refers to a part of the system divided on the basis of the functionality.
關(guān)注點是指基于功能劃分系統(tǒng)的一部分。
什么是橫切關(guān)注點(Crosscutting Concern)?
部分關(guān)注點「橫切」程序代碼中的數(shù)個模塊,即在多個模塊中都有出現(xiàn),它們即被稱作「橫切關(guān)注點(Cross-cutting concerns, Horizontal concerns)」。
這樣說好像還是特別抽象?那我們舉個例子。
日志功能就是橫切關(guān)注點的一個典型案例。日志功能往往橫跨系統(tǒng)中的每個業(yè)務(wù)模塊,即“橫切”所有需要日志功能的類和方法體。所以我們說日志成為了橫切整個系統(tǒng)對象結(jié)構(gòu)的關(guān)注點 —— 也就叫做橫切關(guān)注點啦。
解決橫切關(guān)注點不是提煉某個公共方法那么簡單,公共方法具有功能的完整性,如果想要適配所有的業(yè)務(wù)模塊,隨著業(yè)務(wù)的迭代,公共方法中的代碼會越來越復(fù)雜。所以Vue框架中采用Mixins功能,React使用HOC替換Mixins來解決橫切關(guān)注點問題。
Mixins
Vue官網(wǎng)中對 mixins介 紹如下
混入 (mixin) 提供了一種非常靈活的方式,來分發(fā) Vue 組件中的可復(fù)用功能。一個混入對象可以包含任意組件選項。當(dāng)組件使用混入對象時,所有混入對象的選項將被“混合”進(jìn)入該組件本身的選項。
官網(wǎng)例子
// 定義一個混入對象 var myMixin = {created: function () {this.hello()},methods: {hello: function () {console.log('hello from mixin!')}} }// 定義一個使用混入對象的組件 var Component = Vue.extend({mixins: [myMixin] })var component = new Component() // => "hello from mixin!"上面的例子通過mixins方式將對象myMixin的方法添加到Component組件上去。
HOC
React官網(wǎng)對HOC介紹如下
高階組件(HOC)是 React 中用于復(fù)用組件邏輯的一種高級技巧。HOC 自身不是 React API 的一部分,它是一種基于 React 的組合特性而形成的設(shè)計模式。
具體而言,高階組件是參數(shù)為組件,返回值為新組件的函數(shù)。
從這兩段話中,我們不難提煉出復(fù)用組件、高級技巧、設(shè)計模式、參數(shù)為組件、返回為新組件的函數(shù)等關(guān)鍵詞,發(fā)散一下思維,HOC允許開發(fā)者在定義一個特定的地方定義某個邏輯,并在許多組件之間共享它,進(jìn)而解決橫切關(guān)注點的問題。
Mixins和HOC,為什么React選擇了后者?
Mixins的誕生很好的幫助解決了功能復(fù)用的問題,但是隨著時間的推移,Mixins也逐漸暴露出它的短板。
Mixins的短板
React對于Mixins帶來的麻煩,有一篇文章(文章地址)進(jìn)行了詳細(xì)的介紹,簡單總結(jié)為一下幾點。
引入隱式依賴
JavaScript 是一種動態(tài)語言,因此很難強制執(zhí)行或記錄這些依賴關(guān)系。一般組件可以引入多個mixin,所以在使用某個mixin中的方法時,其實并不能夠區(qū)分是使用的哪個mixin的方法,一旦有人移除了某個被引用的方法,而沒有將所有依賴該方法的地方修改成正確的依賴,原本正常的功能就會發(fā)生錯誤。
這些隱含的依賴關(guān)系會給開發(fā)增加難度。
導(dǎo)致名稱沖突
不能保證兩個特定的 mixin 可以一起使用。例如,如果 FluxListenerMixin 定義了 handleChange(),而 WindowSizeMixin 定義了 handleChange(),則不能一起使用。也不能在自己的組件上定義具有此名稱的方法。
另外,如果與第三方包中的 mixin 有名稱沖突,不能只重命名它的方法。相反,必須在組件上使用笨拙的方法名稱以避免沖突。
導(dǎo)致復(fù)雜性滾雪球
即使 mixin 一開始很簡單,但隨著時間的推移,它們往往會變得復(fù)雜。
一個mixin可以被多個組件引入,導(dǎo)致使用相同 mixin 的組件變得越來越耦合。任何新功能都會被添加到使用該 mixin 的所有組件中。如果不復(fù)制代碼或在 mixin 之間引入更多的依賴關(guān)系和間接性,就無法拆分 mixin 的“更簡單”部分,也就導(dǎo)致mixin的復(fù)雜性越來越高。
HOC是怎么做的?
為了解決功能復(fù)用問題,同時為了規(guī)避Mixins產(chǎn)生的問題,HOC做了以下設(shè)計。
透傳props
將與HOC自身無關(guān)的props透傳,提升使用的靈活性,降低組件間的耦合性。
組合HOC
將HOC與容器組件組合,不直接修改傳入組件的HOC,避免引入隱式依賴。
最大化可組合性
HOC可以接收多個參數(shù),這樣可以降低單一參數(shù)帶了的功能復(fù)雜性不斷增加的問題。
HOC的實際應(yīng)用
名將的歸宿是戰(zhàn)場,技術(shù)的歸宿是實踐。
通過探索HOC的實際應(yīng)用,也能能幫助解決“為什么我平時用不上HOC?”和“我怎樣才能用的上HOC?”兩個疑問。
我先想明白了為什么我平時用不上HOC
因為我沒有跳出思維定式的圈子,在早期的業(yè)務(wù)開發(fā)中,更多的是封裝完整的功能組件,對于高階組件沒有理解的很透徹,一方面是不知道用在哪,另一方面是使用了可能也沒有想到這種設(shè)計模式就是HOC。
所以,我后來就開始注意,在實際開發(fā)中,哪些能用HOC,以及使用之后,是否開發(fā)效率和代碼可維護(hù)性變的更高。
哪些功能可以讓你用上HOC
一通百通,一悟千悟。
我將項目中用到HOC的功能進(jìn)行了整理歸納,大致有以下應(yīng)用場景。
注:是項目中用到的,并不全是我一個人開發(fā)的,有很多是我同事的智慧。
按鈕權(quán)限控制
按鈕權(quán)限控制幾乎是后臺管理系統(tǒng)必備的功能。
功能分析
用一句話概括一個基礎(chǔ)的按鈕權(quán)限控制功能,即不同角色用戶可進(jìn)行的頁面操作可通過數(shù)據(jù)配置進(jìn)行控制。
功能實現(xiàn)
新增了PowerButton組件
- 因為每個頁面的每個按鈕都需要加權(quán)限控制,所以使用組合的方式,將權(quán)限組件包裹在按鈕容器外側(cè)達(dá)到控制的目的;
- 緩存權(quán)限控制按鈕數(shù)據(jù),方便統(tǒng)一管理,該數(shù)據(jù)為頁面pathname對應(yīng)權(quán)限按鈕數(shù)組的枚舉;
- 獲取權(quán)限控制按鈕數(shù)據(jù),獲取緩存中powerButtonData的值和當(dāng)前頁面的pathname,進(jìn)而獲取當(dāng)前頁面的權(quán)限按鈕數(shù)組;
- 獲取當(dāng)前按鈕的code值在權(quán)限按鈕數(shù)組中的索引。如果索引值大于-1表示當(dāng)前按鈕存在,頁面也會展示按鈕;反之則表示按鈕不存在,頁面不會展示該按鈕;
頁面使用
我們目前使用的中文字符做為按鈕的唯一值,這樣方便業(yè)務(wù)方在后臺配置每個頁面的按鈕權(quán)限。如果有英文字符容易配錯。
import { PowerButton } from '@/components';<PowerButton code='新增'><Button type='primary'>新增</Button> </PowerButton>頁面鑒權(quán)
ToC的業(yè)務(wù),總少不了對于用戶登錄的處理功能,主要在于某些頁面信息必須等拿到用戶登錄的信息之后才能正常的展示,比如用戶的訂單信息、收獲地址,但是一些頁面不需要用戶登錄也可以正常在用戶的設(shè)備上展示,比如首頁、商品詳情頁等。這個時候就需要對頁面權(quán)限做處理頁就是我們常說的頁面鑒權(quán)功能。
功能分析
- 用戶在進(jìn)入不需要登錄鑒權(quán)的頁面時,會直接打開頁面,設(shè)備上展示完整的頁面內(nèi)容;
- 用戶在進(jìn)入需要登錄鑒權(quán)的頁面時,前端會進(jìn)行路由攔截(整個處理過程用戶無感知),內(nèi)部處理判斷登錄的邏輯,如果已登錄直接進(jìn)入頁面,如果未登錄進(jìn)入登錄頁,登錄成功之后再進(jìn)入頁面;
功能實現(xiàn)
新增Authority組件
/*** @description 頁面鑒權(quán)*/ import React, { Component } from 'react'; import { Route, Redirect } from 'react-router-dom';class Authority extends Component {constructor(props) {super(props);}/*** 重定向方法* @param {string} path 需要重定向的地址* @return {void} 無*/redirect(path) {let { pathname, search } = this.props.location;// 重定向指定pathreturn <Redirect to={path} />;}render() {const { path, component } = this.props;const { pathname, search } = this.props.location;let query = decodeQuery(search);// =>true: 如果沒有用戶信息,跳轉(zhuǎn)登錄頁if (!sessionStorage.member) return this.redirect('/login');return <Route path={path} component={component} />;} }export default Authority;頁面使用
- 不需要鑒權(quán)的頁面模塊,會直接進(jìn)行路由跳轉(zhuǎn);
- 需要鑒權(quán)的頁面模塊,使用Authority組件進(jìn)行鑒權(quán)處理;
總結(jié)
上面HOC的實際應(yīng)用,是否覺得功能挺眼熟的,空閑的時候,不妨捋捋自己項目的代碼,沒準(zhǔn)會有不錯的收獲。
文章寫的很基礎(chǔ),是因為作者本人還處于不斷學(xué)習(xí)的階段。不過溫故確實能夠知新。
雖然作者本人的功力還沒有足夠深厚,但是正在通過溫習(xí)學(xué)習(xí)收獲成長。希望未來奔跑著,能追上一些大佬的背影。
總結(jié)
以上是生活随笔為你收集整理的【温故知新】梳理React中HOC的点点滴滴的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea项目总是自动重启_IDEA 下
- 下一篇: 举头望明月打计算机术语,呐,你们要的灯谜