克隆初始repo:

  1. # react-mini是分支名 #最后的.表示在当前文件夹创建(不新建文件夹)
  2. git clone --single-branch -b react-mini https://github.com/safak/youtube.git .

安装依赖:

  1. yarn
  2. npm install

从google fonts引入urbanist字体:(line8/line12)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1" />
  6. <meta name="description" content="Web site created using create-react-app" />
  7. <link href="https://fonts.googleapis.com/css2?family=Urbanist:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
  8. <style>
  9. * {
  10. margin: 0;
  11. font-family: 'Urbanist', sans-serif;
  12. }
  13. </style>
  14. <title>React App</title>
  15. </head>
  16. <body>
  17. <noscript>You need to enable JavaScript to run this app.</noscript>
  18. <div id="root"></div>
  19. </body>
  20. </html>

Navbar

在src下新建pages文件夹,新建Home.jsx

安装vscode插件ES7+ React/Redux/React-Native snippets,输入rafce可快速建立react箭头函数模板。其他常用插件还有auto close tag、git lens

  1. import React from "react";
  2. const Home = () => {
  3. return <div>Home</div>;
  4. };
  5. export default Home;

在App.jsx中引入

  1. import Home from "./pages/Home";
  2. const App = () => {
  3. return <Home />;
  4. };
  5. export default App;

在src下新建components文件夹,新建Navbar.jsx,并在Home.jsx中引入。

  1. import React from "react";
  2. import Navbar from "../components/Navbar";
  3. const Home = () => {
  4. return (
  5. <div>
  6. <Navbar />
  7. </div>
  8. );
  9. };
  10. export default Home;

安装styled-components

  1. yarn add styled-components

然后使用flex布局使其均匀分布 (line10-11, line14-22)

设置三个容器的flex: 1比设置width: 33.3%要更加精确

  1. import React from "react";
  2. import styled from "styled-components";
  3. const Container = styled.div`
  4. height: 60px;
  5. `;
  6. const Wrapper = styled.div`
  7. padding: 10px 20px;
  8. display: flex;
  9. justify-content: space-between;
  10. `;
  11. const Left = styled.div`
  12. flex: 1;
  13. `;
  14. const Center = styled.div`
  15. flex: 1;
  16. `;
  17. const Right = styled.div`
  18. flex: 1;
  19. `;
  20. const Navbar = () => {
  21. return (
  22. <Container>
  23. <Wrapper>
  24. <Left>
  25. Lorem ipsum dolor sit amet consectetur adipisicing elit. Possimus,
  26. molestiae. Beatae, excepturi esse tempora nesciunt rerum labore vitae
  27. quo dolor adipisci aperiam eos, ea consectetur odio nisi, repudiandae
  28. harum perferendis.
  29. </Left>
  30. <Center>Center</Center>
  31. <Right>right</Right>
  32. </Wrapper>
  33. </Container>
  34. );
  35. };
  36. export default Navbar;

image.png

安装material-ui:

  1. yarn add @material-ui/core @material-ui/icons

再布局好其他元素,此时的文档树结构为:

  1. .
  2. ├── README.md
  3. ├── package.json
  4. ├── public
  5. └── index.html
  6. ├── src
  7. ├── App.jsx
  8. ├── components
  9. └── Navbar.jsx
  10. ├── index.js
  11. └── pages
  12. └── Home.jsx
  13. └── yarn.lock
  1. import { Badge } from "@material-ui/core";
  2. import { Search, ShoppingCartOutlined } from "@material-ui/icons";
  3. import React from "react";
  4. import styled from "styled-components";
  5. const Container = styled.div`
  6. height: 60px;
  7. `;
  8. const Wrapper = styled.div`
  9. padding: 10px 20px;
  10. display: flex;
  11. align-items: center;
  12. justify-content: space-between;
  13. `;
  14. const Left = styled.div`
  15. flex: 1;
  16. display: flex;
  17. align-items: center;
  18. `;
  19. const Language = styled.span`
  20. font-size: 14px;
  21. cursor: pointer;
  22. `;
  23. const SearchContainer = styled.div`
  24. border: 1px solid lightgray;
  25. display: flex;
  26. align-items: center;
  27. margin-left: 25px;
  28. padding: 5px;
  29. `;
  30. const Input = styled.input`
  31. border: none;
  32. `;
  33. const Center = styled.div`
  34. flex: 1;
  35. text-align: center;
  36. `;
  37. const Logo = styled.h1`
  38. font-weight: bold;
  39. `;
  40. const Right = styled.div`
  41. flex: 1;
  42. display: flex;
  43. align-items: center;
  44. justify-content: flex-end;
  45. `;
  46. const MenuItem = styled.div`
  47. font-size: 14px;
  48. cursor: pointer;
  49. margin-left: 25px;
  50. `;
  51. const Navbar = () => {
  52. return (
  53. <Container>
  54. <Wrapper>
  55. <Left>
  56. <Language>EN</Language>
  57. <SearchContainer>
  58. <Input />
  59. <Search style={{ color: "gray", fontSize: 16 }} />
  60. </SearchContainer>
  61. </Left>
  62. <Center>
  63. <Logo>LAMA.</Logo>
  64. </Center>
  65. <Right>
  66. <MenuItem>Register</MenuItem>
  67. <MenuItem>Sign in</MenuItem>
  68. <MenuItem>
  69. <Badge badgeContent={4} color="primary">
  70. <ShoppingCartOutlined />
  71. </Badge>
  72. </MenuItem>
  73. </Right>
  74. </Wrapper>
  75. </Container>
  76. );
  77. };
  78. export default Navbar;

最后效果:
image.png

Announcement

