

























AutoSizer 是 react-virtualized 库中的一个核心组件,它的主要功能是自动检测父元素的尺寸变化,并将计算后的宽高值传递给子组件。这使得需要明确尺寸的组件(如 Grid、List、Table)能够自动适应容器的可用空间。
AutoSizer 采用 Render Props 模式,通过函数形式的 children 属性将尺寸信息传递给子组件:
<AutoSizer>
{({height, width}) => (
<List height={height} width={width} />
)}
</AutoSizer>AutoSizer 的核心设计原则是:填充但不拉伸父容器。这避免了与 flexbox 布局的冲突问题。具体实现上:
overflow: visible 和 height: 0 / width: 0,不会阻止容器收缩offsetHeight/offsetWidth 减去 padding 来获取可用空间type Props = {
children: Size => React.Element<*>, // Render Props 函数
className?: string, // 自定义 CSS 类名
defaultHeight?: number, // SSR 默认高度
defaultWidth?: number, // SSR 默认宽度
disableHeight: boolean, // 禁用高度管理
disableWidth: boolean, // 禁用宽度管理
nonce?: string, // CSP nonce
onResize: Size => void, // 尺寸变化回调
style: ?Object, // 自定义样式
};
type State = {
height: number,
width: number,
};组件维护了以下关键实例属性:
_parentNode: 父 DOM 节点引用_autoSizer: AutoSizer 根元素引用_window: window 对象引用(用于 SSR 兼容)_detectElementResize: 元素尺寸检测器实例componentDidMount() {
const {nonce} = this.props;
if (
this._autoSizer &&
this._autoSizer.parentNode &&
// ... 安全检查
) {
// 延迟访问 parentNode,处理边缘情况
this._parentNode = this._autoSizer.parentNode;
this._window = this._autoSizer.parentNode.ownerDocument.defaultView;
// 延迟加载 resize handler 以支持 SSR
this._detectElementResize = createDetectElementResize(
nonce,
this._window,
);
// 添加尺寸变化监听器
this._detectElementResize.addResizeListener(
this._parentNode,
this._onResize,
);
// 立即执行一次尺寸计算
this._onResize();
}
}关键点:
detectElementResize,避免在服务端执行浏览器相关代码_onResize() 来获取初始尺寸_onResize = () => {
const {disableHeight, disableWidth, onResize} = this.props;
if (this._parentNode) {
// 获取父元素的总尺寸
const height = this._parentNode.offsetHeight || 0;
const width = this._parentNode.offsetWidth || 0;
// 获取 window 对象(SSR 兼容)
const win = this._window || window;
// 获取计算后的样式
const style = win.getComputedStyle(this._parentNode) || {};
// 解析 padding 值
const paddingLeft = parseInt(style.paddingLeft, 10) || 0;
const paddingRight = parseInt(style.paddingRight, 10) || 0;
const paddingTop = parseInt(style.paddingTop, 10) || 0;
const paddingBottom = parseInt(style.paddingBottom, 10) || 0;
// 计算可用空间(减去 padding)
const newHeight = height - paddingTop - paddingBottom;
const newWidth = width - paddingLeft - paddingRight;
// 只在尺寸真正变化时更新状态
if (
(!disableHeight && this.state.height !== newHeight) ||
(!disableWidth && this.state.width !== newWidth)
) {
this.setState({
height: newHeight,
width: newWidth,
});
// 触发 onResize 回调
onResize({height: newHeight, width: newWidth});
}
}
};关键点:
|| 0 来防止无效值,避免 NaN 的情况(处理组件被立即移除 DOM 的边缘情况)getComputedStyle 获取实际的 padding 值,并从总尺寸中减去,确保子组件获得的是真正的可用空间disableHeight 和 disableWidth 属性来决定是否管理对应维度render() {
const {
children,
className,
disableHeight,
disableWidth,
style,
} = this.props;
const {height, width} = this.state;
// 外层 div 不强制宽度/高度,防止容器无法收缩
// 内层组件应使用溢出和计算后的宽度/高度
const outerStyle: Object = {overflow: 'visible'};
const childParams: Object = {};
if (!disableHeight) {
outerStyle.height = 0; // 不阻止容器收缩
childParams.height = height;
}
if (!disableWidth) {
outerStyle.width = 0; // 不阻止容器收缩
childParams.width = width;
}
return (
<div
className={className}
ref={this._setRef}
style={{
...outerStyle,
...style,
}}>
{children(childParams)}
</div>
);
}关键设计决策:
disableHeight 和 disableWidth 决定是否传递对应的尺寸值componentWillUnmount() {
if (this._detectElementResize && this._parentNode) {
this._detectElementResize.removeResizeListener(
this._parentNode,
this._onResize,
);
}
}在组件卸载时移除尺寸监听器,防止内存泄漏。
AutoSizer 依赖于 detectElementResize 库来监听元素尺寸变化。这个库使用了一种巧妙的机制:
Scroll 事件检测:使用 scroll 事件来检测元素尺寸变化,而不是轮询或 MutationObserver
触发元素(Triggers):
expand-trigger 和 contract-trigger检测机制:
expand-trigger:当父元素变大时,其内部元素会触发滚动contract-trigger:当父元素变小时,其自身会触发滚动CSS 动画检测:使用 CSS 动画来检测元素的显示/重新附加(display/re-attach)
性能优化:
requestAnimationFrame 来节流处理detectElementResize 会对父元素进行一些 DOM 操作:
position 为 static,会改为 relativeresize-triggers)这些操作是在 React VirtualDOM 之外进行的,这是为了性能考虑的必要权衡。
AutoSizer 通过以下机制支持 SSR:
默认尺寸:通过 defaultHeight 和 defaultWidth 属性提供初始渲染的尺寸值
延迟初始化:在 componentDidMount 中才初始化 detectElementResize,避免在服务端执行浏览器相关代码
安全检查:在访问 DOM API 前进行充分的安全检查,确保在服务端环境下不会出错
state = {
height: this.props.defaultHeight || 0,
width: this.props.defaultWidth || 0,
};初始状态使用默认值或 0,在客户端挂载后会立即更新为实际尺寸。
在 _onResize 中使用 || 0 来处理组件被立即移除 DOM 的情况:
const height = this._parentNode.offsetHeight || 0;
const width = this._parentNode.offsetWidth || 0;延迟访问 parentNode 直到 componentDidMount,以兼容不同的 React 实现(如 react-lite)和边缘情况。
通过 parseInt 和 || 0 来处理无效的 padding 值:
const paddingLeft = parseInt(style.paddingLeft, 10) || 0;保存 window 对象引用,避免在某些环境下(如 iframe)访问错误的 window 对象。
条件更新:只在尺寸真正变化时更新 state 和触发回调
事件节流:detectElementResize 使用 requestAnimationFrame 来节流处理滚动事件
避免重排:忽略来自子元素的滚动事件,减少不必要的 reflow
最小化 DOM 操作:只在必要时进行 DOM 查询和操作
在 flexbox 容器中使用 AutoSizer 时,建议将其包裹在一个 div 中,而不是直接作为 flex 容器的子元素,以避免尺寸计算的循环问题。
如果 AutoSizer 报告的高度或宽度为 0,通常是因为父元素(或其某个祖先元素)的高度为 0。需要确保父元素有明确的尺寸。
可以使用 disableHeight 或 disableWidth 来只管理一个维度:
<AutoSizer disableHeight>
{({width}) => <Component height={200} width={width} />}
</AutoSizer>AutoSizer 通过以下核心技术实现了自动尺寸管理:
height: 0 / width: 0 避免阻止容器收缩这种设计使得 AutoSizer 能够可靠地工作在各种复杂布局场景中,为 react-virtualized 的其他组件提供了灵活的尺寸管理能力。
原文地址:https://webfem.com/post/react-virtualized-AutoSizer,转载请注明出处
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。