管理頁面我們通常能看到一連串的卡片伶椿,以很明顯的方式對用戶進行提示月褥,也可以是圖表嵌在卡片的形式直觀展示等浊。那就會存在一個需求欲诺,用戶可以自定義自己想要的卡片布局抄谐,其實也就是自定義寬高這樣。
比如說上圖扰法,我想自定義成為下面這樣蛹含,
這樣一個需求我們?nèi)绾稳崿F(xiàn)呢?
查閱了一些文章之后塞颁,我選擇了
React-Grid-Layout
這樣一個組件浦箱,基本放大縮小以及拖拽這些都能實現(xiàn)卧斟,參照官方文檔就ok,我這里主要講一下憎茂,使用之后會出現(xiàn)的一些問題珍语。因為是基于
hzero
的項目,所有的路由是像一個tab
頁一樣并列在頂部竖幔,并且路由之間的跳轉(zhuǎn)不會觸發(fā)組件的unmount
方法板乙,這時候就遇到了一個問題。當(dāng)我們在其他頁面進行瀏覽器的放大和縮小后拳氢,再回到使用React-Grid-Layout
的頁面募逞,就會發(fā)現(xiàn),所有的卡片擠在一起馋评,樣式錯亂了放接,只在使用React-Grid-Layout
的頁面縮放就不會有問題,這樣試了幾次之后留特,很奇怪纠脾,為什么出現(xiàn)這種情況呢,那就只能去看看組件源碼了蜕青,看了會苟蹈,我發(fā)現(xiàn)了問題。
// @flow
import React from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import type { ComponentType as ReactComponentType } from "react";
type WPProps = {
className?: string,
measureBeforeMount: boolean,
style?: Object
};
type WPState = {
width: number
};
/*
* A simple HOC that provides facility for listening to container resizes.
*/
// typescript 類型檢查
export default function WidthProvider<
Props,
ComposedProps: { ...Props, ...WPProps }
>(
ComposedComponent: ReactComponentType<Props>
): ReactComponentType<ComposedProps> {
return class WidthProvider extends React.Component<ComposedProps, WPState> {
static defaultProps = {
measureBeforeMount: false
};
static propTypes = {
// If true, will not render children until mounted. Useful for getting the exact width before
// rendering, to prevent any unsightly resizing.
measureBeforeMount: PropTypes.bool
};
state = {
width: 1280
};
mounted: boolean = false;
componentDidMount() {
this.mounted = true;
window.addEventListener("resize", this.onWindowResize);
// Call to properly set the breakpoint and resize the elements.
// Note that if you're doing a full-width element, this can get a little wonky if a scrollbar
// appears because of the grid. In that case, fire your own resize event, or set `overflow: scroll` on your body.
this.onWindowResize();
}
componentWillUnmount() {
this.mounted = false;
window.removeEventListener("resize", this.onWindowResize);
}
onWindowResize = () => {
if (!this.mounted) return;
// eslint-disable-next-line
const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
if (node instanceof HTMLElement)
this.setState({ width: node.offsetWidth });
};
render() {
const { measureBeforeMount, ...rest } = this.props;
if (measureBeforeMount && !this.mounted) {
return (
<div className={this.props.className} style={this.props.style} />
);
}
return <ComposedComponent {...rest} {...this.state} />;
}
};
}
可以看到這里監(jiān)聽了頁面的resize
右核,然后進行onWindowResize
方法慧脱,我們在其他頁面進行resize
之后,也會觸發(fā)這個方法贺喝,這也是我前面要強調(diào)一下路由切換不會unmount
的原因菱鸥,這里的resize
沒法得到正確的node.offsetWidth
,所以樣式錯亂了躏鱼,那怎么辦呢氮采?
首先我想著,在這個頁面寫一個時鐘挠他,不停的js
去觸發(fā)resize
事件扳抽,讓它在切換頁面的時候重新計算篡帕,這樣肯定是ok的殖侵,但是這樣肯定會有性能問題,因為不停的延時調(diào)用镰烧,這樣我就得想個辦法拢军,不那么頻繁的刷新了,怎么做呢?
路由切換怔鳖,頁面也切換了茉唉,那組件肯定會觸發(fā)receiveProps
鉤子,那我在這里判斷一下當(dāng)前頁面的路由,如果是當(dāng)前路由度陆,那就刷新一下(dispatchEvent('resize'))艾凯,這樣就做到只是進入這個頁面就刷新,比時鐘要好一些懂傀。但是這樣還是很low趾诗,因為進入其他頁面沒有resize
操作,只要返回這個頁面就會刷新蹬蚁,接下來我就要優(yōu)化一下恃泪,只有當(dāng)其他頁面resize
了之后,回到當(dāng)前頁才dispatchEvent('resize')
犀斋。
// 當(dāng)切換到其他頁面并進行了頁面縮放之后重新resize當(dāng)前頁面
resize = debounce(() => {
window.dispatchEvent(new Event('resize'));
this.setState({
loading: false,
ifResize: false,
});
}, 1000);
// eslint-disable-next-line
UNSAFE_componentWillReceiveProps() {
if (window.location.href.indexOf('hipsappsa/pandect') > -1 && this.state.ifResize) {
this.setState({
loading: true,
});
this.resize();
}
}
componentDidMount() {
const callback = this.onreSize;
const resize = debounce(() => {
callback();
}, 1000);
window.addEventListener('resize', resize);
}
onResize() {
const callback = this.onreSize;
debounce(() => {
callback();
}, 1000);
}
/**
* 響應(yīng)式瀑布
*/
@Bind
onreSize() {
if (window.location.href.indexOf('hipsappsa/pandect') === -1) {
this.setState({
ifResize: true,
});
} else {
this.setState({
ifResize: false,
});
}
}
頁面掛載的時候就進行resize
監(jiān)聽贝乎,當(dāng)發(fā)生resize
事件之后進行一下路由判斷,如果是其他頁面叽粹,就改變state
里的一個值览效,返回到當(dāng)前頁之后進行一下state
的值判斷,為true
才刷新虫几。
為了頁面切換的時候更合理一些(不展示錯亂的頁面)朽肥,加個loading
延時一秒過度一下,還要記得unmount
時去除掉resize
監(jiān)聽持钉。
這里的圖表用的是bizcharts
衡招,它會監(jiān)聽頁面resize
的變化,但是當(dāng)我們改變寬高的時候每强,發(fā)現(xiàn)沒有自適應(yīng)始腾,一開始我的解決方法是把這個渲染的canvas
設(shè)置為width:100%
,這樣能使它拉伸開空执,但是很丑浪箭,因為是相當(dāng)于是放大,這樣效果太不好了辨绊,所以也要解決這個問題奶栖。
React-Grid-Layout
組件有一個props
,onResize
事件返回當(dāng)前改變寬高這樣的事件回調(diào)门坷,我們在這個回調(diào)里手動觸發(fā)一下頁面resize
事件宣鄙,以實現(xiàn)bizcharts
的自適應(yīng)。
// 當(dāng)手動修改resize之后觸發(fā)一次頁面resize使得bizcharts重新渲染
handResize = debounce(() => {
window.dispatchEvent(new Event('resize'));
}, 100);