React项目实战之租房app项目(十)个人中心模块&登录访问控制&项目打包部署上线

前言

一、个人中心模块

1.1 个人中心模块效果图

1.2 个人中心模块基础布局

在src/pages/Profile/index.js中添加如下代码:

render() {
    return (
      <div className={styles.root}>
        {/* 个人信息 */}
        <div className={styles.title}>
          <img
            className={styles.bg}
            src={BASE_URL + '/img/profile/bg.png'}
            alt="背景图"
          />
          <div className={styles.info}>
            <div className={styles.myIcon}>
              <img
                className={styles.avatar}
                src={avatar || DEFAULT_AVATAR}
                alt="icon"
              />
            </div>
            <div className={styles.user}>
              <div className={styles.name}>{nickname || '游客'}</div>
              {/* 登录后展示: */}
              {isLogin ? (
                <>
                  <div className={styles.auth}>
                    <span onClick={this.logout}>退出</span>
                  </div>
                  <div className={styles.edit}>
                    编辑个人资料
                    <span className={styles.arrow}>
                      <i className="iconfont icon-arrow" />
                    </span>
                  </div>
                </>
              ) : (
                <div className={styles.edit}>
                  <Button
                    type="primary"
                    size="small"
                    inline
                    onClick={() => history.push('/login')}
                  >
                    去登录
                  </Button>
                </div>
              )}
              {/* 未登录展示: */}
            </div>
          </div>
        </div>
        {/* 九宫格菜单 */}
        <Grid
          data={menus}
          // 列数
          columnNum={3}
          // 不需要分割线
          hasLine={false}
          // 渲染每一项
          renderItem={item =>
            item.to ? (
              <Link to={item.to}>
                <div className={styles.menuItem}>
                  <i className={`iconfont ${item.iconfont}`} />
                  <span>{item.name}</span>
                </div>
              </Link>
            ) : (
              <div className={styles.menuItem}>
                <i className={`iconfont ${item.iconfont}`} />
                <span>{item.name}</span>
              </div>
            )
          }
        />
        {/* 加入我们 */}
        <div className={styles.ad}>
          <img src={BASE_URL + '/img/profile/join.png'} alt="" />
        </div>
      </div>
    )
  }

1.2 个人中心模块功能实现

1.2.1 判断用户是否登陆

实现步骤:

1、在state中添加两个状态:isLogin(是否登录)和userInfo(用户信息)
2、从utils中导入isAuth(登录状态)、getToken(获取token)
3、创建方法getUserInfo,用户来获取个人资料
4、在方法中,通过isLogin判断用户是否登录
5、如果没有登录,则不发送请求,渲染未登录信息
6、如果已登录,就根据接口发送请求,获取用户个人资料
7、渲染个人资料数据

代码示例:
在src/pages/Profile/index.js中添加如下代码:

import { BASE_URL, isAuth, getToken, API } from '../../utils'

export default class Profile extends Component {
  state = {
    // 是否登录
    isLogin: isAuth(),
    // 用户信息
    userInfo: {
      avatar: '',
      nickname: ''
    }
  }

  // 注意:不要忘了在进入页面时调用方法 !
  componentDidMount() {
    this.getUserInfo()
  }

  async getUserInfo() {
    if (!this.state.isLogin) {
      // 未登录
      return
    }

    // 发送请求,获取个人资料
    const res = await API.get('/user', {
      headers: {
        authorization: getToken()
      }
    })

    if (res.data.status === 200) {
      const { avatar, nickname } = res.data.body
      this.setState({
        userInfo: {
          avatar: BASE_URL + avatar,
          nickname
        }
      })
    }
  }

  render() {
    const { history } = this.props

    const {
      isLogin,
      userInfo: { avatar, nickname }
    } = this.state

    return (
      <div className={styles.root}>
        {/* 个人信息 */}
        <div className={styles.title}>
          <img
            className={styles.bg}
            src={BASE_URL + '/img/profile/bg.png'}
            alt="背景图"
          />
          <div className={styles.info}>
            <div className={styles.myIcon}>
              <img
                className={styles.avatar}
                src={avatar || DEFAULT_AVATAR}
                alt="icon"
              />
            </div>
            <div className={styles.user}>
              <div className={styles.name}>{nickname || '游客'}</div>
              {/* 登录后展示: */}
              {isLogin ? (
                <>
                  <div className={styles.auth}>
                    <span onClick={this.logout}>退出</span>
                  </div>
                  <div className={styles.edit}>
                    编辑个人资料
                    <span className={styles.arrow}>
                      <i className="iconfont icon-arrow" />
                    </span>
                  </div>
                </>
              ) : (
                <div className={styles.edit}>
                  <Button
                    type="primary"
                    size="small"
                    inline
                    onClick={() => history.push('/login')}
                  >
                    去登录
                  </Button>
                </div>
              )}

              {/* 未登录展示: */}
            </div>
          </div>
        </div>
        {/* 九宫格菜单 */}
        <Grid
          data={menus}
          columnNum={3}
          hasLine={false}
          renderItem={item =>
            item.to ? (
              <Link to={item.to}>
                <div className={styles.menuItem}>
                  <i className={`iconfont ${item.iconfont}`} />
                  <span>{item.name}</span>
                </div>
              </Link>
            ) : (
              <div className={styles.menuItem}>
                <i className={`iconfont ${item.iconfont}`} />
                <span>{item.name}</span>
              </div>
            )
          }
        />


        {/* 加入我们 */}
        <div className={styles.ad}>
          <img src={BASE_URL + '/img/profile/join.png'} alt="" />
        </div>
      </div>
    )
  }
}

1.2.2 退出功能

实现步骤:

1、点击退出按钮,弹出对话框,提示是否确定退出
2、给退出按钮绑定点击事件,创建方法logout作为事件处理函数
3、导入Modal对话框组件(弹出模态框)
4、在方法中,拷贝Modal组件文档中确认对话框的示例代码
5、修改对话框的文字提示
6、在退出按钮的事件处理程序中,先调用退出接口(让服务器端退出),再移除本地token(本地退出)
7、把登录状态isLogin设置为false
8、清空用户状态对象

代码示例:
在src/pages/Profile/index.js中添加如下代码:

import {..., Modal } from 'antd-mobile'

const alert = Modal.alert 

 // 退出
  logout = () => {
    alert('提示', '是否确定退出?', [
      { text: '取消' },
      {
        text: '退出',
        onPress: async () => {
          // 调用退出接口
          await API.post('/user/logout', null, {
            headers: {
              authorization: getToken()
            }
          })

          // 移除本地token
          removeToken()

          // 处理状态
          this.setState({
            isLogin: false,
            userInfo: {
              avatar: '',
              nickname: ''
            }
          })
        }
      }
    ])
  }

二、登录访问控制

2.1 使用axios拦截器统一处理token

实现步骤:

1、在api.js 中,添加请求拦截器 (API.interceptors.request.user())
2、获取到当前请求的接口路径(url)
3、判断接口路径,是否以/user 开头,并且不是登录或注册接口(只给需要的接口添加请求头)
4、如果是,就添加请求头Authorization
5、添加响应拦截器 (API.interceptors.response.use())
6、判断返回值中的状态码
7、如果是400,标示token超时或异常,直接移除token

代码示例:
在src/utils/api.js中添加如下代码:

// 添加请求拦截器
API.interceptors.request.use(config => {
  const { url } = config
  // 判断请求url路径
  if (
    url.startsWith('/user') &&
    !url.startsWith('/user/login') &&
    !url.startsWith('/user/registered')
  ) {
    // 添加请求头
    config.headers.Authorization = getToken()
  }
  return config
})

// 添加响应拦截器
API.interceptors.response.use(response => {
  const { status } = response.data
  if (status === 400) {
    // 此时,说明 token 失效,直接移除 token 即可
    removeToken()
  }
  return response
})

2.2 使用AuthRoute鉴权路由组件控制页面跳转

实现原理:

1、限制某个页面只能在登陆的情况下访问,但是在React路由中并没有直接提供该组件,需要手动封装,来实现登陆访问控制(类似与Vue路由的导航守卫)
2、AuthRoute 组件实际上就是对原来Route组件做了一次包装,来实现一些额外的功能
3、render方法:render props模式,指定该路由要渲染的组件内容
4、Redirect组件:重定向组件,通过to属性,指定要跳转的路由信息

实现步骤:

1、在components目录中创建AuthRoute/index.js 文件
2、创建组件AuthRoute并导出
3、在AuthRoute组件中返回Route组件(在Route基础上做了一层包装,用于实现自定义功能)
4、给Route组件,添加render方法,指定改组件要渲染的内容(类似与component属性)
5、在render方法中,调用isAuth() 判断是否登陆
6、如果登陆了,就渲染当前组件(通过参数component获取到要渲染的组件,需要重命名)
7、如果没有登陆,就重定向到登陆页面,并且指定登陆成功后要跳转的页面路径
8、将AuthRoute组件接收到的props原样传递给Route组件(保证与Route组件使用方式相同)
9、使用AuthRoute组件配置路由规则,验证是否实现页面的登陆访问控制

代码示例:
在src/components/AuthRoute/index.js中添加如下代码:

const AuthRoute = ({ component: Component, ...rest }) => {
  return (
    <Route
      {...rest}
      render={props => {
        const isLogin = isAuth()

        if (isLogin) {
          // 已登录
          // 将 props 传递给组件,组件中才能获取到路由相关信息
          return <Component {...props} />
        } else {
          // 未登录
          return (
            <Redirect
              to={
   
   {
                pathname: '/login',
                state: {
                  from: props.location
                }
              }}
            />
          )
        }
      }}
    />
  )
}
export default AuthRoute

2.3 修改登录成功跳转

实现步骤:

1、登陆成功后,判断是否需要跳转到用户想要访问的页面(判断props.location.state 是否有值)
2、如果不需要,则直接调用history.go(-1) 返回上一页
3、如果需要,就跳转到from.pathname 指定的页面(推荐使用replace方法模式,不是push)

代码示例:
在src/pages/Login/index.js中添加如下代码:

// 表单的提交事件
  handleSubmit: async (values, { props }) => {
    ...
    if (status === 200) {
      // 登录成功
      localStorage.setItem('hkzf_token', body.token)
      if (!props.location.state) {
        // 此时,表示是直接进入到了该页面,直接调用 go(-1) 即可
        props.history.go(-1)
      } else {
        props.history.replace(props.location.state.from.pathname)
      }
    } else {
      // 登录失败
      Toast.info(description, 2, null, false)
    }
  }

三、项目打包

打包步骤:

1、在根目录创建 .env.production 文件,配置生产环境的接口基础路径
2、在项目根目录中,打开终端
3、输入命令: yarn build,进行项目打包,生成build文件夹(打包好的项目内容)
4、将build目录中的文件内容,部署到都服务器中即可
5、打包成功后,在根目录中就会生成一个build文件夹

总结

到此为止,我们的项目就已经全部开发完成了,由于篇幅有限,我们只展示了一部分的业务逻辑的编写,在真实的生产环境中,还要根据公司的需求去灵活运用这些知识。

猜你喜欢

转载自blog.csdn.net/qq_40652101/article/details/128966872