在components下创建Announcement.jsx并在Home.jsx中引入:

  1. import styled from "styled-components";
  2. const Container = styled.div`
  3. height: 30px;
  4. background-color: teal;
  5. color: white;
  6. display: flex;
  7. align-items: center;
  8. justify-content: center;
  9. font-size: 14px;
  10. font-weight: 500;
  11. `;
  12. const Announcement = () => {
  13. return <Container>Super Deal! Free Shipping on Orders Over $50</Container>;
  14. };
  15. export default Announcement;

最后效果:
image.png

Slider

在components下创建Slider.jsx并在Home.jsx中引入:

  1. import { ArrowLeftOutlined, ArrowRightOutlined } from "@material-ui/icons";
  2. import styled from "styled-components";
  3. const Container = styled.div`
  4. width: 100%;
  5. height: 100vh;
  6. display: flex;
  7. position: relative;
  8. overflow: hidden;
  9. `;
  10. const Arrow = styled.div`
  11. width: 50px;
  12. height: 50px;
  13. background-color: #fff7f7;
  14. border-radius: 50%;
  15. display: flex;
  16. align-items: center;
  17. justify-content: center;
  18. cursor: pointer;
  19. opacity: 0.5;
  20. // 箭头垂直居中:
  21. position: absolute;
  22. top: 0;
  23. bottom: 0;
  24. margin: auto;
  25. left: ${(props) => props.direction === "left" && "10px"};
  26. right: ${(props) => props.direction === "right" && "10px"};
  27. // 防止Wrapper的transform:translateX()将arrow覆盖
  28. z-index: 2;
  29. `;
  30. const Wrapper = styled.div`
  31. height: 100%;
  32. display: flex;
  33. `;
  34. const Slide = styled.div`
  35. width: 100vw;
  36. height: 100vh;
  37. display: flex;
  38. align-items: center;
  39. background-color: ${(props) => props.bg};
  40. `;
  41. const ImgContainer = styled.div`
  42. height: 100%;
  43. flex: 1;
  44. `;
  45. const Image = styled.img`
  46. height: 80%;
  47. `;
  48. const InfoContainer = styled.div`
  49. flex: 1;
  50. padding: 50px;
  51. `;
  52. const Title = styled.h1`
  53. font-size: 70px;
  54. `;
  55. const Desc = styled.p`
  56. margin: 50px 0px;
  57. font-size: 20px;
  58. font-weight: 500;
  59. letter-spacing: 3px;
  60. `;
  61. const Button = styled.button`
  62. padding: 10px;
  63. font-size: 20px;
  64. background-color: transparent;
  65. cursor: pointer;
  66. `;
  67. const Slider = () => {
  68. const handleClick = (direction) => {};
  69. return (
  70. <Container>
  71. {/* 传参数要用箭头函数 */}
  72. <Arrow direction="left" onClick={() => handleClick("left")}>
  73. <ArrowLeftOutlined />
  74. </Arrow>
  75. <Wrapper>
  76. <Slide bg="#cde2e9">
  77. <ImgContainer>
  78. <Image src="https://static.nike.com.hk/resources/product/DV3317-494/DV3317-494_BL1.png" />
  79. </ImgContainer>
  80. <InfoContainer>
  81. <Title>SUMMER SEAL</Title>
  82. <Desc>
  83. DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.
  84. </Desc>
  85. <Button>SHOW NOW</Button>
  86. </InfoContainer>
  87. </Slide>
  88. <Slide bg="#8c9cb2">
  89. <ImgContainer>
  90. <Image src="https://static.nike.com.hk/resources/product/DH7615-101/DH7615-101_BL1.png" />
  91. </ImgContainer>
  92. <InfoContainer>
  93. <Title>SUMMER SEAL</Title>
  94. <Desc>
  95. DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.
  96. </Desc>
  97. <Button>SHOW NOW</Button>
  98. </InfoContainer>
  99. </Slide>
  100. <Slide bg="#e1ddaa">
  101. <ImgContainer>
  102. <Image src="https://static.nike.com.hk/resources/product/DJ6014-100/DJ6014-100_BL1.png" />
  103. </ImgContainer>
  104. <InfoContainer>
  105. <Title>SUMMER SEAL</Title>
  106. <Desc>
  107. DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.
  108. </Desc>
  109. <Button>SHOW NOW</Button>
  110. </InfoContainer>
  111. </Slide>
  112. </Wrapper>
  113. <Arrow direction="right" onClick={() => handleClick("left")}>
  114. <ArrowRightOutlined />
  115. </Arrow>
  116. </Container>
  117. );
  118. };
  119. export default Slider;

在src下新建data.js:

  1. export const sliderItems = [
  2. {
  3. id: 1,
  4. img: "https://static.nike.com.hk/resources/product/DV3317-494/DV3317-494_BL1.png",
  5. title: "SUMMER SALE",
  6. desc: "DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.",
  7. bg: "#cde2e9",
  8. },
  9. {
  10. id: 2,
  11. img: "https://static.nike.com.hk/resources/product/DH7615-101/DH7615-101_BL1.png",
  12. title: "SUMMER SALE",
  13. desc: "DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.",
  14. bg: "#8c9cb2",
  15. },
  16. {
  17. id: 3,
  18. img: "https://static.nike.com.hk/resources/product/DJ6014-100/DJ6014-100_BL1.png",
  19. title: "SUMMER SALE",
  20. desc: "DON'T COMPROMISE ON STYLE! GET FLAT 30% OFF FOR NEW ARRIVALS.",
  21. bg: "#e1ddaa",
  22. },
  23. ];

