React+AntD组件搭建后台管理系统(不定时更新)

项目描述

该项目是属于一个前后台分离的后台管理的SPA,即单页Web应用(single page web application,SPA),包含了用户管理、商品分类管理、商品管理以及权限管理等功能模块,在技术方面采用的是React的相关知识、AntD组件以及Github第三方库进行模块化、工程化的模式开发。

总的来说,这个后台管理系统实现的是增删改查的基本操作,但是对于像我这种前端小白来说还是收获匪浅,起码是对于一个项目的开发流程有所了解,现在根据每个组件记录一下学习历程,知识点的总结以及踩过的坑。

提前声明一下,是在网上找到的别人搭建好的接口,利用谷歌浏览器的扩展工具 Postman进行接口测试,发送ajax请求实现前后台的数据交互,然后运用webpack进行项目的打包生成build文件。

Login 登陆组件

React+AntD组件搭建后台管理系统(不定时更新)_第1张图片
组件简介: 是静态组件,自定义了一部分的样式布局,使用AntD组件里面的 Form Input Button Icon 等组件实现用户登录的表单界面

组件作用: 主要是用来进行表单数据(用户数据)的收集以及表单验证,其表单验证规则直接使用AntD组件文档里的属性

组件相关操作:

  • 为了可以读取用户名称,然后在Admin组件内的首页界面上动态的显示用户名称,需要将登录的 user 保存到内存中去,所以生成一个专门用来保存用户变量的工具模块。
// 保存user
        const user = result.data
        memoryUtils.user = user  //保存在内存中
        storeageUtils.saveUser(user)  //保存到local中去
  • 为实现维持登录与自动登录的功能,需要采用浏览器的本地存储建立持久链接,那么将保存用户、读取用户、删除用户的方法封装成一个单独的模块,并且将这个模块对象暴露出去以供其他组件使用。
localStorage.setItem(USER_KEY,JSON.stringify(user))
  • 但是考虑到各大浏览器的兼容性,选择在github官网上下载store库,可以解决跨域的问题,同时也有相对应操作用户的方法,其内部也可以自动的转换json数据。
const storageUtils = {
  // 保存user
  saveUser(user) {
  // localStorage.setItem(USER_KEY,JSON.stringify(user))
    store.set(USER_KEY,user)
  },
  // 读取user
  getUser(){
    // return JSON.parse(localStorage.getItem(USER_KEY) || '{}' )
    return store.get(USER_KEY) || {}
  },
  // 删除user
  removeUser(){
    // localStorage.removeItem(USER_KEY)
    store.remove(USER_KEY)
  }
}

export default storageUtils

注意点:在上述的代码中 getUser() 的返回值是一个有意思的设计,因为在读取用户时,考虑到用户可能是第一次登陆,为了防止后续获取用户信息出错,最好返回一个空对象

  • 在入口文件index.js中去读取在本地存储的用户数据 user 保存到自定义内存模块中去,这样的话,保证以后其他组件想要操作用户数据时直接导入内存模块。用户登录完成后刷新界面仍然会显示的是登录的界面
// 读取local中保存的user 保存到内存中
const user = storageUtils.getUser()
memoryUtils.user = user
  • 通过判断内存中的用户 user 是否存在,来决定页面的跳转。
  1. 用户已经存在,则直接跳转到主界面
// 跳转到管理界面(不需要再回退回到登录)
        props.history.replace('/home')
  1. 用户不存在,自动跳转到登录页面
if(!user || !user._id) {
            // 自动跳转到登录页面
            return <Redirect to='/login'/>
        }

项目知识点

  • async 和 await

    1、作用?
    简化promise对象的使用:不再使用then() 来指定成功/失败的回调函数
    以同步编码(没有回调函数) 方式实现异步流程

    2、哪里写await
    在返回promise的表达式左侧写await: 不想要promise 想要promise异步执行的成功的value数据
    3、哪里写async
    await所在函数(最近的)的左侧写async

  • 高阶函数:
    (1)一类特别的函数
    接收函数类型的参数
    返回值是函数
    (2)常见的高阶函数
    定时器:setTimeout() setInterval() Promise
    数组相关的遍历方法:forEach() filter() map() reduce() find()
    函数对象的bind()
    (3)高阶函数更新动态 更加具有扩展性

  • 高阶组件
    本质上就是一个函数
    接收一个组件(被包装组件)返回一个新的组件(包装组件)包装组件会向被包装组件传入特定属性
    作用:扩展组件的功能

  • 高阶组件 与 高阶函数的关系
    高阶组件是特别的高阶函数
    接收一个组件函数 返回是一个新的组件函数

Admin 组件

该组件为后台管理系统的主界面,包括左侧菜单栏 LeftNav 组件,头部 Header 组件,中间用来展示内容的各个小组件,以及底部的 Footer组件。主要运用AntD组件完成布局,以及子路由的配置。

