react项目细节流程

本项目的git仓库

不看攻略手写一个react+redux后台管理系统

B端项目 直接用的ANTD,肯定没那么好看

  • 关于redux

想要获取redux中的数据,写在connect的state和方法参数中,调用:this.props.参数/方法名

容器组件想要冲redux中取值。state=>{一般和后面是一样的:state.reducer的Index中定义的名字} ,方法通过action引入

写在class组件中的参数,需要是表达式 state={a:***}, 调用:this.state.a

2021.5.21

测试服务器好用 开始写代码

启动服务器&数据库

  • cmd 服务器所在目录,看package.json的要求 npm start
  • 导入数据库

脚手架使用

  1. create-react-app content_manage

  2. 删掉脚手架里没用的东西

  3. 建一个git仓库,上传内容

  4. public中存一些静态资源,静态的index界面,之后所有的JS页面都渲染到这个index上。在CSS内存入reset,静态页面引入即可,

  5. src文件夹

    • api: 存放二次封装的axios
    • component: 存放无业务逻辑的UI组件(不能和redux产生交互)
    • config:存配置
    • containers:所有的容器组件(当然他们也是路由)
    • redux: reducers(操作state的方法) action_creators(做什么) store(redux的主要组件) action_type(防止写错)
  6. app.js 登录和注册路由

  7. index.js 渲染app.js,因为用的是redux 所以用provider包裹

路由定义

  1. app中是两个主要的路由,login & admin 如果不写默认跳转到home
  2. 写静态的login,jsx中引入静态的图片要用import

安装依赖

  1. yarn add react-router-dom(想用路由 必须引入)
  2. yarn add redux
  3. npm install --save react-thunk
  4. npm install --save react-redux // 第一步引入会出错
  5. npm install --save-dev redux-devtools-extension // 插件依赖

使用Antd

  1. yarn add antd

  2. antd的修改和按需引入: 看官网文档即可 更改较快

  3. antd按需要引入 改配置

  4. 路由选项卡 Switch包裹

  5. Switch外必须有路由器 一般写在index里 可以选择BrowserSouter/HashRoute

  6. antd的提交和事实验证方法的编写方法 在成功的方法里发送ajax请求,失败的直接弹出验证

  7. 自定义校验写在rules里即可!!看文档啊看文档,自己试不如看文档

————————————————————————————

2021.5.22

redux环境搭配

  1. 为了简洁:把有redux属性的方法UI组件和容器组件写在一起 都写在containers里(既有UI又有容器即connect)

  2. 新建redux文件夹 新建 store.js reducers(操作state的方法) action_creators(做什么) action_type.js

    reducers里写入一个汇总reducer的index文件

  3. index里给路由器外包一个顶级组件provider,传store过去

    注意暴露方式: 如果是多个函数 不能默认暴露

  4. 目前login还是UI组件,引入connect 改写为容器组件

    一个干净的组件要和redux产生交互 需引入connect 和 action_creators 然后用connect连接action和原组件

开始发请求

  1. yarn add axios

  2. 登录 post 请求

  3. 出现了!**跨域问题 **

    在package.json 添加 “proxy”: “http://localhost:5000” 记得把所有发请求的地址改成3000 就是项目的端口

  4. POST请求如何让请求体获得参数,POST请求写成urlencorded才能接收到,后期加一个请求拦截器吧

    https://www.cnblogs.com/yiyi17/p/9409249.html

    axios的POST请求默认将参数以JSON格式发送给后端

    (1)更最简单的解决方式,直接改后台:

    //引入body-parser模块
    const bodyParser = require('body-parser')
    //引入中间件,解析JSON格式字符串
    router.use(bodyParser.json());
    //___________________________
    //express也自己封装了一个这个方法,可以不引入新模块
    router.use(express.json())
    

    (2)使用querystring

    import qs from 'querystring'
    axios.post('http://localhost:3000/login/',qs.stringify({username,password}))
    