然后替换数据:

  1. ...
  2. const Wrapper = styled.div`
  3. height: 100%;
  4. display: flex;
  5. transition: all 1s ease;
  6. transform: translateX(${(props) => props.slideIndex * -100}vw);
  7. `;
  8. ...
  9. const Slider = () => {
  10. const [slideIndex, setSlideIndex] = useState(0);
  11. const handleClick = (direction) => {
  12. if (direction === "left") {
  13. setSlideIndex(slideIndex > 0 ? slideIndex - 1 : 2);
  14. } else {
  15. setSlideIndex(slideIndex < 2 ? slideIndex + 1 : 0);
  16. }
  17. };
  18. return (
  19. <Container>
  20. {/* 传参数要用箭头函数 */}
  21. <Arrow direction="left" onClick={() => handleClick("left")}>
  22. <ArrowLeftOutlined />
  23. </Arrow>
  24. <Wrapper slideIndex={slideIndex}>
  25. {sliderItems.map((item) => (
  26. <Slide bg={item.bg} key={item.id}>
  27. <ImgContainer>
  28. <Image src={item.img} />
  29. </ImgContainer>
  30. <InfoContainer>
  31. <Title>{item.title}</Title>
  32. <Desc>{item.desc}</Desc>
  33. <Button>SHOW NOW</Button>
  34. </InfoContainer>
  35. </Slide>
  36. ))}
  37. </Wrapper>
  38. <Arrow direction="right" onClick={() => handleClick("right")}>
  39. <ArrowRightOutlined />
  40. </Arrow>
  41. </Container>
  42. );
  43. };
  44. export default Slider;

完成效果:
Jietu20220423-100620-HD.gif

Category

在data.js中添加Category数据:

  1. ...
  2. export const categories = [
  3. {
  4. id: 1,
  5. img: "https://static.nike.com.hk/resources/product/DV0683-419/DV0683-419_BL1.png",
  6. title: "T-shirt",
  7. },
  8. {
  9. id: 2,
  10. img: "https://static.nike.com.hk/resources/product/DQ0231-100/DQ0231-100_BL1.png",
  11. title: "Shoes",
  12. },
  13. {
  14. id: 3,
  15. img: "https://static.nike.com.hk/resources/product/DV3193-010/DV3193-010_BL1.png",
  16. title: "Pants",
  17. },
  18. ];

在components文件夹下创建Categories.jsx和CategoryItem.jsx,并在Home.jsx引入Categories.jsx:

  1. import styled from "styled-components";
  2. import { categories } from "../data";
  3. import CategoryItem from "./CategoryItem";
  4. const Container = styled.div`
  5. display: flex;
  6. padding: 20px;
  7. justify-content: space-between;
  8. `;
  9. const Categories = () => {
  10. return (
  11. <Container>
  12. {categories.map((item) => (
  13. <CategoryItem item={item} />
  14. ))}
  15. </Container>
  16. );
  17. };
  18. export default Categories;
  1. import styled from "styled-components";
  2. const Container = styled.div`
  3. flex: 1;
  4. margin: 3px;
  5. /* height: 70vh; */
  6. position: relative;
  7. `;
  8. const Image = styled.img`
  9. //不然其父容器Container的flex:1不生效,因为图片会撑大父容器
  10. width: 100%;
  11. /* height: 100%;
  12. object-fit: cover; */
  13. `;
  14. const Info = styled.div`
  15. position: absolute;
  16. top: 0;
  17. left: 0;
  18. width: 100%;
  19. height: 100%;
  20. display: flex;
  21. flex-direction: column;
  22. align-items: center;
  23. justify-content: center;
  24. `;
  25. const Title = styled.h1`
  26. color: white;
  27. margin-bottom: 20px;
  28. `;
  29. const Button = styled.button`
  30. border: none;
  31. padding: 10px;
  32. background-color: white;
  33. color: gray;
  34. font-weight: 600;
  35. cursor: pointer;
  36. `;
  37. const CategoryItem = ({ item }) => {
  38. return (
  39. <Container>
  40. <Image src={item.img} />
  41. <Info>
  42. <Title>{item.title}</Title>
  43. <Button>SHOP NOW</Button>
  44. </Info>
  45. </Container>
  46. );
  47. };
  48. export default CategoryItem;

完成效果:
image.png

Products

在data.js中添加products信息:

  1. export const popularProducts = [
  2. {
  3. id: 1,
  4. img: "https://static.nike.com.hk/resources/product/AR5005-549/AR5005-549_BL1.png",
  5. },
  6. {
  7. id: 2,
  8. img: "https://static.nike.com.hk/resources/product/DV3449-010/DV3449-010_BL1.png",
  9. },
  10. {
  11. id: 3,
  12. img: "https://static.nike.com.hk/resources/product/DM0013-100/DM0013-100_BL1.png",
  13. },
  14. {
  15. id: 4,
  16. img: "https://static.nike.com.hk/resources/product/DC3237-100/DC3237-100_BL1.png",
  17. },
  18. {
  19. id: 5,
  20. img: "https://static.nike.com.hk/resources/product/DH7579-100/DH7579-100_BL1.png",
  21. },
  22. {
  23. id: 6,
  24. img: "https://static.nike.com.hk/resources/product/DH7161-065/DH7161-065_BL1.png",
  25. },
  26. ];

