发布于 2026-01-06 2 阅读
0

使用 Chakra UI 构建落地页 - 第一部分

使用 Chakra UI 构建落地页 - 第一部分

这篇文章最初发布在我的博客上。

您可以在这里订阅并了解更多我的文章。


太长不看

我将教你如何使用 Chakra UI 设计系统在 React 中构建响应式落地页。这是第一部分,我们将设置落地页并构建首页横幅部分。

Chakra UI React 登录页面至今

着陆页技巧电子书的推广横幅

安装

我们将创建一个新项目,并将create-react-app其命名为 landing-page。

npx create-react-app landing-page
cd demo-app
Enter fullscreen mode Exit fullscreen mode

接下来,我们将安装 Chakra UI 库及其依赖项。

yarn add @chakra-ui/core @emotion/core @emotion/styled emotion-theming
Enter fullscreen mode Exit fullscreen mode

要使用 React 设置 Chakra UI,需要它的 ThemeProvider,还可以选择添加自定义主题。您可以查看我之前的文章了解安装步骤

定义文件夹结构

关于理想的 React 文件夹结构一直存在很多讨论。我认为并不存在完美的文件夹结构,你只需要选择一个清晰易懂、符合你目标的结构即可。

我采用KISS 原则(“保持简单,笨蛋”)来构建文件夹结构。它由三个核心目录组成:pagescomponentsutils

├─ public
├─ src
    └─ components
        └─ layouts
        └─ sections
        └─ ui
    └─ utils
    └─ App.js
    └─ index.js
Enter fullscreen mode Exit fullscreen mode

组件文件夹

components文件夹包含三个子目录:

  • sections包含所有部分的文件夹(例如:Hero、Header、Footer
  • 包含我们主要页面布局的文件夹layouts(例如 LandingLayout、AuthLayout)
  • ui包含所有没有业务逻辑或副作用的小型组件的文件夹(例如:Logo、按钮

页面文件夹

在这个pages文件夹中,您可以将我们着陆页的所有页面放置进去,例如首页、关于我们、注册、登录等等。

每个页面都会渲染一个布局,并由多个部分组成。每个部分组件都以其主要变量(文本、图像、链接)作为 props,因此自定义页面非常容易。

工具文件夹

utils文件夹包含所有函数助手(例如,我们的自定义主题)。

一般来说,当需要在多个地方使用特定功能时,最好创建一个辅助函数。

设置应用程序组件

在本教程中,由于我们将创建一个单页着陆页,因此路由是可选的。但是,大多数实际的着陆页都包含多个页面,所以我在这里设置了路由,以节省您的时间。

App组件将负责我们应用程序的路由。当然,我们会选择最先进的路由react-router-dom库。

您可以通过输入以下命令进行安装:

yarn add react-router-dom
Enter fullscreen mode Exit fullscreen mode

在本教程中,由于我们将创建一个单页着陆页,因此路由是可选的。但是,大多数实际的着陆页都包含多个页面,因此我在这里进行了设置,以方便您的操作。

我们的设置很简单。如果您需要了解更多高级细节,可以查看官方文档。

我们必须使用一个BrowserRouter组件来包装我们的应用程序,该组件可以使 UI 与 URL 保持同步。建议使用 BrowserRouter 而不是普通的路由,Router因为它会history在内部处理对象。

然后,我们定义Route组件(仅/用于我们的示例),并用组件包裹它们Switch

Switch 组件选择与当前位置匹配的路径,并且只返回一个组件。

import React from "react"
import { BrowserRouter, Switch, Route } from "react-router-dom"

import Landing from "./pages/Landing"

export default function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/">
          <Landing />
        </Route>
      </Switch>
    </BrowserRouter>
  )
}
Enter fullscreen mode Exit fullscreen mode

创建布局

现在是时候创建LandingLayout.js文件并将其放入/components/layouts文件夹中了。

该组件将始终渲染页眉、页脚以及作为子组件传递的任何组件。

为了实现落地页的垂直布局,我们需要添加Flex Chakra UI 组件。它会以经典div元素的形式渲染display: flex。Flex 组件有一些方便的简写属性:

  • flexDirectiondirection
  • flexWrapwrap
  • alignItemsalign
  • justifyContentjustify

因此,初始的 LandingLayout 组件是一个列居中的 flexbox 组件,它渲染 Header 组件及其所有子组件。为了使布局居中并具有响应式设计,我们添加了CSS 样式并针对大屏幕margin: 0 auto进行了设置。max-width: 1200px

