(03)Header 组件开发——③ Header 组件布局 | React.js 项目实战:PC 端“简书”开发

复制代码
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

🔗"简书"官网

❗️CSS 声明顺序(在正式开始编写项目的 CSS 之前,啰嗦几句------请尽量养成好的习惯,相关的属性声明应当归为一组,以后的工作定能左右逢源):

  • Position 定位
  • Box model 盒模型
  • Typography 印刷
  • Visual 视觉

💡例如:

css 复制代码
.declaration-order {
  /* Positioning 定位 */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;


  /* Box-model 盒模型 */
  display: block;
  float: right;
  width: 100px;
  height: 100px;


  /* Typography 印刷 */
  font: normal 13px "Helvetica Neue", sans-serif;
  line-height: 1.5;
  color: #333;
  text-align: center;

  /* Visual 视觉 */
  background-color: #f5f5f5;
  border: 1px solid #e5e5e5;
  border-radius: 3px;

  /* Misc 杂项 */
  opacity: 1;
}

由于"定位"(Position)可以从正常的文档流中移除元素,并且还能覆盖"盒模型"(Box model)相关的样式,因此排在首位。

"盒模型"(Box model)排在第二位,因为它决定了组件的尺寸和位置。

其他属性只是影响组件的"内部",或者是不影响前两组属性,因此排在后面。

1️⃣在 header 目录下创建一个 style.js 文件,用于写 header 部分的"样式组件":

2️⃣编写下图红框部分的 JSX 和 styled-components:

  • JSX------打开 header 目录下的 index.js 文件:
jsx 复制代码
import React, {Component} from "react";

// ❗️2️⃣-②:从当前目录下的 style.js 中引入各个定义好的"样式组件";
import {
  HeaderWrapper,
  Logo,
  
} from "./style";

class Header extends Component {
  render() {
    
    // ❗️❗️❗️2️⃣-①:将"标签"拆分成一个个的"样式组件";
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

      </HeaderWrapper>
    )
  }
}

export default Header;
  • styled-components------打开 header 目录下的 style.js 文件:
javascript 复制代码
import styled from "styled-components"; /*
																				2️⃣-③:首先,从 styled-components 中
                                        引入 styled 方法;
                                         */

// 2️⃣-④:整个 <header> 部分的样式可以写成一个大的 Header 组件;
export const HeaderWrapper = styled.header`
  box-sizing: content-box;

  padding: 0 20px;
  height: 56px;
  line-height: 56px; /*
										 ❗️header 的高度可以写一个固定的高度,也可以由内容去撑开。
										 但当前的场景下,写一个固定的高度比较合适。因为由内容去撑开的话,
										 很容易导致上下的缝隙不太合适。
										 如果写一个固定的 height,然后 line-height 等于这个 height,
										 那很容易就实现里边的整个元素都处于这个区域的正中心!
											*/

										 /*
										 ❗️但要注意:由于继承性,它的子元素也会去继承 line-height 等于
										 56px。故,若子元素有特别需要,要单独设置 line-height 的值!
											*/
 
  border-bottom: 1px solid #eee;
  
  &:after {
    content: "";
    display: block;
    clear: both;
  } /*
		❗️给 header 清除浮动,当然也可以在 JSX 中,给 HeaderWrapper 加一个"类"clearfix,
    用"通用样式"清除浮动。之所以加在这里,主要是为了"好看"。
		 */
`;

// 2️⃣-⑤:"简书"这个 logo 可以写成一个可点击的(a 标签)Logo 组件;
export const Logo = styled.a.attrs({
	href: "/"
})`
  float: left;
	height: 56px;
  & > img {
    height: 50px; /*
									❗️由于 header 一开始就设置了 height=line-height,
									故 logo 图片的高度设置好后,宽度就自适应了。
									 */
  }
`;

返回页面查看效果:

2 样式组件------Navbar 相关

3️⃣编写下图红框部分的 JSX 和 styled-components:

  • JSX------打开 header 目录下的 index.js 文件:
jsx 复制代码
import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
} from "./style";

class Header extends Component {
  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
      </HeaderWrapper>
    )
  }
}

export default Header;
  • styled-components------打开 header 目录下的 style.js 文件:
javascript 复制代码
import styled from "styled-components";  

export const HeaderWrapper = styled.header`
  box-sizing: content-box;
	
	padding: 0 20px;
  height: 56px;

  line-height: 56px;
  
	border-bottom: 1px solid #eee;
  
  &:after {
    content: "";
    display: block;
    clear: both;
  }
`;