在components文件夹下新建Products.jsx和Product.jsx:

  1. import styled from "styled-components";
  2. import { popularProducts } from "../data";
  3. import Product from "./Product";
  4. const Container = styled.div`
  5. padding: 20px;
  6. display: flex;
  7. flex-wrap: wrap;
  8. justify-content: space-between;
  9. `;
  10. const Products = () => {
  11. return (
  12. <Container>
  13. {popularProducts.map((item) => (
  14. <Product item={item} key={item.id} />
  15. ))}
  16. </Container>
  17. );
  18. };
  19. export default Products;
  1. import {
  2. FavoriteBorderOutlined,
  3. SearchOutlined,
  4. ShoppingCartOutlined,
  5. } from "@material-ui/icons";
  6. import styled from "styled-components";
  7. const Info = styled.div`
  8. opacity: 0;
  9. width: 100%;
  10. height: 100%;
  11. position: absolute;
  12. top: 0;
  13. left: 0;
  14. background-color: rgba(0, 0, 0, 0.2);
  15. z-index: 3;
  16. display: flex;
  17. align-items: center;
  18. justify-content: center;
  19. transition: all 0.5s ease;
  20. cursor: pointer;
  21. `;
  22. const Container = styled.div`
  23. flex: 1;
  24. margin: 5px;
  25. min-width: 280px;
  26. height: 350px;
  27. display: flex;
  28. align-items: center;
  29. justify-content: center;
  30. background-color: #f5fbfd;
  31. position: relative;
  32. &:hover ${Info} {
  33. opacity: 1;
  34. }
  35. `;
  36. const Circle = styled.div`
  37. width: 200px;
  38. height: 200px;
  39. border-radius: 50%;
  40. background-color: white;
  41. // important!
  42. position: absolute;
  43. `;
  44. const Image = styled.img`
  45. height: 75%;
  46. // 避免被上面的Circle挡住
  47. z-index: 2;
  48. `;
  49. const Icon = styled.div`
  50. width: 40px;
  51. height: 40px;
  52. border-radius: 50%;
  53. background-color: white;
  54. display: flex;
  55. align-items: center;
  56. justify-content: center;
  57. margin: 10px;
  58. transition: all 0.5s ease;
  59. &:hover {
  60. background-color: #e9f5f5;
  61. transform: scale(1.1);
  62. }
  63. `;
  64. const Product = ({ item }) => {
  65. return (
  66. <Container>
  67. <Circle />
  68. <Image src={item.img} />
  69. <Info>
  70. <Icon>
  71. <ShoppingCartOutlined />
  72. </Icon>
  73. <Icon>
  74. <SearchOutlined />
  75. </Icon>
  76. <Icon>
  77. <FavoriteBorderOutlined />
  78. </Icon>
  79. </Info>
  80. </Container>
  81. );
  82. };
  83. export default Product;

完成效果:

Newsletter

在components文件夹下新建Newsletter.jsx:

  1. import { Send } from "@material-ui/icons";
  2. import styled from "styled-components";
  3. const Container = styled.div`
  4. height: 60vh;
  5. background-color: #fcf5f5;
  6. display: flex;
  7. align-items: center;
  8. justify-content: center;
  9. flex-direction: column;
  10. `;
  11. const Title = styled.h1`
  12. font-size: 70px;
  13. margin-bottom: 20px;
  14. `;
  15. const Desc = styled.div`
  16. font-size: 24px;
  17. font-weight: 300;
  18. margin-bottom: 20px;
  19. `;
  20. const InputContainer = styled.div`
  21. width: 50%;
  22. height: 40px;
  23. background-color: white;
  24. display: flex;
  25. justify-content: space-between;
  26. border: 1px solid lightgray;
  27. `;
  28. const Input = styled.input`
  29. border: none;
  30. flex: 8;
  31. padding-left: 20px;
  32. `;
  33. const Button = styled.button`
  34. flex: 1;
  35. border: none;
  36. background-color: teal;
  37. color: white;
  38. `;
  39. const Newsletter = () => {
  40. return (
  41. <Container>
  42. <Title>Newsletter</Title>
  43. <Desc>Get timely updates from your favorite products.</Desc>
  44. <InputContainer>
  45. <Input placeholder="Your email" />
  46. <Button>
  47. <Send />
  48. </Button>
  49. </InputContainer>
  50. </Container>
  51. );
  52. };
  53. export default Newsletter;

完成效果:
image.png

Footer

在components文件夹下新建Footer.jsx:

  1. import {
  2. Facebook,
  3. Instagram,
  4. MailOutline,
  5. Phone,
  6. Pinterest,
  7. Room,
  8. Twitter,
  9. } from "@material-ui/icons";
  10. import styled from "styled-components";
  11. const Container = styled.div`
  12. display: flex;
  13. `;
  14. const Left = styled.div`
  15. flex: 1;
  16. display: flex;
  17. flex-direction: column;
  18. padding: 20px;
  19. `;
  20. const Logo = styled.h1``;
  21. const Desc = styled.p`
  22. margin: 20px 0px;
  23. `;
  24. const SocialContainer = styled.div`
  25. display: flex;
  26. `;
  27. const SocialIcon = styled.div`
  28. width: 40px;
  29. height: 40px;
  30. border-radius: 50%;
  31. color: white;
  32. background-color: ${(props) => props.color};
  33. display: flex;
  34. align-items: center;
  35. justify-content: center;
  36. margin-right: 20px;
  37. `;
  38. const Center = styled.div`
  39. flex: 1;
  40. padding: 20px;
  41. `;
  42. const Title = styled.h3`
  43. margin-bottom: 30px;
  44. `;
  45. const List = styled.ul`
  46. margin: 0;
  47. padding: 0;
  48. list-style: none;
  49. display: flex;
  50. flex-wrap: wrap;
  51. `;
  52. const ListItem = styled.li`
  53. width: 50%;
  54. `;
  55. const Right = styled.div`
  56. flex: 1;
  57. padding: 20px;
  58. `;
  59. const ContactItem = styled.div`
  60. margin-bottom: 20px;
  61. display: flex;
  62. align-items: center;
  63. `;
  64. const Payment = styled.img`
  65. width: 200px;
  66. `;
  67. const Footer = () => {
  68. return (
  69. <Container>
  70. <Left>
  71. <Logo>LAMA.</Logo>
  72. <Desc>
  73. Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iste esse
  74. enim, maiores perspiciatis optio veritatis consectetur adipisci, eaque
  75. molestias quod, ab debitis at corporis velit odio molestiae? Aliquam,
  76. molestias exercitationem!
  77. </Desc>
  78. <SocialContainer>
  79. <SocialIcon color="#3b5999">
  80. <Facebook />
  81. </SocialIcon>
  82. <SocialIcon color="#e4405f">
  83. <Instagram />
  84. </SocialIcon>
  85. <SocialIcon color="#55acee">
  86. <Twitter />
  87. </SocialIcon>
  88. <SocialIcon color="#e60023">
  89. <Pinterest />
  90. </SocialIcon>
  91. </SocialContainer>
  92. </Left>
  93. <Center>
  94. <Title>Useful Links</Title>
  95. <List>
  96. <ListItem>Home</ListItem>
  97. <ListItem>Cart</ListItem>
  98. <ListItem>Man Fashion</ListItem>
  99. <ListItem>Woman Fashion</ListItem>
  100. <ListItem>Accessories</ListItem>
  101. <ListItem>My Account</ListItem>
  102. <ListItem>Order Tracking</ListItem>
  103. <ListItem>Wishlist</ListItem>
  104. <ListItem>Terms</ListItem>
  105. </List>
  106. </Center>
  107. <Right>
  108. <Title>Contact</Title>
  109. <ContactItem>
  110. <Room style={{ marginRight: "10px" }} />
  111. 622 Dixie Path, South Tobinchester 98336
  112. </ContactItem>
  113. <ContactItem>
  114. <Phone style={{ marginRight: "10px" }} />
  115. +86 123 456 78
  116. </ContactItem>
  117. <ContactItem>
  118. <MailOutline style={{ marginRight: "10px" }} />
  119. contact@lama.io
  120. </ContactItem>
  121. <Payment src="https://th.bing.com/th/id/R.3b4d8519e606ab343119f65f4dbf6c94?rik=ScHdmWbvIvBl2g&pid=ImgRaw&r=0" />
  122. </Right>
  123. </Container>
  124. );
  125. };
  126. export default Footer;