Chakra UI 提供了两种定义响应式样式的方法。您可以根据具体情况选择更合适、更易读的方案。

  // First option
  maxW={[
      "auto",    // base
      "auto",    // 480px upwards
      "auto",    // 768px upwards
      "1200px",  // 992px upwards
    ]}

  // Second option
  maxW={{
    base: "auto",
    sm: "auto",
    md: "auto",
    lg: "auto",
    xl: "1200px"
  }}
Enter fullscreen mode Exit fullscreen mode

完整的LandingLayout组件如下:

import React from "react"
import { Flex } from "@chakra-ui/core"
import Header from "../sections/Header"
import Footer from "../sections/Footer" // will add this in the part 2

export default function LandingLayout(props) {
  return (
    <Flex
      direction="column"
      align="center"
      maxW={{ xl: "1200px" }}
      m="0 auto"
      {...props}
    >
      <Header />
      {props.children}
      <Footer />
    </Flex>
  )
}
Enter fullscreen mode Exit fullscreen mode

下一步是创建Header位于其内部的组件。LandingLayout.

响应式头部组件

文件Header.js将位于该/components/sections文件夹中。

该组件的起点是Jean Bauer 在 Chakra UI 官方文档中编写的这段代码。

我们将进行一些调整,使该组件完全响应式,并增强其用户界面。

标题组件在不同屏幕上的显示效果

最外层组件是一个行状的 flexboxnav元素。该justify属性设置为 true,space-between以便在徽标和实际菜单之间留出适当的间距。

此外,我们根据活动屏幕尺寸,将颜色background-color组合color规则设置为上图中显示的颜色组合。

<Flex
  as="nav"
  align="center"
  justify="space-between"
  wrap="wrap"
  w="100%"
  mb={8}
  p={8}
  bg={["primary.500", "primary.500", "transparent", "transparent"]}
  color={["white", "white", "primary.700", "primary.700"]}
  {...props}
>
  ...
</Flex>
Enter fullscreen mode Exit fullscreen mode

display: block这里我们要应用的基本技巧是通过有条件地应用 CSS 规则来隐藏/显示菜单图标和菜单项display: none

菜单/关闭图标仅在断点处可见base,在大于md断点的屏幕上隐藏。根据该show值,我们会显示CloseIcon(当show === true)或MenuIcon(当show === false)。

<Box display={{ base: "block", md: "none" }} onClick={toggleMenu}>
  {show ? <CloseIcon /> : <MenuIcon />}
</Box>
Enter fullscreen mode Exit fullscreen mode

菜单项也采用了同样的技巧。在大于md断点的屏幕上,菜单项始终显示;而在较小的屏幕上,菜单项则根据条件显示。显示条件取决于变量的状态show,该变量可通过点击“菜单/关闭”图标进行切换。

这里需要特别说明的是 CSS 属性的使用flex-basic。它用于设置 flex 元素的初始主尺寸。我们使用该属性来强制菜单图标出现时,元素换行显示。它与flex-wrap: wrap最外层 flex 组件的规则结合使用,该规则允许其子元素换行显示。

<Box
  display={{ base: show ? "block" : "none", md: "block" }}
  flexBasis={{ base: "100%", md: "auto" }}
>
  ...
</Box>
Enter fullscreen mode Exit fullscreen mode

这个盒子里存放着我们的菜单。为了方便起见,我们将使用一个Flex容器来定义子元素的方向并调整它们的位置。

这里简单说明一下。我们本来可以选择 Stack 组件而不是 Flex 组件Stack。但在我们的例子中,Stack 组件引入了一些 UI 错误,所以我们选择了 Stack 组件Flex

<Flex
  align="center"
  justify={["center", "space-between", "flex-end", "flex-end"]}
  direction={["column", "row", "row", "row"]}
  pt={[4, 4, 0, 0]}
>
  ...
</Flex>
Enter fullscreen mode Exit fullscreen mode

对于菜单项,我们创建一个单独的MenuItem组件,该组件会渲染一个Text带有元素的组件Link到所需位置。
由于使用了Flex组件作为容器,我们需要手动设置菜单项之间的间距。

这是通过传递该属性实现的isLast。该属性指示是否需要向 MenuItem 添加适当的边距。

const MenuItem = ({ children, isLast, to = "/", ...rest }) => {
  return (
    <Text
      mb={{ base: isLast ? 0 : 8, sm: 0 }}
      mr={{ base: 0, sm: isLast ? 0 : 8 }}
      display="block"
      {...rest}
    >
      <Link to={to}>{children}</Link>
    </Text>
  )
}
Enter fullscreen mode Exit fullscreen mode