————————————————————————————

5.23

将项目中所有请求封装到一起

  1. 新建API文件夹,汇总所有请求并向外暴露

  2. axios方法返回一个Promise实例需要自己手动处理成功和失败的回调,为精简写方法,将其改为Async和await

  3. 使用await只能获得成功的回调,trycatch可以获得失败的回调。但是为了思路更清晰,加入拦截器,不需要catch捕获错误,如果出错直接走响应拦截器的error

  4. 配置超时 axios.create 加入请求拦截器&响应拦截器(直接去Axios库看格式)

    请求拦截器:把传过来的POST请求中是JSON格式的数据用querystring转为urlencorded格式

    响应拦截器:如果出错,用ANTD的message组件弹出错误并终端promise链,如果成功返回response

    在请求+响应拦截器中加入进度条 用NProgress这个库就可以

  5. 配置项目的默认路径 新建config文件夹 写一个入口文件

  6. 改服务器 让服务器返回一个token,作为登录令牌

  7. 把token 和 user 作保存到redux的state里(用redux的工具查看状态) 这些方法都存在state里,this.props.XXXX

    注意token保存的位置,这个直接影响后期如读取内容

  8. 先保存Token到admin之后 再跳转页面

————————————————————————————

5.24

七天免登录

  1. admin中读取是否登录的状态,如果没登录直接跳转login 跳转最好用Redirect

    如果没登录必须return一下,不然render会出错

  2. 服务器返回的登录内容要保存起来,存到localstorge 写在action里

    localStorage.setItem(‘user’,JSON.stringify(value.user))

    将状态从localStorage更新到login_reducer中,这样就在state中可以和login页面交互

  3. 从state中读Login状态,如果已经登录了直接跳转到admin

————————————————————————————

5.25

退出登录

  1. 在admin 页面退出登录,删除localstorage和redux中保存的信息

装饰器语法

  1. connect函数是个高阶函数,所以使用装饰器语法处理装饰器语法

  2. 安装装饰器:

    npm install save- @babel/plugin-proposal-decorators

  3. 修改配置文件 config-overrides.js

const { addDecoratorsLegacy} = require('customize-cra');

 module.exports = override(
    addDecoratorsLegacy()
 );
  1. 写法

    //@connect方法传入state和action方法之后,传入Admin组件
    @connect(
      state => ({userInfo:state.userInfo}),
      {deleteUserInfo:createDeleteUserInfoAction}
    )
    class Admin extends Component{}
    // 不能把暴露和组件定义写在一行
    export default Admin
    

如何鉴别用户是否登录

  1. cookie 保存session-ID

  2. cookie-session 保存敏感数据

  3. token(如果将cookie导出,可以模拟登录状态) 过滤器先看token的格式符不符合

    之后所有的操作都需要携带token验证自己的登录状态,因此只要发送请求,就要将redux中的token读取出来,因此写在Axios的请求拦截器上,如果有token ,把token加在请求头上。在token前加一个格式前缀,方便过滤器校验

    config.headers.Authorization = ‘sqn’+token

  4. token过期之后,自动返回登录页面。逻辑完成在响应拦截器里 (store.dispatch就是在和redux交互)

写Admin的整体布局

  1. 用ANTD的布局
  2. 拆组件,组件都和REDUX交互的都是容器组件

