导航栏

导航栏首先简单分析一下这个导航栏的组成,可以划分两部分:logo和导航链接区域。

根据这个划分,,我们可以开始写组件的DOM结构和定义样式名字:

import React from 'react';
import styles from './styles.scss';

class TopNav extends React.Component {
  render() {
    return (
      <div className={styles.container}>
        <div className={styles.logo}></div>
        <div className={styles.nav}>
          <nav>
            <ul>
              <li><button>成为房东</button></li>
              <li><button>帮助</button></li>
              <li><button>注册</button></li>
              <li><button>登录</button></li>
            </ul>
          </nav>
        </div>
      </div>
    );
  }
}

export default TopNav;

刷新页面,此时,你的页面大概长成这样:

现在,我们可以开始添加样式到./TopNav/styles.scss:

.container {
    font-family: Circular,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,sans-serif;
}

.logo {
    background-image: url("./logo.svg");
    background-repeat: no-repeat;
    background-position: center center;
    height: 34px;
    width: 34px;
    display: inline-block;
    vertical-align: middle;
}

.nav {
    display: inline-block;
    float: right;
    ul {
        padding: 0;
    }

    li {
        display: inline-block;
        list-style: none;

        button {
            background: transparent;
            border: none;
            font-size: 14px;
            color: #484848;
            margin: 0 16px;
            cursor: pointer;
            padding: 0 0 5px 0;
            -webkit-font-smoothing: antialiased;

            &:hover {
                border-bottom: 2px solid #484848;
            }
        }
    }
}

这样,NavTop的基本样式和DOM结构就完成了,到目前为止,我们通过React实现了一个纯展示组件(Presentational Component),它没有任何的状态(state)和行为。(请仔细阅读《Presentational and Container Components》

弹出框
导航栏的第二步,是给链接添加PopupMenu弹出框,并通过组件的状态来控制弹出框的toggle。

PopupMenu它是一个独立的组件,这个时候,我们可以考虑在NavTop目录下新建一个PopupMenu.jsx来构建该组件。

在开始写组件之前,我们先引入classnames来帮助管理class的状态(class的组合),因为,我们使用了CSS Modules,所以需要使用它的bind版本 ,classnames bind version for css modules

yarn add classnames

同理,我们可以根据设计给出PopupMenu的DOM结构:

import React from 'react';
import classNames from 'classnames/bind';
import styles from './PopupMenu.scss';

const cx = classNames.bind(styles);

class PopupMenu extends React.PureComponent {

  render() {
    return (
      <div className={cx('container', this.props.className)}>
        <ul>
          {
            this.props.links.map(link => (<li key={`pop-mnu-li${link.id}`}><a href={link.href}><div>{link.title}</div></a></li>))
          }
        </ul>
      </div>);
  }
}

PopupMenu.propTypes = {
  links: React.PropTypes.arrayOf(React.PropTypes.object)
}

export default PopupMenu;

这里使用了PureComponent而不是Component,原因是,我们知道PopupMenu组件是一个Presentational组件,到这里了解PureComponent是什么以及Component渲染性能

然后,我们需要修改TopNav/index.jsx来控制PopupMenu的显示:

import React from 'react';
import classNames from 'classnames/bind';
import styles from './styles.scss';
import PopupMenu from './PopupMenu';

const cx = classNames.bind(styles);

class TopNav extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showLandlordMenu: false
    };
  }

  toggleShowLandlordMenu = (toggleOff = false) => {
    this.setState({
      showLandlordMenu: !toggleOff && !this.state.showLandlordMenu
    });
  }

  render() {
    return (
      <div className={cx('container')}>
        <div className={cx('logo')}></div>
        <div className={cx('nav')}>
          <nav>
            <ul>
              <li>
                <button onClick={() =>this.toggleShowLandlordMenu()} onBlur={() => this.toggleShowLandlordMenu(true)}>成为房东</button>
                <PopupMenu
                  className={cx('landlord-menu', { 'hidden': !this.state.showLandlordMenu })}
                  links={[{ id: 1, title: '出租房源', href: '' }, { id: 2, title: '展开体验', href: '' }]}
                />
              </li>
              <li><button>帮助</button></li>
              <li><button>注册</button></li>
              <li><button>登录</button></li>
            </ul>
          </nav>
        </div>
      </div>
    );
  }
}

export default TopNav;

onBlur的作用是当用户点击到PopupMenu之外的部分时,可以让组件消失。

这个时候,可以先不管PopupMenu的样式,直接测试弹出和消失的功能是否工作。

最后添加PopupMenu组件的样式:

.container {
    background-color: #ffffff;
    border: 1px solid #DBDBDB;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
    min-width: 282px;
    max-width: 460px;
    min-height: 60px;
    right: 0px;
    top: 40px;

    li {
        display: block;
        color: #484848;

        a {
            display: block;
            text-decoration: none;
            color: #767676;
            font-family: Circular,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,sans-serif;
            word-wrap: break-word;
            font-size: 15px;
            line-height: 18px;
            letter-spacing: 0.2px;
            padding: 0 16px;

            div {
                border-bottom: 1px solid transparent;
                padding: 16px 0;
                border-color: #F2F2F2;

                &:hover {
                    border-color: #767676;
                }
            }
        }
    }
}

.container:after, .container:before {
    content: '';
    display: block;
    position: absolute;
    left: 226px;
    width: 0;
    height: 0;
    border-style: solid;
}

.container:after {
    top: -22px;
    border-color: transparent transparent white transparent;
    border-width: 11px;
}

.container:before {
    top: -23px;
    border-color: transparent transparent #DBDBDB transparent;
    border-width: 11px;
}

代码

你可以在prepare-env分支查看到当前步骤所有的代码

git clone [email protected]:ThoughtWorksWuhanUI/react-zero-to-one.git
git checkout nav-top

本页编辑:Benwei

results matching ""

    No results matching ""