最终的头部组件如下所示:

import React from "react"
import { Link } from "react-router-dom"
import { Box, Flex, Text, Button, Stack, PseudoBox } from "@chakra-ui/core"
import Logo from "../ui/Logo"

import { CloseIcon, MenuIcon } from ".../Icons"

const MenuItems = props => {
  const { children, isLast, to = "/", ...rest } = props
  return (
    <Text
      mb={{ base: isLast ? 0 : 8, sm: 0 }}
      mr={{ base: 0, sm: isLast ? 0 : 8 }}
      display="block"
      {...rest}
    >
      <Link to={to}>{children}</Link>
    </Text>
  )
}

const Header = props => {
  const [show, setShow] = React.useState(false)
  const toggleMenu = () => setShow(!show)

  return (
    <Flex
      as="nav"
      align="center"
      justify="space-between"
      wrap="wrap"
      w="100%"
      mb={8}
      p={8}
      bg={["primary.500", "primary.500", "transparent", "transparent"]}
      color={["white", "white", "primary.700", "primary.700"]}
      {...props}
    >
      <Flex align="center">
        <Logo
          w="100px"
          color={["white", "white", "primary.500", "primary.500"]}
        />
      </Flex>

      <Box display={{ base: "block", md: "none" }} onClick={toggleMenu}>
        {show ? <CloseIcon /> : <MenuIcon />}
      </Box>

      <Box
        display={{ base: show ? "block" : "none", md: "block" }}
        flexBasis={{ base: "100%", md: "auto" }}
      >
        <Flex
          align={["center", "center", "center", "center"]}
          justify={["center", "space-between", "flex-end", "flex-end"]}
          direction={["column", "row", "row", "row"]}
          pt={[4, 4, 0, 0]}
        >
          <MenuItems to="/">Home</MenuItems>
          <MenuItems to="/how">How It works </MenuItems>
          <MenuItems to="/faetures">Features </MenuItems>
          <MenuItems to="/pricing">Pricing </MenuItems>
          <MenuItems to="/signup" isLast>
            <Button
              size="sm"
              rounded="md"
              color={["primary.500", "primary.500", "white", "white"]}
              bg={["white", "white", "primary.500", "primary.500"]}
              _hover={{
                bg: [
                  "primary.100",
                  "primary.100",
                  "primary.600",
                  "primary.600",
                ],
              }}
            >
              Create Account
            </Button>
          </MenuItems>
        </Flex>
      </Box>
    </Flex>
  )
}

export default Header
Enter fullscreen mode Exit fullscreen mode

让我们深入了解一下英雄部分。

首页横幅部分是任何落地页最重要的部分。它是用户首先接触的部分,必须做到完美!

如下所示,该部分由两个核心元素组成:图片和主要内容(标题、副标题、行动号召按钮)。

不同屏幕尺寸下的Hero组件

在继续之前,我们需要在这里定义组件的属性。我们首页的主要变量有五个:标题、副标题和按钮的文本,以及图片和行动号召(CTA)链接的URL。

export default function Hero({
  title,
  subtitle,
  image,
  ctaLink,
  ctaText,
  ...rest
}) {
  return ();
}
Enter fullscreen mode Exit fullscreen mode

关于实际代码,顶部容器将Flex再次成为一个组件。我们将根据屏幕尺寸更改其flex-direction属性justify-content

对于移动设备,我们将方向设置为column-reverse。之所以这样选择,是因为我们想改变两个主要元素的顺序。

传递该属性是为了允许我们从外部rest操作组件的最外层容器。Hero

<Flex
  align="center"
  justify={{ base: "center", md: "space-around", xl: "space-between" }}
  direction={{ base: "column-reverse", md: "row" }}
  wrap="no-wrap"
  minH="70vh"
  px={8}
  mb={16}
  {...rest}
>
  ...
</Flex>
Enter fullscreen mode Exit fullscreen mode

传递此min-height属性是为了确保该部分在大屏幕上垂直居中显示。您可以在下图看到区别。

设置和不设置属性 min-height 的区别

现在,轮到图像组件了。唯一需要做的就是调整宽度。在小屏幕上,我们需要强制width: 80%并逐步缩小图像宽度。

此外,我们margin-bottom在小屏幕上添加了一个,以增大图像和内容之间的空间。

