ant-design-pro Authorized許可權組件設計
本文將介紹pro中Authorized許可權組件的實現思路、在系統中的應用以及個人對組件設計的思考和感想,如有不正確的地方,歡迎指正。
首先來看下許可權組件的實現思路:現有許可權(currentAuthorize)和准入許可權(authorize)
做比較,如果一致,則渲染和准入許可權匹配的組件。
思路很簡單,我們先不關心Authorized許可權組件的具體細節,用一段代碼來實現上面描述的功能:
<div> { currentAuthorize === authorize ? <MatchComponent> : <NoMatchComponent> }</div>
先來分析一下上述代碼存在的問題:
系統中的多個模塊都需要進行相應的許可權設置,也就是說上面的代碼需要在多處調用,一方面是代碼重複冗餘,更可怕的是一旦判斷邏輯currentAuthorize === authorize變化,所有模塊的判斷邏輯都需要作出相應的更改。
這樣的實現方式很不react的,react推崇的是聲明式編程(不理解的小夥伴可以看下程墨老師關於聲明式編程的一篇文章從年會看聲明式編程(Declarative Programming),對我啟發很大):react對用戶編寫的組件提供了統一管理的能力,說的具體點就是用戶不需要在每個組件中書寫生命周期實現,真實dom節點對應等代碼,這些和組件相關的功能代碼已經被react統一管理了,用戶使用組件時只需要關心渲染什麼就可以了,根本不需要操心如何渲染。這就是聲明式編程的思想。
- 聲明式編程減少了重複勞動:組件相關的功能代碼在一個地方書寫就可以了,不需要在每個組件中都來實現。
- 聲明式編程提高了代碼的可維護性:react未來可能會優化組件相關的功能代碼,但是react的組件代碼是不需要改變的。
基於此原因,我們用聲明式編程的思想對許可權代碼進行重寫,將許可權的邏輯判斷代碼封裝在react組件中統一維護管理(像是react-route庫中將路由處理的代碼封裝在了route組件中統一維護,這些都是符合聲明式編程思想的):
//許可權組件使用:<Authorized currentAuthorize={admin} authorize={[admin,guest]} noMatch={<div>無權訪問</div>}/> <div>許可權通過後顯示的內容</div></Authorized>
有一點具體說明:用戶在當前系統中會被分配相應的角色信息,在登錄成功後返回。使用Authorized組件時currentAuthorize屬性是不需要重複傳的,引用時傳遞一次即可(消滅重複代碼),實現方案如下:
import Authorized from ./Authorized;//每次引入組件之前調用函數傳入currentAuthorize,//通過組件內部變數來維護currentAuthorize。let CURRENT = NULL;const renderAuthorize = (currentAuthority) => { CURRENT = currentAuthority; return Authorized;};export { CURRENT };export default renderAuthorize;
Authorized組件內部實現:
//Authorized.jsimport React from react;import CheckPermissions from ./CheckPermissions;class Authorized extends React.Component { render() { const { children, authority, noMatch = null } = this.props; const childrenRender = typeof children === undefined ? null : children; //現有許可權和准入許可權比對 //CheckPermissions返回childrenRender或者noMatch return CheckPermissions(authority, childrenRender, noMatch); }}export default Authorized;//CheckPermissions.jsimport React from react;import { CURRENT } from ./index;const checkPermissions = (authority, currentAuthority, target, Exception) => { // 沒有判定許可權.默認查看所有 // Retirement authority, return target; if (!authority) { return target; } // 數組處理 if (Array.isArray(authority)) { if (authority.indexOf(currentAuthority) >= 0) { return target; } return Exception; } // string 處理 if (typeof authority === string) { if (authority === currentAuthority) { return target; } return Exception; } // Function 處理 if (typeof authority === function) { try { const bool = authority(currentAuthority); if (bool) { return target; } return Exception; } catch (error) { throw error; } } throw new Error(unsupported parameters);};export { checkPermissions };const check = (authority, target, Exception) => { return checkPermissions(authority, CURRENT, target, Exception);};export default check;
這樣一個基本的許可權組件就封裝完成了,我們結合業務看一下在系統中的應用:
//route.js//登錄模塊路由<Route path="/user" component={UserLayout}/>//主模塊路由,用戶未登錄(currentAuthorized為guest)重定向到登錄頁。<Authorized authorize={[admin]} noMatch={<Route path="/" render={() => <Redirect to={/user/login} />} />}> <Route path="/" component={BasicLayout} /></Authorized>
主模塊中每個功能模塊都要做一次許可權路由判斷,所以我們根據當前的具體業務擴展一下許可權組件:
//AuthorizedRoute.jsimport React from react;import { Route, Redirect } from react-router-dom;import Authorized from ./Authorized;//路由許可權組件封裝了noMatch和children的傳入值,省去了route和redirect組件的配置。//用戶在使用時只需傳入path及對應的重定向地址。export default ({authority, component: Component, redirectPath, render, ...rest}) => { return ( <Authorized authorize={authority} noMatch={ <Route {...rest} render={() => <Redirect to={redirectPath} />} /> } > <Route {...rest} render={(props) => (Component ? <Component {...props} /> : render(props)) } /> </Authorized> )}//route.js<Route path="/user" component={UserLayout}/>{/* <Authorized authorize={[admin]} noMatch={<Route path="/" render={() => <Redirect to={/user/login} />} />}> <Route path="/" component={BasicLayout} /></Authorized> */}//<AuthorizedRoute path="/" render={props => <BasicLayout {...props} />} authority={[admin, user]} redirectPath="/user/login"/>
最後將擴展組件掛載到組件函數上,完整的index.js文件如下:
import Authorized from ./Authorized;import AuthorizedRoute from ./AuthorizedRoute;let CURRENT = NULL;Authorized.AuthorizedRoute = AuthorizedRoute;const renderAuthorize = (currentAuthority) => { if (currentAuthority) { if (currentAuthority.constructor.name === Function) { CURRENT = currentAuthority(); } if (currentAuthority.constructor.name === String) { CURRENT = currentAuthority; } } else { CURRENT = NULL; } return Authorized;};export { CURRENT };export default renderAuthorize;
以上是本人對pro中Authorized組件設計的一點感想,大家在閱讀源碼時一定不要只停留在表層實現,要深入的思考這樣的設計是為了解決開發中的哪些痛點。
感謝花時間閱讀文章,後續會整理更多關於pro的文章,敬請關注。
個人github:xiaoxiaoaobama (王有聞)
推薦閱讀:
※前端日刊-2018.02.14
※前端技能學習導航
※2017新出爐的前端資源匯總
※React源碼分析 - 生命周期
※前端日刊-2017.12.17