export const Logo = styled.a.attrs({ 
  href: "/"
})`
  float: left;
	height: 56px;
  & > img {
    height: 50px;  
  }
`;


// 🚀Navbar 相关~
export const Navbar = styled.ul`
  float: left;
`;

export const ItemList = styled.li`
  float: left;
  padding: 0 4px; /*
									❗️给 li 加点 padding,可以使相邻的两个可点击的按钮中间有一个缝。
					        当然,要实现这个功能,给 li 加 margin,或给 a 加 margin 也可以!
									 */

  &.active > a{
    color: #e86f5e;  
  }

  &.active > a:hover {
    background-color: #fff;
  }
`;

export const LinkList = styled.a`
  display: block;
  padding: 0 10px;
  
  font-size: 17px;
  line-height: 56px;
  color: #333;

  &:hover {
    background-color: #eee;
  }

`;

返回页面查看效果:

3 样式组件------SearchArea 相关

4️⃣编写下图红框部分的 JSX 和 styled-components:

  • JSX------打开 header 目录下的 index.js 文件:
jsx 复制代码
import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
  SearchArea,
  SearchInput,
  SearchPanel,
  PanelTitle,
  PanelChange,
  PanelLabels,
  LabelLink,
  
} from "./style";

class Header extends Component {
  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
      
        <SearchArea>
          <SearchInput />
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
              <PanelChange>
                <span className="iconfont icon-refresh">&#xe65f;</span>
                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              <LabelLink href="/">
                区块链
              </LabelLink>
              <LabelLink href="/">
                故事
              </LabelLink>
              <LabelLink href="/">
                小程序
              </LabelLink>
              <LabelLink href="/">
                前端一万小时
              </LabelLink>
            </PanelLabels>
          </SearchPanel>
        </SearchArea>
      </HeaderWrapper>
    )
  }
}

export default Header;
  • styled-components------打开 header 目录下的 style.js 文件:
javascript 复制代码
import styled from "styled-components";  

export const HeaderWrapper = styled.header`
  box-sizing: content-box;
	padding: 0 20px;
  height: 56px;
  
  line-height: 56px;
  
  border-bottom: 1px solid #eee;
  
  &:after {
    content: "";
    display: block;
    clear: both;
  }
`;

export const Logo = styled.a.attrs({ 
  href: "/"
})`
  float: left;
	height: 56px;
  & > img {
    height: 50px;  
  }
`;


// 🚀Navbar 相关~
export const Navbar = styled.ul`
  float: left;

`;

export const ItemList = styled.li`
  float: left;
  padding: 0 4px;

  &.active > a{
    color: #e86f5e;  
  }

  &.active > a:hover {
    background-color: #fff;
  }
`;

export const LinkList = styled.a`
  display: block;
  padding: 0 10px;
  
  font-size: 17px;
  line-height: 56px;
  color: #333;
  

  &:hover {
    background-color: #eee;
  }

`;


// 🚀SearchArea 相关~
export const SearchArea = styled.div`
  position: relative;

  float: left;  /* ❗️先成为浮动元素,变小。 */
  margin-left: 30px;

  .icon-search {
    position: absolute;
    top: 10px;
		right: 10px;

    width: 32px;
    height: 32px;  

    color: #aaa;
    line-height: 32px; /*
											 ❗️对图标的样式进行重置,height=line-height 实现字体图标
											 在设置的盒子里"垂直居中"。
											  */
    text-align: center; /* ❗️使图标在其盒子里水平居中。 */

    border-radius: 50%;
  }
`;

export const SearchInput = styled.input.attrs({
  placeholder: "搜索"
})`
  width: 200px;
  padding: 0 20px;
  
  font-size: 15px;
  line-height: 36px;

  border: none; /*
								❗️一般来说,input 被点击的时候,会出现一个蓝色的框,
								这里我们不需要这个默认样式。
								 */
  background-color: #eee;
  border-radius: 18px;
  outline: none;

  transition: all .3s; /* ❗️添加"过渡"动画。 */

  &:focus {
    width: 240px;
  }

  &:focus~div {
    display: block;
  }

  &:focus + .icon-search {
    color: #fff;
    background-color: #969696;
  }
`;