完成效果:
image.png
至此,主页就做完了。此时的目录结构:

  1. .
  2. ├── README.md
  3. ├── package.json
  4. ├── public
  5. └── index.html
  6. ├── src
  7. ├── App.jsx
  8. ├── components
  9. ├── Announcement.jsx
  10. ├── Categories.jsx
  11. ├── CategoryItem.jsx
  12. ├── Footer.jsx
  13. ├── Navbar.jsx
  14. ├── Newsletter.jsx
  15. ├── Product.jsx
  16. ├── Products.jsx
  17. └── Slider.jsx
  18. ├── data.js
  19. ├── index.js
  20. └── pages
  21. └── Home.jsx
  22. └── yarn.lock

Product Page

在pages下新建ProductList.jsx和Product.jsx:

  1. import styled from "styled-components";
  2. import Navbar from "../components/Navbar";
  3. import Announcement from "../components/Announcement";
  4. import Products from "../components/Products";
  5. import Newsletter from "../components/Newsletter";
  6. import Footer from "../components/Footer";
  7. const Container = styled.div``;
  8. const Title = styled.h1`
  9. margin: 20px;
  10. `;
  11. const FilterContainer = styled.div`
  12. display: flex;
  13. justify-content: space-between;
  14. `;
  15. const Filter = styled.div`
  16. margin: 20px;
  17. `;
  18. const FilterText = styled.span`
  19. font-size: 20px;
  20. font-weight: 600;
  21. margin-right: 20px;
  22. `;
  23. const Select = styled.select`
  24. padding: 10px;
  25. margin-right: 20px;
  26. `;
  27. const Option = styled.option``;
  28. const ProductList = () => {
  29. return (
  30. <Container>
  31. <Announcement />
  32. <Navbar />
  33. <Title>Dresses</Title>
  34. <FilterContainer>
  35. <Filter>
  36. <FilterText>Filter Products:</FilterText>
  37. <Select>
  38. <Option disabled selected>
  39. Color
  40. </Option>
  41. <Option>White</Option>
  42. <Option>Black</Option>
  43. <Option>Red</Option>
  44. <Option>Blue</Option>
  45. <Option>Yellow</Option>
  46. <Option>Green</Option>
  47. </Select>
  48. <Select>
  49. <Option disabled selected>
  50. Size
  51. </Option>
  52. <Option>XS</Option>
  53. <Option>S</Option>
  54. <Option>M</Option>
  55. <Option>L</Option>
  56. <Option>XL</Option>
  57. </Select>
  58. </Filter>
  59. <Filter>
  60. <FilterText>Sort Products:</FilterText>
  61. <Select>
  62. <Option selected>Newest</Option>
  63. <Option>Price (asc)</Option>
  64. <Option>Price (desc)</Option>
  65. </Select>
  66. </Filter>
  67. </FilterContainer>
  68. <Products />
  69. <Newsletter />
  70. <Footer />
  71. </Container>
  72. );
  73. };
  74. export default ProductList;