头部布局

  1. 上下两部分+静态界面

  2. 屏幕全屏:screenfull 库

    npm install screenfull 事件监听绑定在componentDidMount下 一挂在就监听

  3. 浏览器全屏的问题目前解决不了

  4. 从redux中获取用户名

    • 从redux中取内容 state.XXX store.getState().userInfo
    • 从穿过的state中取内容:this.props.XXXX
  5. 头部退出登录:加一个confirm提问,是否确定退出,注意this想要传递要不然就提前保存this 否则就用箭头函数

  6. 头部时间:

    state中保存时间,渲染完成后开定时器每一秒更新一下

    npm install dayjs 写法: dayjs().format(’{YYYY} MM-DDTHH:mm:ss SSS [Z] A’)\

      state={
        date:dayjs().format('YYYY年 MM月DD日 HH:mm:ss')
      }
      componentDidMount(){
        setInterval(()=>{
          this.setState({date:dayjs().format('YYYY年 MM月DD日 HH:mm:ss')})
        },1000)
      }
    

    当切换页面时,即头部卸载的时候,要把定时器停掉,写在willUnmount

  7. 头部天气:

    请求百度的天气接口,出现了跨域问题,用jsonp发请求解决跨域问题

    npm install jsonp

    jsonp的请求结果的获取是异步的。使用Promise,失败了中断promise链,成功返回一个resolve(XXXX)

    要把回调函数带回来的值交给外层方法的时候,考虑用promise,不然无法return返回值,回调和主线程是异步执行的,所以用promise,返回一个promise对象,这样在需要用的地方用await去接收

//获取天气信息(百度接口,使用jsonp的方式请求)
export const reqWeather = ()=>{
  return new Promise((resolve,reject)=>{
      jsonp(`http://api.map.baidu.com/telematics/v3/weather?location=${CITY}&output=json&ak=${WEATHER_AK}`,(err,data)=>{
      if(err){
        message.error('请求天气接口失败,请联系管理员')
        return new Promise(()=>{})
      }else{
        const {dayPictureUrl,temperature,weather} = data.results[0].weather_data[0]
        let weatherObj = {dayPictureUrl,temperature,weather}
        resolve(weatherObj)
      }
    })
  })
}

将请求来的天气信息维护进state,页面动态展示state信息

              <img src={weatherInfo.dayPictureUrl} alt="天气信息"/>
              {weatherInfo.weather}&nbsp;&nbsp;&nbsp;温度:{weatherInfo.temperature}

使用withRouter

让非路由组件使用路由组件的API,动态获取路由名称,{this.props.location.pathname}

header不是路由组件,就是将非路由组件渲染成路由组件(这也是个高阶组件)

从路径中对应到Title

循环遍历,找path和配置对象中的key有没有一致的情况

getTitle = (()=>{
    let pathKey = this.props.location.pathname.split('/').reverse()[0]
    let title = ''
    menuList.forEach ((item)=>{
      if(item.children instanceof Array){
        let res = item.children.find ((childItem)=>{
          return childItem.key == pathKey
        })
        if(res) title =res.title;
      }else{
        if(item.key ==pathKey)  title = item.title
      }
    })
    // return title;
    // 直接维护到class中的state里 完整写法:this.setState({title:title})
    this.setState({title})
  })
  • 当想要获得title的时候要调用getTitle方法,因为并无点击事件等调用,需要title的时候调用方法为:getTitle()

    注意加括号才能调用

————————————————————————————

5.26

侧边栏

  1. 静态布局:Antd引入即可,引入静态资源注意不能到src外(脚手架的规定),所以在src下建立一个static文件夹

  2. 引入react-router-dom 中的Link 做自动切换

  3. 自动生成菜单,写成一个递归的方法 ,这样多级菜单也可以直接动态渲染

        createMenu=(target)=>{
            return(
                target.map((item)=>{
                    if(!item.children){
                        return(
                          <Item key={item.key} icon = {iconConfigMap[item.icon]}>
                              <Link to={item.path}>
                                  <span>{item.title}</span>
                              </Link>
                          </Item>
                        )
                    }else{
                        return(
                            <SubMenu key={item.key} icon = {iconConfigMap[item.icon]} title={item.title} >
                                {this.createMenu(item.children)}
                            </SubMenu>
                        )
                    }
                }
                )
            )
        }
    

    问题:如何动态渲染组件

    jsx中无法用模板字符串读取组件,只能将Icon的配置写在JSX文件中

    // left_nav.jsx
    const iconConfigMap = {
        'HomeOutlined':<HomeOutlined />,
      }
    return(
        icon = {iconConfigMap[item.icon]}
    )
    // config.js
    export default[
      {
        title: '首页', // 菜单标题名称
        key: 'home', // 对应的path
        icon: 'HomeOutlined',
        path: '/admin/home'//对应路径
      }]
    
    
  4. 使用withrouter动态获取路径

    defaultSelectedKeys={this.props.location.pathname.split('/').reverse()[0]} //默认选中
    

    pathname获得路径,拆分之后翻转数组拿到第一个就是之前的最后一个

    这样刷新页面还会停留在原页面,不会按照默认选中跳转到别处

  5. 选中子菜单的时候,刷新之后默认打开父级菜单

    defaultOpenKeys={this.props.location.pathname.split('/').splice(2)} //默认打开
    