export const SearchPanel = styled.div`
  position: absolute;
  z-index: 1;

  width: 250px;
  padding: 16px;

  line-height: 1; /* ❗️重置行高。 */

  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);  
  /*
  ❗️box-shadow 语法规则:
  "X 偏移量 | Y 偏移量 | 阴影模糊面积 | 阴影扩散半径 | 阴影颜色"
   */

  display: none;

  &::before { /* ❗️做出向上的"三角"。 */
    content: "";
    display: block;
    position: absolute;
    top: -5px;
    left: 30px;
    width: 14px;
    height: 14px;
    background-color: #fff;
    transform: rotateZ(45deg);
    box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
  }


`;

export const PanelTitle = styled.h3`
  color: #888;
  font-weight: normal;
`;

export const PanelChange = styled.div`
  float: right;
  
  color: #888;
  font-size: 13px;
  font-weight: normal;

  cursor: pointer; /* ❗️做一个"手"的形状。 */
`;

export const PanelLabels = styled.div`
  margin-top: 10px;
`;

export const LabelLink = styled.a`
  float: left;
  
  padding: 2px 4px;
  margin: 10px 10px 0 0;

	color: #888;

  border: 1px solid #ccc;
  border-radius: 4px;
  

  &:hover {
    border-color: #888;
  }
`;

返回页面查看效果: ❗️整体效果没有问题,但请将进度条拉到最后几秒(0:22),这里是有问题的:

按照"简书"官网的交互逻辑,除了点击 input 框,下边的 panel 会出现外。当鼠标滑动至 panel 区域后,鼠标做任何点击,panel 区域也都不会隐藏(我们现在实现的效果会隐藏,因为我们只是在 input 上设置了 :focus,当点击 panel 区域时,input 框就"失焦"了,"失焦"的后果就是 panel 区域再次隐藏),只有当鼠标离开 panel 区域后,panel 才会隐藏!

对于视频中官网这个效果,我们直接用 CSS 中的"伪类选择器"也能很轻松地实现。继续在 header 目录下的 style.js 中,定位到 SearchPanel 样式组件:

javascript 复制代码
export const SearchPanel = styled.div`
  position: absolute;
  z-index: 1;

  width: 250px;
  padding: 16px;

  line-height: 1; /* ❗️重置行高。 */

  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);  

  display: none;

  &::before { 
    content: "";
    display: block;
    position: absolute;
    top: -5px;
    left: 30px;
    width: 14px;
    height: 14px;
    background-color: #fff;
    transform: rotateZ(45deg);
    box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
  }

  &:hover { /*
						❗️直接在里加一个 :hover,当这个区域出现后,只要它处于 hover 状态,
						则显示为 block。
						 */
    display: block;
  }
`;

返回页面查看效果:

4 样式组件------Extra 相关

5️⃣编写下图红框部分的 JSX 和 styled-components:

  • JSX------打开 header 目录下的 index.js 文件:
jsx 复制代码
import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
  SearchArea,
  SearchInput,
  SearchPanel,
  PanelTitle,
  PanelChange,
  PanelLabels,
  LabelLink,
  
  Extra,
  ExtraLink
  
} from "./style";

class Header extends Component {
  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
        <SearchArea>
          <SearchInput />
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
              <PanelChange>
                <span className="iconfont icon-refresh">&#xe65f;</span>
                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              <LabelLink href="/">
                区块链
              </LabelLink>
              <LabelLink href="/">
                故事
              </LabelLink>
              <LabelLink href="/">
                小程序
              </LabelLink>
              <LabelLink href="/">
                前端一万小时
              </LabelLink>
            </PanelLabels>
          </SearchPanel>
        </SearchArea>
      
      
      	<Extra>
      		<span className="iconfont icon-textsize" >&#xe739;</span>
          <ExtraLink className="login" href="/">
            登录
          </ExtraLink>
          <ExtraLink className="register" href="/">
            注册
          </ExtraLink> 
      
          <ExtraLink className="writing" href="/">
            <span className="iconfont icon-pen">&#xe600;</span>
            写文章
          </ExtraLink>     
      	</Extra>
      </HeaderWrapper>
    )
  }
}

export default Header;
  • styled-components------打开 header 目录下的 style.js 文件:
javascript 复制代码
import styled from "styled-components";  

export const HeaderWrapper = styled.header`
  box-sizing: content-box;
  padding: 0 20px;
  height: 56px;
  
  line-height: 56px;
  
  border-bottom: 1px solid #eee;
  
  &:after {
    content: "";
    display: block;
    clear: both;
  }
`;

export const Logo = styled.a.attrs({ 
  href: "/"
})`
  float: left;
	height: 56px;
  & > img {
    height: 50px;  
  }
`;


// 🚀Navbar 相关~
export const Navbar = styled.ul`
  float: left;

`;

