(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!

相关推荐
小曲曲1 小时前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS3 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年7 小时前
react中useMemo的使用场景
前端·react.js·前端框架