效果:(在App.jsx中把Home组件换成相应组件进行预览)
image.png

  1. import { Add, Remove } from "@material-ui/icons";
  2. import styled from "styled-components";
  3. import Announcement from "../components/Announcement";
  4. import Footer from "../components/Footer";
  5. import Navbar from "../components/Navbar";
  6. import Newsletter from "../components/Newsletter";
  7. const Container = styled.div``;
  8. const Wrapper = styled.div`
  9. padding: 50px;
  10. display: flex;
  11. `;
  12. const ImgContainer = styled.div`
  13. flex: 1;
  14. `;
  15. const Image = styled.img`
  16. width: 100%;
  17. /* height: 90vh;
  18. object-fit: cover; */
  19. `;
  20. const InfoContainer = styled.div`
  21. flex: 1;
  22. padding: 0px 50px;
  23. `;
  24. const Title = styled.h1`
  25. font-weight: 200;
  26. `;
  27. const Desc = styled.p`
  28. margin: 20px 0px;
  29. `;
  30. const Price = styled.span`
  31. font-weight: 100;
  32. font-size: 40px;
  33. `;
  34. const FilterContainer = styled.div`
  35. width: 50%;
  36. margin: 30px 0px;
  37. display: flex;
  38. justify-content: space-between;
  39. `;
  40. const Filter = styled.div`
  41. display: flex;
  42. align-items: center;
  43. `;
  44. const FilterTitle = styled.span`
  45. font-size: 20px;
  46. font-weight: 200;
  47. `;
  48. const FilterColor = styled.div`
  49. width: 20px;
  50. height: 20px;
  51. border-radius: 50%;
  52. background-color: ${(props) => props.color};
  53. margin: 0px 5px;
  54. cursor: pointer;
  55. `;
  56. const FilterSize = styled.select`
  57. margin-left: 10px;
  58. padding: 5px;
  59. `;
  60. const FilterSizeOption = styled.option``;
  61. const AddContainer = styled.div`
  62. width: 50%;
  63. display: flex;
  64. align-items: center;
  65. justify-content: space-between;
  66. `;
  67. const AmountContainer = styled.div`
  68. display: flex;
  69. align-items: center;
  70. font-weight: 700;
  71. `;
  72. const Amount = styled.span`
  73. width: 30px;
  74. height: 30px;
  75. border-radius: 10px;
  76. border: 1px solid teal;
  77. display: flex;
  78. align-items: center;
  79. justify-content: center;
  80. margin: 0px 5px;
  81. `;
  82. const Button = styled.button`
  83. padding: 15px;
  84. border: 2px solid teal;
  85. background-color: white;
  86. cursor: pointer;
  87. font-weight: 500;
  88. &:hover {
  89. background-color: #f8f4f4;
  90. }
  91. `;
  92. const Product = () => {
  93. return (
  94. <Container>
  95. <Announcement />
  96. <Navbar />
  97. <Wrapper>
  98. <ImgContainer>
  99. <Image src="https://static.nike.com.hk/resources/product/DH7133-013/DH7133-013_BL1.png" />
  100. </ImgContainer>
  101. <InfoContainer>
  102. <Title>Denim Jumpsuit</Title>
  103. <Desc>
  104. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
  105. venenatis, dolor in finibus malesuada, lectus ipsum porta nunc, at
  106. iaculis arcu nisi sed mauris. Nulla fermentum vestibulum ex, eget
  107. tristique tortor pretium ut. Curabitur elit justo, consequat id
  108. condimentum ac, volutpat ornare.
  109. </Desc>
  110. <Price>$ 20</Price>
  111. <FilterContainer>
  112. <Filter>
  113. <FilterTitle>Color</FilterTitle>
  114. <FilterColor color="black" />
  115. <FilterColor color="darkblue" />
  116. <FilterColor color="gray" />
  117. </Filter>
  118. <Filter>
  119. <FilterTitle>Size</FilterTitle>
  120. <FilterSize>
  121. <FilterSizeOption>XS</FilterSizeOption>
  122. <FilterSizeOption>S</FilterSizeOption>
  123. <FilterSizeOption>M</FilterSizeOption>
  124. <FilterSizeOption>L</FilterSizeOption>
  125. <FilterSizeOption>XL</FilterSizeOption>
  126. </FilterSize>
  127. </Filter>
  128. </FilterContainer>
  129. <AddContainer>
  130. <AmountContainer>
  131. <Remove />
  132. <Amount>1</Amount>
  133. <Add />
  134. </AmountContainer>
  135. <Button>ADD TO CART</Button>
  136. </AddContainer>
  137. </InfoContainer>
  138. </Wrapper>
  139. <Newsletter />
  140. <Footer />
  141. </Container>
  142. );
  143. };
  144. export default Product;

效果:
image.png

Register and Login page

在pages文件夹下新建Register.jsx和Login.jsx:

  1. import styled from "styled-components";
  2. const Container = styled.div`
  3. width: 100vw;
  4. height: 100vh;
  5. background: linear-gradient(
  6. rgba(255, 255, 255, 0.5),
  7. rgba(255, 255, 255, 0.5)
  8. ),
  9. url("https://static.nike.com.hk/resources/template/kvFullNewModule1/20200722195508.jpg")
  10. center;
  11. background-size: cover;
  12. display: flex;
  13. align-items: center;
  14. justify-content: center;
  15. `;
  16. const Wrapper = styled.div`
  17. width: 40%;
  18. padding: 20px;
  19. background-color: white;
  20. `;
  21. const Title = styled.h1`
  22. font-size: 24px;
  23. font-weight: 300;
  24. `;
  25. const Form = styled.form`
  26. display: flex;
  27. flex-wrap: wrap;
  28. `;
  29. const Input = styled.input`
  30. flex: 1;
  31. min-width: 40%;
  32. margin: 20px 10px 0px 0px;
  33. padding: 10px;
  34. `;
  35. const Agreement = styled.span`
  36. font-size: 12px;
  37. margin: 20px 0px;
  38. `;
  39. const Button = styled.button`
  40. width: 40%;
  41. border: none;
  42. padding: 15px 20px;
  43. background-color: teal;
  44. color: white;
  45. cursor: pointer;
  46. `;
  47. const Register = () => {
  48. return (
  49. <Container>
  50. <Wrapper>
  51. <Title>CREATE AN ACCOUNT</Title>
  52. <Form>
  53. <Input placeholder="name" />
  54. <Input placeholder="last name" />
  55. <Input placeholder="username" />
  56. <Input placeholder="email" />
  57. <Input placeholder="password" />
  58. <Input placeholder="confirm password" />
  59. <Agreement>
  60. By creating an account, I consent to the processing of my personal
  61. data in accordance with the <b>PRIVACY POLICY</b>
  62. </Agreement>
  63. <Button>CREATE</Button>
  64. </Form>
  65. </Wrapper>
  66. </Container>
  67. );
  68. };
  69. export default Register;