从路径中获取Title的问题

  • 由于一直从路径中获取的Title,再加上Home页面开启了一个定时器用来实现秒表,所以每秒钟都会重新渲染页面,为了优化这个问题,我们要把title保存在redux中,如果直接存在state里,并在didUpdate方法中调用getTitle(),会出现挂载的时候的死循环,更新state—挂载—更新state,所以 只能写在redux里。为leftNav中的Item添加点击事件,每次点击将所在的title存入redux

  • 重复一下流程:

    1. 给left_nav中的item加点击事件,定义点击事件,在点击事件中与redux完成通信

    2. 要通信,把left_nav改写为容器组件,引入connect,connect中存在状态和方法两个参数

    3. 既然有方法参数,那么就要引入action,新建action文件,写action文件自然要改写Action-types

    4. 写对应的reducer, reducer写更新state的方法,但是写完记得去index内汇总

    5. 将一开始的点击事件和redux中定义的方法合并一下,onClick事件不能写小括号,但是需要传参,所以用箭头函数再包一层

      <Item key={item.key} icon = {iconConfigMap[item.icon]} onClick={()=>{
        this.props.saveTitle(item.title)
      }}>
    
    1. 然后记得给引用的位置改为容器组件,通过redux中connect组件将数据更新至state ,通过this.props.参数 完成渲染

    2. 最后要完成刷新还能保存路径的逻辑,在getTitle函数中,不返回title,而是将title维护到class的state中的title属性中,注意!不是connect中的state,是类中的是state,connect中的是redux中的值。

    3. 在didMount方法中调用getTitle(),注意!如果是在didUpdate中调用会出现死循环(见上)

    4. 渲染的时候,redux中的title有值,渲染这个title,若为空则说明刚刚刷新,去class中的state中获取计算出来的title,因为每次刷新会执行didMount,就是redux有读redux效率高,redux没有就去路径中计算title,存入state

      {this.props.title||this.state.title}
      

————————————————————————————

5.29 摸鱼了两天

Category页面

  1. antd的card+ table写静态页面
  2. 开始交互,发一个get请求,页面一挂载就开始发送请求,但是不要给生命周期钩子加async,所以在外部定义一个异步函数,在钩子里调用
  3. 给每个数据加一个key 组件库要求有key,数据库存的是ID。当然可以直接改Table组件的属性!淦!
  4. 如果想动态给点击窗口起名字,定义一个变量存入class的state中,然后点击事件更改这个state的值,在给属性赋值的时候直接从state身上读取
  5. 关于Form表单的一些修改,如 formRef = React.createRef(); <Form ref={this.formRef} > 还是仔细看文档比较好
  6. ANTD中 组件的有一些奇奇怪怪的设定 自己尝试吧。如果想要修改就

————————————————————————————

关于警告

  1. scheduler.development.js:298 [Deprecation] SharedArrayBuffer will require cross-origin isolation as of M91, around May 2021. See https://developer.chrome.com/blog/enabling-shared-array-buffer/ for more details.
    
    • 解决方案
    yarn upgrade react --latest
    yarn upgrade react-dom --latest
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值