在 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
,我们将接受一个breakpoints
prop,其默认值为 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