Recharts 是 2016 年初團隊可視化組推出的一款可視化組件庫,爲基礎表格的繪製提供了另一種可能。html
Recharts 含義是從新定義(Redefined)圖表。這個名字的背後在於這個圖表在設計上帶給開發者的是不同的體驗,不只是用 React 設計,也在於從新定義了組合與配置方式。git
Recharts 到今天的版本是 0.9.3,支持 React 0.14.x 或 15.0.x 版本,如今有至少四個國外團隊在產品中使用。爲方便國際化,文檔只有英文官網 Recharts,中文官網還在編寫中。若是試用有問題,歡迎給項目提 issue(P.S. 請使用英文來提,謝謝)。github
接下來咱們會從思想層面來剖析 Recharts 的原理和精髓。web
你們能夠回顧一下在作圖表類的需求時,碰到最糾結的問題是什麼?這裏列了一些我碰到最多的問題:數組
配置很是複雜,可配置的內容太多,找不到到底使用什麼配置項來達到想要的目的echarts
不少樣式沒法徹底統一,變化不少。這個線圖怎麼多了條線?這個柱圖的「柱子」怎麼是個三角形?函數
那 Recharts 是怎麼解決這些問題呢?工具
聲明式的標籤,讓寫圖表和寫 HTML 同樣簡單測試
貼近原生 SVG 的配置項,讓配置項更加天然動畫
接口式的 API,解決各類個性化的需求
下面咱們將仔細分析這些是怎麼實現的。
在看代碼實現以前,咱們先看看怎樣一步步的根據各自的需求建立一個線圖:
首先,經過調用 LineChart
添加一條 dataKey
爲 pv 的 Line
:
const data = [{ name: 'a', uv: 4000, pv: 2400 }, { name: 'b', uv: 3000, pv: 1398 }, ....]; <LineChart width={600} height={300} data={data}> <Line dataKey="pv" stroke="black" /> </LineChart>
運行代碼後結果以下:
而後,咱們能夠根據本身的需求去豐富這個線圖,好比這個線圖須要一個 X 軸和 Y 軸,那隻須要在 LineChart
下添加一個 XAxis
和 YAxis
標籤便可:
const data = [{ name: 'a', uv: 4000, pv: 2400 }, { name: 'b', uv: 3000, pv: 1398 }, ....]; <LineChart width={600} height={300} data={data}> <XAxis /> <YAxis /> <Line dataKey="pv" stroke="black" /> </LineChart>
運行代碼結果以下:
你們看到用 Recharts 繪製圖表不少時候就想拼積木同樣,那 LineChart
內部是如何去識別這些『零件』的呢?咱們先來看一個簡單的函數:
const getDisplayName = (Comp) => { if (!Comp) { return ''; } if (typeof Comp === 'string') { return Comp; } return Comp.displayName || Comp.name || 'Component'; };
這個方法很簡單,能夠用來讀取某個 ReactComponent 的名稱。在 LineChart
的代碼實現中,就是根據 ReactComponent 的 displayName
來識別全部的 Children。咱們先來看一個工具方法:
const findAllByType = (children, type) => { const result = []; let types = []; if (_.isArray(type)) { types = type.map(t => getDisplayName(t)); } else { types = [getDisplayName(type)]; } React.Children.forEach(children, child => { const childType = child && child.type && (child.type.displayName || child.type.name); if (types.indexOf(childType) !== -1) { result.push(child); } }); return result; };
這裏 type 能夠是 ReactComponent 或者 ReactComponent 數組。而 LineChart 內部實現的時候就是調用這個方法來識別各個『零件』:
... render() { const { children } = this.props; const lineItems = findAllByType(children, Line); ... }
圖表的配置項能夠很是多,可是有不少配置項如填充顏色、描邊顏色、描邊寬度等等這些都是SVG標籤原生就支持的屬性,爲了減少你們的配置的成本,Recharts 的組件會去解析原生的屬性。舉個例子,一個線圖裏面有兩條曲線,我想給一條曲線設置成虛線,一條設置成實線,咱們只須要像原生的 SVG 元素同樣設置 stroke-dasharray
屬性就行:
const data = [{ name: 'a', uv: 4000, pv: 2400 }, { name: 'b', uv: 3000, pv: 1398 }, ....]; <LineChart width={600} height={300} data={data}> <XAxis /> <YAxis /> <Line dataKey="pv" stroke="black" strokeDasharray="5 5" /> <Line dataKey="uv" stroke="black" /> </LineChart>
結果以下:
實現原理也比較簡單,首先 Recharts 內部維護一份 SVG 元素支持的全部屬性,而後在渲染 SVG 元素以前,咱們會去解析相應的ReactElement的 props
,看看哪些是 SVG 元素可以支持的屬性,最終這些屬性能夠傳入到渲染的 SVG 元素中。
const PRESENTATION_ATTRIBUTES = { fill: PropTypes.string, strokeDasharray: PropTypes.string, ... }; const getPresentationAttributes = (el) => { if (!el || _.isFunction(el)) { return null; } const props = React.isValidElement(el) ? el.props : el; let result = null; for (const key in props) { if (props.hasOwnProperty(key) && PRESENTATION_ATTRIBUTES[key]) { if (!result) {result = {};} result[key] = props[key]; } } return result; };
關於更多SVG屬性,你們能夠參考W3C標準文檔
不少時基礎圖表每每不能知足全部的要求,那怎麼去知足各類個性化的需求成了圖表組件必需要考慮的事情。
Recharts 對可能會變化的元素都提供了自定義的接口,以x軸的刻度爲例,普通的刻度就是一些文字,在信息圖表中,爲了讓圖表更佳的生動,視覺每每但願可以將文字替換成形象的 icon。
對於這種自定義的需求,Recharts 提供了兩種方式,第一種是經過 React Element 的方式:
const CustomizedTick = (props) => { const { x, y, payload, bgColor, index } = props; return ( <g> <circle cx={x} cy={y + 15} r={10} fill={bgColor}/> <text x={x} y={y + 22} textAnchor="middle" fill="#fff">{index}</text> </g> ); }; <LineChart data={data}> <XAxis tick={<CustomizedTick />}/> <YAxis/> <Line dataKey="pv" stroke="black" strokeDasharray="5 5"/> <Line dataKey="uv" stroke="black"/> </LineChart>
經過將 tick 設置成一個 React Element,在拿到內部 props 的同時,也能夠很是方便的從外部傳入 props
。
第二種自定義的方式是經過 function:
const renderCustomizedTick = (props) => { const { x, y, payload, index } = props; return ( <g> <circle cx={x} cy={y + 15} r={10} fill="#666"/> <text x={x} y={y + 22} textAnchor="middle" fill="#fff">{index}</text> </g> ); }; <LineChart data={data}> <XAxis tick={renderCustomizedTick} /> <YAxis/> <Line dataKey="pv" stroke="black" strokeDasharray="5 5"/> <Line dataKey="uv" stroke="black"/> </LineChart>
這種方法,renderCustomizedTick
中拿到的參數和 CustomizedTick
的 props
是同樣的,固然這種自定義的方法外部傳參數會稍微麻煩一些。
看到這裏你們可能會好奇內部是怎麼去實現?原理也很是簡單,咱們在內部計算好 tick 的位置等信息,而後判讀 tick 參數的類型,實現代碼簡化以下:
let tickItem; if (React.isValidElement(tick)) { tickItem = React.cloneElement(tick, props); } else if (_.isFunction(tick)) { tickItem = tick(props); } else { tickItem = <text {...props} className="recharts-cartesian-axis-tick-value">{value}</text>; }
看到這裏你們能夠發現 Recharts 內部主要作了計算各類 layout 的事,每一個區塊具體展現什麼內容都是能夠自定義的。
到這裏咱們已經介紹了 Recharts 實現可視化組件的一些核心思想,其實這些思想不僅是在可視化組件中能夠應用,不少組件也能夠考慮利用這種思想來實現,例如表格組件就能夠抽取 Table
和 Column
兩個組件,而後你們使用表格也很是簡單:
<Table data={data}> <Column name="名稱" dataKey="name"/> <Column name="數量" dataKey="count" align="right" th={<SortableTh order="asc" onChange={handleSort}/>}/> <Column name="金額" dataKey="amt" td="float" align="right"/> </Table>
咱們大約會在本月末,或下月初發布,
更好的動畫支持
同步文檔更新
增長一些圖表的支持
90% 的測試覆蓋率
關於無線支持可能會放到 1.0 以後再考慮,由於 SVG 對手機的兼容性支持度通常。1.0 版本以後,會切分出 React 15.x 的 Recharts。由於 15.x 對 SVG 的支持更加完善。
儘管 web 端已經有很多優秀的可視化庫,亦或是圖表庫,好比 Echarts,highcharts,科學界有 ggplot,他們都是可視化界的前輩。在可視化的探索上,給咱們不少啓發。咱們造 Recharts 的初忠是給 React 社區貢獻一個代碼更優雅,靈活可裝卸的圖表庫的圖表庫。
感謝團隊可視化組的小夥伴。最後是安利時間,第一款使用 Recharts 的線上項目 阿里指數。