效果:
image.png

  1. import styled from "styled-components";
  2. const Container = styled.div`
  3. width: 100vw;
  4. height: 100vh;
  5. background: linear-gradient(
  6. rgba(255, 255, 255, 0.5),
  7. rgba(255, 255, 255, 0.5)
  8. ),
  9. url("https://static.nike.com.hk/resources/template/kvFullNewModule1/20200722195508.jpg")
  10. center;
  11. background-size: cover;
  12. display: flex;
  13. align-items: center;
  14. justify-content: center;
  15. `;
  16. const Wrapper = styled.div`
  17. width: 25%;
  18. padding: 20px;
  19. background-color: white;
  20. `;
  21. const Title = styled.h1`
  22. font-size: 24px;
  23. font-weight: 300;
  24. `;
  25. const Form = styled.form`
  26. display: flex;
  27. flex-direction: column;
  28. `;
  29. const Input = styled.input`
  30. flex: 1;
  31. min-width: 40%;
  32. margin: 10px 0;
  33. padding: 10px;
  34. `;
  35. const Button = styled.button`
  36. width: 40%;
  37. border: none;
  38. padding: 15px 20px;
  39. background-color: teal;
  40. color: white;
  41. cursor: pointer;
  42. margin-bottom: 10px;
  43. `;
  44. const Link = styled.a`
  45. margin: 5px 0px;
  46. font-size: 12px;
  47. text-decoration: underline;
  48. cursor: pointer;
  49. `;
  50. const Login = () => {
  51. return (
  52. <Container>
  53. <Wrapper>
  54. <Title>SIGN IN</Title>
  55. <Form>
  56. <Input placeholder="username" />
  57. <Input placeholder="password" />
  58. <Button>LOGIN</Button>
  59. <Link>DO NOT YOU REMEMBER THE PASSWORD?</Link>
  60. <Link>CREATE A NEW ACCOUNT</Link>
  61. </Form>
  62. </Wrapper>
  63. </Container>
  64. );
  65. };
  66. export default Login;

效果:
image.png

Cart