<Box w={{ base: "80%", sm: "60%", md: "50%" }} mb={{ base: 12, md: 0 }}>
  <Image src={image} size="100%" rounded="1rem" shadow="2xl" />
</Box>
Enter fullscreen mode Exit fullscreen mode

关于内容元素,它是一个简单的Stack元素,包含两个Heading组件,即按钮ButtonText按钮下方的文本组件。

这里唯一值得注意的地方是元素的排列方式,在移动设备上应该居中显示,而在大屏幕上则应该位于左侧。

<Stack
  spacing={4}
  w={{ base: "80%", md: "40%" }}
  align={["center", "center", "flex-start", "flex-start"]}
>
  <Heading
    as="h1"
    size="xl"
    fontWeight="bold"
    color="primary.800"
    textAlign={["center", "center", "left", "left"]}
  >
    {title}
  </Heading>
  <Heading
    as="h2"
    size="md"
    color="primary.800"
    opacity="0.8"
    fontWeight="normal"
    lineHeight={1.5}
    textAlign={["center", "center", "left", "left"]}
  >
    {subtitle}
  </Heading>
  <Link to={ctaLink}>
    <Button
      variantColor="primary"
      borderRadius="8px"
      py="4"
      px="4"
      lineHeight="1"
      size="md"
      rightIcon="chevron-right"
    >
      {ctaText}
    </Button>
  </Link>
  <Text
    fontSize="xs"
    mt={2}
    textAlign="center"
    color="primary.800"
    opacity="0.6"
  >
    No credit card required.
  </Text>
</Stack>
Enter fullscreen mode Exit fullscreen mode

Hero 组件已准备就绪,您可以在下方查看完整代码:

import React from "react"
import { Link } from "react-router-dom"
import PropTypes from "prop-types"
import { Box, Button, Flex, Image, Heading, Stack, Text } from "@chakra-ui/core"

export default function Hero({
  title,
  subtitle,
  image,
  ctaLink,
  ctaText,
  ...rest
}) {
  return (
    <Flex
      align="center"
      justify={{ base: "center", md: "space-around", xl: "space-between" }}
      direction={{ base: "column-reverse", md: "row" }}
      wrap="no-wrap"
      minH="70vh"
      px={8}
      mb={16}
      {...rest}
    >
      <Stack
        spacing={4}
        w={{ base: "80%", md: "40%" }}
        align={["center", "center", "flex-start", "flex-start"]}
      >
        <Heading
          as="h1"
          size="xl"
          fontWeight="bold"
          color="primary.800"
          textAlign={["center", "center", "left", "left"]}
        >
          {title}
        </Heading>
        <Heading
          as="h2"
          size="md"
          color="primary.800"
          opacity="0.8"
          fontWeight="normal"
          lineHeight={1.5}
          textAlign={["center", "center", "left", "left"]}
        >
          {subtitle}
        </Heading>
        <Link to={ctaLink}>
          <Button
            variantColor="primary"
            borderRadius="8px"
            py="4"
            px="4"
            lineHeight="1"
            size="md"
            rightIcon="chevron-right"
          >
            {ctaText}
          </Button>
        </Link>
        <Text
          fontSize="xs"
          mt={2}
          textAlign="center"
          color="primary.800"
          opacity="0.6"
        >
          No credit card required.
        </Text>
      </Stack>
      <Box w={{ base: "80%", sm: "60%", md: "50%" }} mb={{ base: 12, md: 0 }}>
        <Image src={image} size="100%" rounded="1rem" shadow="2xl" />
      </Box>
    </Flex>
  )
}

Hero.propTypes = {
  title: PropTypes.string,
  subtitle: PropTypes.string,
  image: PropTypes.string,
  ctaText: PropTypes.string,
  ctaLink: PropTypes.string,
}

Hero.defaultProps = {
  title: "React landing page with Chakra UI",
  subtitle:
    "This is the subheader section where you describe the basic benefits of your product",
  image: "https://source.unsplash.com/collection/404339/800x600",
  ctaText: "Create your account now",
  ctaLink: "/signup",
}
Enter fullscreen mode Exit fullscreen mode

总结

目前为止,我们的着陆页应该看起来像这样!

Chakra UI React 登录页面至今

下周,我们将构建响应式页脚功能部分。


如果你喜欢这篇文章,可以关注我的推特账号,我每天都会分享关于编程、设计和微型创业公司启动的技巧。

着陆页技巧电子书的推广横幅

文章来源:https://dev.to/d__raptis/build-a-landing-page-with-chakra-ui-part-1-56gd