前言:前一篇記錄了【后台管理系統】目前進展開發中遇到的一些應用點,這一篇會梳理一些自己學習Ant Design Pro源碼的功能點。附:Ant Design Pro 在線預覽地址。
Dashboard指示板
pages/Dashboard 目錄下:Analysis.js分析頁、Monitor.js指控頁、WorkPlace.js工作台
用到的一些技術點:react v16.6 動態 import,React.lazy()、Suspense、Error boundaries (來源:Postbird博客)
Ⅰ、動態 import
在 Code-Splitting 部分,提出拆分組件的最佳方式(best way) 是使用動態的 import 方式。
比如下面兩種使用方式的對比:
之前:
import { add } from './math'; console.log(add(16, 26));
之后:
import("./math").then(math => { console.log(math.add(16, 26)); });
可以發現動態 import 提供了 Promise 規范的 API,比如 .then()
,關於 ES 動態 import,可以查看下面鏈接:
同樣,下面這篇文章上也可以參考:
目前動態 import 仍舊是 ECMAScript 的提案,並沒有納入規范,不過既然 react 能夠大力的推進,應該下個標准會被寫入。可以查看 TC39-https://github.com/tc39/proposal-dynamic-import
動態 import 主要用於延遲請求,對於組件我覺得沒什么太大的用處,但是對於延遲加載方法或者bundle非常有用,比如下面的代碼:
可以發現,當觸發點擊事件的時候,才會去引入需要的方法或者是對象,並且由於 Promise API 的特性,可以使用 Promise.all
Promise.race
這種 API,進行並行加載,然后在 then() 回調中調用方法,非常方便
class App extends Component { clickHandle = (e) => { Promise.all([ import('./mods/Lazy2') ]).then(([a]) => { console.log(a); a(e); }); } render() { return ( <div className="App"> <header className="App-header"> <button onClick={this.clickHandle}>click</button> </header> </div> ); } }
webpack 已經支持動態 import,在 create-react-app 和 next.js 中都已經可以使用。如果是自己的 webpack 腳手架,需要在 webpack 中進行配置,具體的可以參考下面的方式https://webpack.js.org/guides/code-splitting/,最終配置完的樣式類似於:https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269(這個鏈接是react文檔給出的額,但是我的網絡無法訪問)
同樣的,如果使用 babel,需要使用babel-plugin-syntax-dynamic-import這個組件來保證Babel不對動態導入進行轉換。
Ⅱ、React.lazy() 和 Suspense
1、React.lazy()
動態 import 主要應用場景是延遲加載方法,對於組件來說,並不是很適用,但是 React.lazy
對於組件的加載則是有比較大的幫助。
- 目前明確指出,React.lazy 和 suspense 並不適用於服務端渲染
先看一下前后的區別,
之前代碼:
import OtherComponent from './OtherComponent'; function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
之后:
const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
關鍵點是 const OtherComponent = React.lazy(() => import('./OtherComponent'));
這個語句,摒棄了之前的 import X from 'x'
的靜態引入方式。
同樣的,這個變動會使得在組件渲染的時候,再去加載包含引入路徑的組件。
React.lazy(()=>{})
這個方法的回調中其實就是包含了一個動態 import, 以下面方式舉例:
const Lazy2 = React.lazy(() =>import('./mods/Lazy2').then((e) => { console.log(e); }));
箭頭句柄后面就是一個動態 import 的使用, 打印出來的 e,也就是引入的內容,而前面引入的是個組件,因此會打印出如下信息:
2、Suspense
要使用 Suspense
,需要從 react 中 import:
import React, { Component, Suspense } from 'react';
既然是延遲加載,就會有一個加載過程,之前在渲染的時候,我們基本都是自頂一個一個 <Loading>
組件,然后通過變量控制進行操作,如果加載完成,則取消掉 <Loading>
組件。
如果直接使用 React.lazy
,會報錯誤:需要一個 placeholder ui
既然是延遲加載,就一定會有一個loading的過程,而 Suspense
正是完成這個過程。
如同上面的效果會有一個動態的過程,代碼如下:
render() { return ( <div className="App"> <header className="App-header"> <Suspense fallback={<div>Loading...</div>}> {this.renderList()} </Suspense> </header> </div> ); }
Suspense
使用的時候,fallback
一定是存在且有內容的, 否則會報錯。
針對網絡請求的 loading,我並沒覺的這種 fallback 有什么幫助,因為他是組件加載的 loading,如果組件加載完了,那么再去 fallback 就沒意義,也沒效果了。
Ⅲ、Error boundaries
上面 Suspense
是對 loading 的一個打底,而錯誤邊界可以在任何一個組件中進行錯誤的捕獲。
這里只對錯誤邊界進行一個簡要的使用,具體的文檔見下:
這里的錯誤邊界用在這個位置是為了當組件懶加載失敗的時候,進行錯誤的捕獲和保護:
1、創建錯誤邊界組件
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } // 從error中接收錯誤並設置 state static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, info) { // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } }
2、在組件中使用錯誤邊界
將創建的錯誤邊界掛到組件中,不適用 React.lazy
,因為已經沒有更上一層次的錯誤組件了,萬一錯誤邊界組件也懶加載出錯,會導致無法捕獲。
錯誤邊界組件中,通過 componentDidCatch
捕獲錯誤,可以設置信息或發錯誤日志。
錯誤邊界的使用示例可以參考下面的示例:
按照上面的例子,在使用的時候,組件中報錯,觸發錯誤邊界:
Lazy2.jsx:
render() { throw new Error('I crashed!'); return ( <div> <p>{this.state.title}</p> </div> ); }
App.jsx:
<Error> {this.renderList()} </Error>
Error.jsx:
import React, {Component} from 'react'; class Error extends Component { constructor(props) { super(props); this.state = { error: null, errorInfo: null }; } componentDidCatch(error, errorInfo) { // Catch errors in any components below and re-render with error message this.setState({ error: error, errorInfo: errorInfo }) // You can also log error messages to an error reporting service here } render() { if (this.state.errorInfo) { // Error path return ( <div> <h2>Something went wrong.</h2> <details style={{ whiteSpace: 'pre-wrap' }}> {this.state.error && this.state.error.toString()} <br /> {this.state.errorInfo.componentStack} </details> </div> ); } // Normally, just render children return this.props.children; } } export default Error;
最終結果:
Dashboard模塊除了上面的新技術點,在AntD組件和布局上也有一些常用的知識點:GridContent網格內容布局、Charts圖表、Tabs標簽頁
Ⅰ、GridContent網格內容布局
使用GridContent組件,需要從components/PageHeaderWrapper目錄下引用,源碼如下↓
import React, { PureComponent } from 'react'; import { connect } from 'dva'; import styles from './GridContent.less'; class GridContent extends PureComponent { render() { const { contentWidth, children } = this.props; let className = `${styles.main}`; if (contentWidth === 'Fixed') { className = `${styles.main} ${styles.wide}`; } return <div className={className}>{children}</div>; } } export default connect(({ setting }) => ({ contentWidth: setting.contentWidth, }))(GridContent);
1、組件繼承自PureComponent
PureComponent 純組件是React新加的一個類,是優化 React
應用程序最重要的方法之一,易於實施。
只要把繼承類從 Component
換成 PureComponent
即可,可以減少不必要的 render
操作的次數,從而提高性能,而且可以少寫 shouldComponentUpdate
函數,節省了點代碼。
具體使用及原理可參看下面的鏈接:
2、設置多個PageHeader網格式分布樣式
.main { width: 100%; height: 100%; min-height: 100%; transition: 0.3s; &.wide { max-width: 1200px; margin: 0 auto; } }
Ⅱ、Charts圖表
利用 Ant Design Pro 提供的圖表套件,可以靈活組合符合設計規范的圖表來滿足復雜的業務需求。
- 官網Charts組件介紹:https://pro.ant.design/components/Charts-cn/
![]() |
![]() |
迷你區域圖MiniArea | 迷你進度條MiniProgress |
![]() |
![]() |
迷你柱狀圖MiniBar | 柱狀圖Bar |
![]() |
![]() |
餅狀圖Pie, yuan | 迷你餅狀圖Pie |