在pages文件夹下新建Cart.jsx:

  1. import { Add, Remove } from "@material-ui/icons";
  2. import styled from "styled-components";
  3. import Announcement from "../components/Announcement";
  4. import Footer from "../components/Footer";
  5. import Navbar from "../components/Navbar";
  6. const Container = styled.div``;
  7. const Wrapper = styled.div`
  8. padding: 20px;
  9. `;
  10. const Title = styled.h1`
  11. font-weight: 300;
  12. text-align: center;
  13. `;
  14. const Top = styled.div`
  15. display: flex;
  16. align-items: center;
  17. justify-content: space-between;
  18. padding: 20px;
  19. `;
  20. const TopButton = styled.button`
  21. padding: 10px;
  22. font-weight: 600;
  23. cursor: pointer;
  24. border: ${(props) => props.type === "filled" && "none"};
  25. background-color: ${(props) =>
  26. props.type === "filled" ? "black" : "transparent"};
  27. color: ${(props) => props.type === "filled" && "white"};
  28. `;
  29. const TopTexts = styled.div``;
  30. const TopText = styled.span`
  31. text-decoration: underline;
  32. cursor: pointer;
  33. margin: 0px 10px;
  34. `;
  35. const Bottom = styled.div`
  36. display: flex;
  37. justify-content: space-between;
  38. `;
  39. const Info = styled.div`
  40. flex: 3;
  41. `;
  42. const Product = styled.div`
  43. display: flex;
  44. justify-content: space-between;
  45. `;
  46. const ProductDetail = styled.div`
  47. flex: 2;
  48. display: flex;
  49. `;
  50. const Image = styled.img`
  51. width: 200px;
  52. `;
  53. const Details = styled.div`
  54. padding: 20px;
  55. display: flex;
  56. flex-direction: column;
  57. justify-content: space-around;
  58. `;
  59. const ProductName = styled.span``;
  60. const ProductId = styled.span``;
  61. const ProductColor = styled.div`
  62. width: 20px;
  63. height: 20px;
  64. border-radius: 50%;
  65. background-color: ${(props) => props.color};
  66. `;
  67. const ProductSize = styled.span``;
  68. const PriceDetail = styled.div`
  69. flex: 1;
  70. display: flex;
  71. flex-direction: column;
  72. align-items: center;
  73. justify-content: center;
  74. `;
  75. const ProductAmountContainer = styled.div`
  76. display: flex;
  77. align-items: center;
  78. margin-bottom: 20px;
  79. `;
  80. const ProductAmount = styled.div`
  81. font-size: 24px;
  82. margin: 5px;
  83. `;
  84. const ProductPrice = styled.div`
  85. font-size: 30px;
  86. font-weight: 200;
  87. `;
  88. const Hr = styled.hr`
  89. background-color: #eee;
  90. border: none;
  91. height: 1px;
  92. `;
  93. const Summary = styled.div`
  94. flex: 1;
  95. border: 0.5px solid lightgray;
  96. border-radius: 10px;
  97. padding: 20px;
  98. height: 50vh;
  99. `;
  100. const SummaryTitle = styled.h1`
  101. font-weight: 200;
  102. `;
  103. const SummaryItem = styled.div`
  104. margin: 30px 0px;
  105. display: flex;
  106. justify-content: space-between;
  107. font-weight: ${(props) => props.type === "total" && "500"};
  108. font-size: ${(props) => props.type === "total" && "24px"};
  109. `;
  110. const SummaryItemText = styled.span``;
  111. const SummaryItemPrice = styled.span``;
  112. const Button = styled.button`
  113. width: 100%;
  114. padding: 10px;
  115. background-color: black;
  116. color: white;
  117. font-weight: 600;
  118. `;
  119. const Cart = () => {
  120. return (
  121. <Container>
  122. <Navbar />
  123. <Announcement />
  124. <Wrapper>
  125. <Title>YOUR BAG</Title>
  126. <Top>
  127. <TopButton>CONTINUE SHOPPING</TopButton>
  128. <TopTexts>
  129. <TopText>Shopping Bag(2)</TopText>
  130. <TopText>Your Wishlist (0)</TopText>
  131. </TopTexts>
  132. <TopButton type="filled">CHECKOUT NOW</TopButton>
  133. </Top>
  134. <Bottom>
  135. <Info>
  136. <Product>
  137. <ProductDetail>
  138. <Image src="https://hips.hearstapps.com/vader-prod.s3.amazonaws.com/1614188818-TD1MTHU_SHOE_ANGLE_GLOBAL_MENS_TREE_DASHERS_THUNDER_b01b1013-cd8d-48e7-bed9-52db26515dc4.png?crop=1xw:1.00xh;center,top&resize=480%3A%2A" />
  139. <Details>
  140. <ProductName>
  141. <b>Product:</b> JESSIE THUNDER SHOES
  142. </ProductName>
  143. <ProductId>
  144. <b>ID:</b> 93813718293
  145. </ProductId>
  146. <ProductColor color="black" />
  147. <ProductSize>
  148. <b>Size:</b> 37.5
  149. </ProductSize>
  150. </Details>
  151. </ProductDetail>
  152. <PriceDetail>
  153. <ProductAmountContainer>
  154. <Add />
  155. <ProductAmount>2</ProductAmount>
  156. <Remove />
  157. </ProductAmountContainer>
  158. <ProductPrice>$ 30</ProductPrice>
  159. </PriceDetail>
  160. </Product>
  161. <Hr />
  162. <Product>
  163. <ProductDetail>
  164. <Image src="https://i.pinimg.com/originals/2d/af/f8/2daff8e0823e51dd752704a47d5b795c.png" />
  165. <Details>
  166. <ProductName>
  167. <b>Product:</b> HAKURA T-SHIRT
  168. </ProductName>
  169. <ProductId>
  170. <b>ID:</b> 93813718293
  171. </ProductId>
  172. <ProductColor color="gray" />
  173. <ProductSize>
  174. <b>Size:</b> M
  175. </ProductSize>
  176. </Details>
  177. </ProductDetail>
  178. <PriceDetail>
  179. <ProductAmountContainer>
  180. <Add />
  181. <ProductAmount>1</ProductAmount>
  182. <Remove />
  183. </ProductAmountContainer>
  184. <ProductPrice>$ 20</ProductPrice>
  185. </PriceDetail>
  186. </Product>
  187. </Info>
  188. <Summary>
  189. <SummaryTitle>ORDER SUMMARY</SummaryTitle>
  190. <SummaryItem>
  191. <SummaryItemText>Subtotal</SummaryItemText>
  192. <SummaryItemPrice>$ 80</SummaryItemPrice>
  193. </SummaryItem>
  194. <SummaryItem>
  195. <SummaryItemText>Estimated Shipping</SummaryItemText>
  196. <SummaryItemPrice>$ 5.90</SummaryItemPrice>
  197. </SummaryItem>
  198. <SummaryItem>
  199. <SummaryItemText>Shipping Discount</SummaryItemText>
  200. <SummaryItemPrice>$ -5.90</SummaryItemPrice>
  201. </SummaryItem>
  202. <SummaryItem type="total">
  203. <SummaryItemText>Total</SummaryItemText>
  204. <SummaryItemPrice>$ 80</SummaryItemPrice>
  205. </SummaryItem>
  206. <Button>CHECKOUT NOW</Button>
  207. </Summary>
  208. </Bottom>
  209. </Wrapper>
  210. <Footer />
  211. </Container>
  212. );
  213. };
  214. export default Cart;

效果:
image.png

移动端适配

src文件夹下新建responsive.js:

  1. import { css } from "styled-components";
  2. export const mobile = (props) => {
  3. return css`
  4. @media only screen and (max-width: 380px) {
  5. ${props}
  6. }
  7. `;
  8. };
  9. // export const tablet = (props) =>{
  10. // return css`
  11. // @media only screen and (max-width: 380px) {
  12. // ${props}
  13. // }
  14. // `
  15. // }

然后对各个页面进行适配

  1. ...
  2. import { mobile } from "../responsive";
  3. ...
  4. const Product = styled.div`
  5. display: flex;
  6. justify-content: space-between;
  7. ${mobile({ flexDirection: "column" })}
  8. `;
  9. ...

至此,所有页面就都完成了。此时的目录结构:

  1. .
  2. ├── README.md
  3. ├── package.json
  4. ├── public
  5. └── index.html
  6. ├── src
  7. ├── App.jsx
  8. ├── components
  9. ├── Announcement.jsx
  10. ├── Categories.jsx
  11. ├── CategoryItem.jsx
  12. ├── Footer.jsx
  13. ├── Navbar.jsx
  14. ├── Newsletter.jsx
  15. ├── Product.jsx
  16. ├── Products.jsx
  17. └── Slider.jsx
  18. ├── data.js
  19. ├── index.js
  20. ├── pages
  21. ├── Cart.jsx
  22. ├── Home.jsx
  23. ├── Login.jsx
  24. ├── Product.jsx
  25. ├── ProductList.jsx
  26. └── Register.jsx
  27. └── responsive.js
  28. └── yarn.lock