export const ItemList = styled.li`
  float: left;
  padding: 0 4px;

  &.active > a{
    color: #e86f5e;  
  }

  &.active > a:hover {
    background-color: #fff;
  }
`;

export const LinkList = styled.a`
  display: block;
  padding: 0 10px;
  
  font-size: 17px;
  line-height: 56px;
  color: #333;
  

  &:hover {
    background-color: #eee;
  }

`;


// 🚀SearchArea 相关~
export const SearchArea = styled.div`
  position: relative;

  float: left;  
  margin-left: 30px;

  .icon-search {
    position: absolute;
    top: 10px;
    right: 10px;

    width: 32px;
    height: 32px;  

    color: #aaa;
    line-height: 32px;  
    text-align: center;  

    border-radius: 50%;
  }
`;

export const SearchInput = styled.input.attrs({
  placeholder: "搜索"
})`
  width: 200px;
  padding: 0 20px;
  
  font-size: 15px;
  line-height: 36px;

  border: none;  
  background-color: #eee;
  border-radius: 18px;
  outline: none;

  transition: all .3s;  

  &:focus {
    width: 240px;
  }

  &:focus~div {
    display: block;
  }

  &:focus + .icon-search {
    color: #fff;
    background-color: #969696;
  }
`;

export const SearchPanel = styled.div`
  position: absolute;
  z-index: 1;

  width: 250px;
  padding: 16px;

  line-height: 1;  

  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 1px 4px 2px rgba(0,0,0,0.1);  

  display: none;

  &::before {  
    content: "";
    display: block;
    position: absolute;
    top: -5px;
    left: 30px;
    width: 14px;
    height: 14px;
    background-color: #fff;
    transform: rotateZ(45deg);
    box-shadow: -2px -2px 2px -2px rgba(0,0,0,0.1);
  }

  &:hover {
    display: block;
  }


`;

export const PanelTitle = styled.h3`
  color: #888;
  font-weight: normal;
`;

export const PanelChange = styled.div`
  float: right;
  
  color: #888;
  font-size: 13px;
  font-weight: normal;

  cursor: pointer;  
`;

export const PanelLabels = styled.div`
  margin-top: 10px;
`;

export const LabelLink = styled.a`
  float: left;
  
  padding: 2px 4px;
  margin: 10px 10px 0 0;

  color: #888;

  border: 1px solid #ccc;
  border-radius: 4px;
  

  &:hover {
    border-color: #888;
  }
`;



// 🚀Extra 相关~
export const Extra = styled.div`
  float: right;

  .icon-textsize {
    float: left;
    font-size: 25px;
    color: #888;
    
    cursor: pointer;
  }

`;

export const ExtraLink = styled.a`
  float: left;
  padding: 10px 20px;
  margin: 10px 0 0 20px;
  font-size: 15px;
  line-height: 1;


  &.login {
    color: #888;
    background-color: #fff;
  }

  &.register {
    color: #e56e5d
    border: 1px solid #e56e5d
    border-radius: 30px;
  }

  &.writing {
    color: #fff;
    background-color: #e56e5d;
    border-radius: 30px;
    
  }
`;

返回页面查看效果:

祝好,qdywxs ♥ you!

相关推荐
Z_Wonderful11 分钟前
在 Next.js 中,使用 [id] 或 public 作为文件夹或文件名是两种完全不同的概念,分别对应 动态路由 和 静态资源托管
javascript·网络·chrome
咬人喵喵17 分钟前
E2.COOL 平台深度解析:从特效分类到实战操作指南
前端·编辑器·svg
RisunJan1 小时前
Linux命令-named-checkzone
linux·前端
小陈工1 小时前
Python Web开发入门(十):数据库迁移与版本管理——让数据库变更可控可回滚
前端·数据库·人工智能·python·sql·云原生·架构
吹晚风吧1 小时前
解决vite打包,base配置前缀,nginx的dist包找不到资源
服务器·前端·nginx
weixin199701080162 小时前
《施耐德商品详情页前端性能优化实战》
前端·性能优化
不想上班只想要钱2 小时前
模板里 item.xxx 报错 ,报 item的类型为未知
前端·vue
Irene19912 小时前
推荐 React 开发需要在 VS Code 中安装的插件
react.js
妖萌妹儿2 小时前
postman怎么做参数化批量测试,测试不同输入组合
开发语言·javascript·postman
阿琳a_2 小时前
在github上部署个人的vitepress文档网站
前端·vue.js·github·网站搭建·cesium