摘要:本文是造輪系列第三篇。造輪子系列組件思路造輪系列對話框組件思路想閱讀更多優質文章請猛戳博客一年百來篇優質文章等著你初始化參考組件分別分為五個組件。參考方應杭老師的造輪子課程交流干貨系列文章匯總如下,覺得不錯點個,歡迎加群互相學習。
本文是React造輪系列第三篇。
1.React 造輪子系列:Icon 組件思路
2.React造輪系列:對話框組件 - Dialog 思路
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
初始化 Layout參考 And Design ,Layout 組件分別分為 Layout, Header, Aside, Content,Footer 五個組件。基本使用結構如下:
header content
假如我們想直接在 Layout 組件添加 style 和 className 如:
// 同上
這樣寫并不支持,我們需要在組件內聲明它:
// lib/layout/layout.tsx interface Props { style: CSSProperties, className: string } const Layout: React.FunctionComponent= (props) => { return ( {props.children}) }
注意這個 style 是一個 CSSProperties,如果不知道 style 是什么類型的,這邊有間技巧就是在正常 div 上寫 style,然后通過 IDE 功能跳轉到定義代碼塊,就能知道類型了。
上面寫法看上去沒問題,但如果我還想支持 id 或者 src 等 html 原生的屬性呢,是不是要一個一個的寫呢,當然不是,因為接口是可以繼承的,我們直接繼承 MapHTMLAttributes 即可:
interface Props extends React.MapHTMLAttributes{ }
接下就是使用傳入的 style, className:
const Layout: React.FunctionComponent= (props) => { const {className, ...rest} = props return ( {props.children}) }
這里的 sc 是做第一個輪子的時候封裝,對應的方法如下:
function scopedClassMaker(prefix: string) { return function x(name?: string) { const result = [prefix, name].filter(Boolean).join("-"); return [result, options && options.extra].filter(Boolean).join(" ") }; } export {scopedClassMaker};
從上述的實現方式,可以發現問題,如果我們直接在組件內寫 className={sc(""), className}, 我們通過 sc 方法生成的函數會被傳入的 className 覆蓋。所以需要就 sc 方法進一步驟改造,擴展傳入 className,實現方式如下:
interface Options { extra: string | undefined } function scopedClassMaker(prefix: string) { return function x(name?: string, options ?:Options ) { const result = [prefix, name].filter(Boolean).join("-"); if (options && options.extra) { return [result, options && options.extra].filter(Boolean).join(" ") } else { return result; } }; } export {scopedClassMaker};
如果懂 Es6 閱讀以下代碼應該很容易,這里就一在詳細講了。
然后調用方式如下:
// lib/layout/layout.tsx ... const Layout: React.FunctionComponent= (props) => { const {className, ...rest} = props return ( {props.children}) } ...
在回顧一下,開始的結構:
//lib/layout/layout.example.tsxheader content
再次運行:
這里有個問題,實際我們想要的效果是 Content 內容是要撐開的,所以我們需要使用 flex 來布局,自動填寫使用的 flex-grow 屬性:
// lib/layout/layout.scss .gu-layout { border: 1px solid red; display: flex; flex-direction: column; &-content { flex-grow: 1; } }
運行效果:
那如果 Layout 里面還有 Layout 呢,如下:
第二個例子
header content
運行效果:
如果嵌套 Layout,content 還是沒有撐開。說明如果 Layout 里面還有 Layout,那里面的 Layout 應該占滿全部。
.gu-layout { // 同上 & & { flex-grow: 1; border: 1px solid blue; } }
這里說明一下 & &, & 表示當前的類名,所以就是 & 就是 .gu-layout。
運行效果:
這樣有個問題, 如果 Layout 里面有 Layout,這個里面的一般是左右布局,所以需要設置水平方向為 row
& & { flex-grow: 1; border: 1px solid blue; flex-direction: row; }
運行效果:
如果想讓 Aside 換到右邊,只需要調整位置即可。
第三個例子
header content
運行效果:
在來看別外一種布局:
第四個例子
header content
運行效果:
可以看到 我們希望當有 Aside 組件時,需要的是左右布局,當前的樣式無法滿足,需要再次調整,參考 AntD 設計,當有里面有 Aside 組件, Layout 就多了一個左右布局的樣式的 className,所以我們要在 Layout 組件檢測 children 類型。
實現思路是,可以先在 Layout 組件內打印 children :
所以我可以通過遍歷 children 來判斷,實現如下:
props.children.map(node => { console.log(node) })
這邊不能直接使用 map,因為 children 的類型有5種, ReactChild, ReactFragment ,ReactPortal,boolean, null, undefined,所以這里需要對 children 進行約束,至少要有一個元素。
// lib/layout/layout.tsx interface Props extends React.MapHTMLAttributes{ children: ReactElement | Array } const Layout: React.FunctionComponent = (props) => { const {className, ...rest} = props let hasAside = false if ((props.children as Array ).length) { (props.children as Array ).map(node => { if (node.type === Aside) { hasAside = true } }) } return ( {props.children}) } export default Layout
添加對應的 css:
.gu-layout { ... &.hasAside { flex-direction: row; .gu-layout{ flex-direction: column } } ... }
運行效果:
上述寫法,有些問題,這一個就是使用到了 let 聲明,這們就不符合我們函數式編程了,第二個 sc 方法還需要進一步改善。
刪除代碼里的 let在上述代碼中,我們使用了一個 let hasAside = false,來判斷 Layout 里面是否有 Aside,這樣寫就不符合我們函數式的定義了。
其實我們做的是通過遍歷,然后一個一個判斷是否有 Aside ,如果有剛設置為 true, 從上圖可以看出,我們最后可以把所有判斷結果 或(|)起來,如果為 true ,則有,否則無。這時候我們就可以使用 es6 新引入的 reduce 方法了。
// lib/layout/layout.tsx ... const Layout: React.FunctionComponent= (props) => { const {className, ...rest} = props if ((props.children as Array ).length) { const hasAside = (props.children as Array ) .reduce((result, node) => result || node.type === Aside, false) } return ( {props.children}) } ...
通過 reduce 改進后的方法有個問題,我們 hasAside 是在 if 塊域里面的,外部訪問不到,那有沒有什么辦法刪除 {} 塊作用域呢?
我們把把 if 條件通過 && 放到跟遍歷同一級:
// lib/layout/layout.tsx ... const children = props.children as Array總結const hasAside = ( children.length) && children.reduce((result, node) => result || node.type === Aside, false) ...
Layout 組件相對簡單,這邊主要介紹一些實現思路,源碼已經到這里。
參考
《方應杭老師的React造輪子課程》
交流干貨系列文章匯總如下,覺得不錯點個Star,歡迎 加群 互相學習。
https://github.com/qq44924588...
我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的干貨,在進階的路上,共勉!
關注公眾號,后臺回復福利,即可看到福利,你懂的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104315.html
摘要:本文是造輪系列第二篇。實現方式事件處理跟差不多,唯一多了一步就是當點擊或者的時候,如果外部有回調就需要調用對應的回調函數。 本文是React造輪系列第二篇。 1.React 造輪子系列:Icon 組件思路 本輪子是通過 React + TypeScript + Webpack 搭建的,至于環境的搭建這邊就不在細說了,自己動手谷歌吧。當然可以參考我的源碼。 想閱讀更多優質文章請猛戳Git...
摘要:數人云開源一款容器管理工具,開發過程中,為了保證的健壯性和穩定性,數人云開發團隊自制了一套適合測試的小工具。于是一款簡單的腳本請求工具解析工具組成的測試小工具雛形出現了。是一款命令行解析文本的工具,支持非常多的語法解析構造重組文本。 數人云開源一款容器管理工具Crane,Crane開發過程中,為了保證API的健壯性和穩定性, 數人云開發團隊自制了一套適合Crane API測試的小工具。...
摘要:虛擬列表的實現有多種方案,本文以組件為基礎進行分析。常見的無限滾動便是延遲渲染的一種實現,而虛擬列表則是按需渲染的一種實現。接下來,本文會簡單介紹虛擬列表的一種實現方案。實現本章節將會創建一個組件,并結合代碼,慢慢梳理虛擬列表的實現。 在 列表數據的展示優化 一文中,提到了對于列表形態的數據展示的按需渲染。這種方式是指根據容器元素的高度以及列表項元素的高度來顯示長列表數據中的某一個部分...
摘要:前言自從上次在掘金發布年山地人的前端完整自學計劃講一個站主山地人的天前端自學故事以來,一眨眼山地人老哥在站做主已經有天了。所以這個體系里的一些框架包括也是山地人年自學計劃的一部分。月底,山地人老哥開啟了的兩個專題。 前言 自從上次在掘金發布【2019年山地人的前端完整自學計劃——講一個B站UP主山地人的40天前端自學故事】 以來,一眨眼山地人老哥在B站做Up主已經有85天了。 時隔一個...
閱讀 2626·2021-09-28 09:36
閱讀 2239·2021-09-07 09:58
閱讀 1499·2019-08-26 13:53
閱讀 1282·2019-08-23 17:53
閱讀 3034·2019-08-23 15:34
閱讀 1856·2019-08-23 15:34
閱讀 2872·2019-08-23 12:04
閱讀 3723·2019-08-23 10:56