return (
  <Layout style={{minHeight:'100%'}}>
     <Sider>
         <LeftNav/>
     </Sider>
     <Layout>
       <Header>Header</Header>
       <Content style={{margin:'20px', backgroundColor:'#fff'}}>
         <Switch>
           <Route path='/home' component={Home}/>
           <Route path='/category' component={Category}/>
           <Route path='/product' component={Product}/>
           <Route path='/role' component={Role}/>
           <Route path='/user' component={User}/>
           <Route path='/chars/bar' component={Bar}/>
           <Route path='/chars/line' component={Line}/>
           <Route path='/chars/pie' component={Pie}/>
           <Redirect to='/home'/>
         </Switch>
       </Content>
       <Footer style={{textAlign:'center',color:'#ccc'}}>推荐使用谷歌浏览器,可以获得更佳页面操作体验!</Footer>
     </Layout>
   </Layout>
)

React+AntD组件搭建后台管理系统(不定时更新)_第2张图片

LeftNav 组件

组件简介: 用来生成菜单的导航栏

组件知识点

  • 将菜单项的数据单独抽离出来,作为一个独立的模块,暴露这个数组 menuList ,其中这个数组的每一项都对应了一个对象

  • 为匹配请求路径所对应的界面,需要设置 AntD 组件中的 Menu 组件的属性:defaultSelectedKeys,但是这里有个小坑,一旦设置为路由路径作为初始选中菜单项之后,它只能改变一次,后续的操作不会生效!根据文档,需要将 defaultSelectedKeys 变为 selectedKeys(根据页面变化重新进行动态匹配菜单项)

  • 针对于有子菜单项的情况,需要在页面打开之后,自动展开其子菜单项。根据文档需要设置 openKey 属性,令其值为当前菜单项的key值即可

  • 运用数组的map以及递归调用的方法根据导入的 menuList 动态生成菜单导航

getMenuNodes = (menuList) => {
   
    // 得到当前请求的路由路径
    const path = this.props.location.pathname

    return menuList.map(item => {
      /* 
        这里的item的形式如下:
        {
          title: '首页', //菜单标题名称
          key: '/home',  //对应的path
          icon: 'home',  //图标名称
          children: []  可能有也可能没有
        },
      */
        if (!item.children) {
          return (
            <Menu.Item key={item.key} icon={item.icon}>
              <Link to={item.key}>{item.title}</Link>
            </Menu.Item>
          )
        } else {
          // 查找一个与当前请求路径匹配的子item
          const cItem = item.children.find(cItem => cItem.key === path)
          // 如果存在 说明当前item的子列表需要打开
          if (cItem) {this.openKey = item.key}
          return (
            <SubMenu key={item.key} icon={item.icon} title={item.title}>
               {/* 递归调用 */}
               {
                this.getMenuNodes(item.children)
              }
            </SubMenu>
          )
        }  
    })
  } 
  • 在上述的操作中用到了高阶组件:用来包装非路由组件,返回一个新的组件,这个新的组件会向非路由组件传递三个属性: histroy location match
export default withRouter(LeftNav)
Header 组件

组件简介: 用来展示实时时间与天气情况,还有用户名,实现用户的退出功能以及显示菜单标题名称。

组件知识点

  • 用户名:直接在内存中读取 user 并获取到对应的 username 展示即可
  • 天气:我是自己在高德地图上面申请了一个查询天气的API接口,然后为解决浏览器的跨域问题,在github上面下载jsonp第三方库,封装成函数直接调用获取结果。

jsonp解决跨域的基本原理将在下面进行详细阐述

  • 实时时间:开启循环定时器,每隔一秒就获取到时间,然后更新状态
  • 在 componentDidMount() 中就调用获取时间、获取天气文本的函数,千万不要忘记在组件卸载之前 componentWillUnmount() 清除获取实时时间的循环定时器
  • 退出:使用AntD组件中的 Modal 实现的,不用与后台交互,只是要注意退出登陆后,将保存在本地以及内存模块中的 user 数据删除,然后将页面跳转到登陆页面。(同样的,要将Header组件包装成一个路由组件,前面已经介绍过,这里就不再赘述了)
// 删除保存的数据
        storageUtils.removeUser()
        memoryUtils.user = {}

        // 跳转到login页面
        this.props.history.replace('/login')
  • 显示菜单标题名称:得到当前的请求路径,它对应着数组 menuList 的某一项的属性key值,需要利用 forEach() 函数遍历整个数组然后进行查找匹配,得到对应的 title 进行展示
getTitle = () => {
    // 得到当前请求路径
    const path = this.props.location.pathname
    let title
    menuList.forEach(item => {
      if (item.key === path) {  //如果当前item对象的key与path匹配 则item的title就是需要显示的title
        title = item.title
      } else if (item.children) {
        // 在所有的子item中进行查找匹配 返回一个布尔值
        const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
        // 如果有值则说明成功匹配 且取出他的title显示
        if (cItem) {
          title = cItem.title
        }
      }
    })
    return title
  }

jsonp解决Ajax跨域的原理

  • 基本原理
    1. 浏览器端: 动态生成

你可能感兴趣的:(项目总结,javascript,reactjs,es6)