在 React 中创建可重用的网格系统

2025-06-09

在 React 中创建可重用的网格系统

网格系统可以说是构建网站最有价值的布局工具。如果没有它,响应式布局就根本算不上响应式。

我经常使用 React,所以我决定创建一个可以在我的 React 应用中复用的网格系统。它最初只是我的个人工具,但随着我越来越习惯它,我决定将它发布出来供其他开发者使用。

所以我照做了。它叫 React Tiny Grid,是一个 12 列的网格系统,非常方便。你可以在这里找到它。

但今天,我们将逐步重建它,以便您可以跟随并了解它是如何构建的。

设置

我们将使用 styled-components 来设置网格系统的样式。现在就安装它吧。

$ npm install --save styled-components

现在我们已经安装了依赖项,我们将创建两个文件:一个用于 Row 组件,一个用于 Column 组件。

$ touch Row.js Column.js

基本网格功能

首先,我们将创建一个基本的弹性包装器,使所有列项具有相同的宽度,并包裹它们。

创建行组件

在我们的 Row.js 文件中,我们将概述基本的行组件。

import  React  from  'react';
import  styled, { css } from  'styled-components';
import { Column } from  './Column';

export const Row = ({children}) => {
  return (
    <Wrapper>
      {React.Children.toArray(children).map((item) => {
        return (
          item && (
            <Column>
              {item.props.children}
            </Column>
          )
        );
      })}
    </Wrapper>
  );
};

const  Wrapper = styled.div`
  @media (min-width: 769px) {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    margin: 0 -8px 0 -8px
  }
`;

让我们来分析一下。

对于基本功能,我们通过children这个组件进行映射,并将它们分别设置为一列(稍后我们将设置它们的样式)。

{React.Children.toArray(children).map((item) => {
  return (
    item && (
      <Column>
        {item.props.children}
      </Column>
    )
  );
})}

要添加网格功能,我们只需创建<Wrapper>一个 flex 元素。

const  Wrapper = styled.div`
  @media (min-width: 769px) {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    margin: 0 -8px 0 -8px;
  }
`;

一旦屏幕宽度超过 769px,我们就会“激活”网格系统。然后,我们将显示设置为弹性。

我们还添加了负边距来考虑列的间距(稍后设置样式)。

margin: 0 -8px 0 -8px;

创建列组件

现在我们有了 Row 组件,我们需要设置 Column 组件的样式。

在我们的 Column.js 文件中,我们将创建基本的列标记和样式。

import  React  from  'react';
import  styled, { css } from  'styled-components';

export const Column = ({children}) => {
  return (
    <Wrapper>{children}</Wrapper>
  );
};

const Wrapper = styled.div`
  flex: 1 1 0;
  width: 100%;
  padding: 8px;
`;

现在我们要做的就是让 Column 能够像其兄弟元素一样调整大小。这可以通过flex属性来实现。

flex: 1 1 0;

我们还为每列添加了 8px 的 padding。如果你还记得的话,这就是我们给组件添加的负 margin 值Row。这是为了确保列的边缘与其父容器的边缘相接。

支持自定义断点

到目前为止,我们已经拥有了一个自动网格系统!所有列的大小都已调整,并且在移动设备上保持全宽。

但真正的网格系统支持自定义断点。现在就让我们来做吧。

在我们的文件中Row.js,我们将接受一个breakpointsprop,其默认值为 769。

export const Row = ({children, breakpoints = [769]}) => {
  ...
};

现在,我们可以用这个断点数组来决定何时激活网格。为此,我们将breakpoints数组中的第一个元素传递给<Wrapper>组件。

export const Row = ({children}) => {
  return (
    <Wrapper breakpoint={breakpoints[0]}>
      ...
    </Wrapper>
  );
};

然后,我们将 769px 媒体查询替换为 styled-components 支持的模板字面量。这样我们就可以使用断点值了。

const  Wrapper = styled.div`
  @media (min-width: ${props => props.breakpoint}px) {
    ...
  }
`;

现在,我们可以将自定义断点传递给我们的Row组件。

<Row breakpoints={[960]} />

但你知道什么会很酷吗?

自定义列宽。针对每个断点 🤯

我们现在就这么做吧!

自定义宽度

回到文件内部Column.js,我们需要接受两个新的 props:首先,一个breakpoints数组,它将从父组件传递下来Row。其次,一个widths数组,它将包含一个数字数组,用于定义要占用的列数。

export const Column = ({children, breapoints, widths = ['auto']}) => {
  ...
};

注意:我们对宽度使用了默认值 auto:这将允许列占用任何可用的空间,以防我们忘记传递 widths 属性。

现在,我们正在设置网格系统,以支持最多三个自定义断点和宽度。但是,我们需要确保这三个断点都有一个默认值,以防我们忘记传入值。

在我们的组件顶部Column,我们将添加这些变量。

const  breakpointOne = breakpoints[0];
const  breakpointTwo = breakpoints.length >= 1 ? breakpoints[1] : null;
const  breakpointThree = breakpoints.length >= 2 ? breakpoints[2] : null;

const  widthOne = widths[0];
const  widthTwo = widths.length >= 1 ? widths[1] : null;
const  widthThree = widths.length >= 2 ? widths[2] : null;

本质上,我们要做的是检查是否有 3 个宽度值。如果没有,就将第三个值设置为前一个宽度项。这样,我们的网格就不会断裂!

现在,我们需要将这些值作为道具传递给列<Wrapper>组件。

export const Column = ({children, breakpoints, widths = ['auto']}) => {
  return (
    <Wrapper
      breakpointOne={breakpointOne}
      breakpointTwo={breakpointTwo}
      breakpointThree={breakpointThree}
      widthOne={widthOne}
      widthTwo={widthTwo}
      widthThree={widthThree}
    >
      {children}
    </Wrapper>
  );
};

这将允许我们根据特定的断点改变列的宽度。

在我们的Wrapper样式组件中,让我们添加媒体查询。

const Wrapper = styled.div`
  flex: 1 1 0;
  width: 100%;
  padding: 8px;

  // ACTIVE BETWEEN BREAKPOINT ONE AND TWO (OR 9999PX)
  @media(min-width: ${props => props.breakpointOne}px) and
  (max-width: ${props => props.breakpointTwo | 9999}px) {
    width: ${props => props.widthOne !== 'auto'
      ? `${(props.widthOne / 12) * 100}%`
      : null};
    flex: ${(props) => (props.widthOne !== 'auto' ? 'none !important' : null)};
  }

  // ACTIVE BETWEEN BREAKPOINT TWO AND THREE (OR 9999PX)
  @media(min-width: ${props => props.breakpointTwo}px) and
  (max-width: ${props => props.breakpointThree | 9999}px) {
    width: ${props => props.widthTwo !== 'auto'
      ? `${(props.widthTwo / 12) * 100}%`
      : null};
    flex: ${(props) => (props.widthTwo !== 'auto' ? 'none !important' : null)};
  }

  // ACTIVE BETWEEN BREAKPOINT THREE AND UP
  @media(min-width: ${props => props.breakpointThree}px) {
    width: ${props => props.widthThree !== 'auto'
      ? `${(props.widthThree / 12) * 100}%`
      : null};
    flex: ${(props) => (props.widthThree !== 'auto' ? 'none !important' : null)};
  }
`;

好的。有很多东西要看。

我们首先要确保max-width在媒体查询中添加一个。这是为了确保flex当宽度值为“auto”时,该属性不会被重置。

我们要注意的是用于计算列宽的函数。由于我们使用的是 12 列网格,因此我们通过将宽度(1-12 之间的值)除以 12 来获得该值。我们将该数字乘以 100 即可得到百分比。

width: ${props => props.widthThree !== 'auto' ? `${(props.widthThree / 12) * 100}%` : null};

我们还添加了一个三元运算符,通过将宽度值设置为空,确保如果列宽是自动的,宽度仍然为 100%。

现在,我们需要做的最后一件事是将断点从Row组件传递到Column组件。

在我们的文件中Row.js,我们将更新返回语句。

return (
  {React.Children.toArray(children).map((item) => {
    return (
      item && (
        <Column
          breakpoints={breakpoints}
          {...item.props}
        >
          {item.props.children}
        </Column>
      )
    );
  })}
)

瞧!现在,我们可以为我们的网格系统使用自定义断点和宽度了。

<Row breakpoints={[576]}>   
 <Column widths={[4]} />  
 <Column widths={[8]} />  
 <Column widths={[3]} />  
 <Column widths={[9]} />  
 <Column widths={[7]} />  
 <Column widths={[5]} />  
</Row>

结论

现在,我们已经拥有一个功能齐全的 React 网格系统。如果您想要更多功能,例如自定义间距、偏移量等,请查看React Tiny Grid

您可以在Github上找到此网格系统的完整代码

如果您喜欢本教程并发现 React Tiny Grid 很有用,请我喝杯咖啡,我将不胜感激!

如果您对网格系统有任何疑问或改进,可以在下面发表评论。

鏂囩珷鏉ユ簮锛�https://dev.to/jarodpeachey/creating-a-reusable-grid-system-in-react-2p47
PREV
玩游戏来提高你的开发技能。
NEXT
Stop Using JavaScript Classes!