React类组件 React基础 Day03

css补充

calc计算

css 复制代码
  .main-home {
    // flex-grow: 1;
    width: calc(100vw - 200px);
    background: blue;
  }

在计算某一个盒子的宽度的时候, 可以使用calc这个属性参与计算, 使用方式 calc(总宽度 - 某一个宽度 ) ,

注意:减号 左右必须有空格

宽度的单位可以是px, vw, 百分比单位

React 路由懒加载

vue路由属于配置式的二路由, React 属于编程式路由

React 路由懒加载为了做性能优化, 按照路由按需加载的, 减少某个页面白屏等待时间,减少打包体积

配置路由懒加载

  1. 通过React.lazy 导入路由懒加载组件
javascript 复制代码
const Home = React.lazy(()=>import('./pages/Home')); // 懒加载组件
const Login = React.lazy(()=>import('./pages/Login'));
const About = React.lazy(()=>import('./pages/About'));

2.必须要用React.Suspend包裹住路由懒加载组件

xml 复制代码
 <React.Suspense fallback={<div>loading</div>}>
    .... 路由配置
 </React.Suspense>

React.Suspense 属性fallback是为了让在等待的时候, 有一个加载的动画, 一般这里可以放个loading的组件,

强调: React中组件首字母必须大写

路由组件简单封装

封装是为了更好的配置路由, 方便路由的管理

先在src目录新建一个router文件夹,router/routes.jsx

第一步:配置路由表 router/routes.jsx

javascript 复制代码
import React from 'react'
import {Navigate} from 'react-router-dom'
const Home = React.lazy(()=>import('../pages/Home')); // 懒加载组件
const Login = React.lazy(()=>import('../pages/Login'));
const About = React.lazy(()=>import('../pages/About'));
const User = React.lazy(()=>import('../pages/manage/User'));
const Role = React.lazy(()=>import('../pages/manage/Role'));

const routerList = [{
    path:'/',
    element:<Navigate to={'/home'}></Navigate>
},{
    path:'/login',
    element:<Login></Login>
},
{
    path:'/home',
    element:<Home></Home>,
    children:[
        {
            path:'',
            element:<User></User>
        },
        {
            path:'/home/user',
            element:<User></User>
        },
        {
            path:'/home/role',
            element:<Role></Role>
        }
    ]
}
]

export default routerList;

第二步:新建路由组件router/RouterIndex.jsx

目的是引入路由配置表, 然后通过map循环把路由组装成我们需要路由格式,即Routes管理Route

javascript 复制代码
import React, { Component } from 'react'
import {Routes,Route} from 'react-router-dom'
import routerList from './routes'

export default class RouterIndex extends Component {

  render() {
    return (
        <Routes>
            {routerList.map((item)=>{
                return <Route path={item.path} element={item.element}>

                {item.children && item.children.map((option)=>{
                    return <Route path={option.path} element={option.element} ></Route>
                })}
                </Route>
            })}
            {/* <Route path='/' element={<Navigate to={'/home'}></Navigate>}></Route> */}
            {/* <Route path='/login' element={<Login></Login>}></Route> */}
            {/* <Route path='/home' element={<Home></Home>}>
                    <Route path='' element={<User></User>}></Route>
                    <Route path='user' element={<User></User>}></Route>
                    <Route path='/home/role' element={<Role></Role>} ></Route>
                </Route>
                <Route path='/about' element={<About></About>} ></Route>
            <Route path='*' element={<div>404页面</div>}></Route> */}
        </Routes>
    )
  }
}

第三步: 在根据组件渲染所有的Route

xml 复制代码
import RouterIndex from './router/RouterIndex';

...
<BrowserRouter>
。。。
	<RouterIndex></RouterIndex>
。。。
</BrowserRouter>

注意:路由组件RouterIndex必须被路由容器BrowserRouter或者HashRouter包裹住

进一步使用递归封装

javascript 复制代码
import React, { Component } from 'react'
import {Routes,Route} from 'react-router-dom'
import routerList from './routes'

export default class RouterIndex extends Component {
    routerFun(routes){
       return routes.map((item)=>{
            return <Route path={item.path} element={item.element}>
                        {item.children && this.routerFun(item.children)}
                   </Route>
        })
    }
  render() {
    return (
        <Routes>
            {this.routerFun(routerList)}
        </Routes>
    )
  }
}

懒加载组件优化

解决左边菜单点击的时候, 第一次加载刷新的问题

直接使用 React.Suspense包裹路由组件

javascript 复制代码
const Login = React.lazy(()=>import('../pages/Login'));
....
{
    path:'/login',
    element: <React.Suspense fallback={<div>loading</div>}><Login></Login></React.Suspense>
},

为了代理管理方便,封装一个函数统一的处理懒加载组件, 路由路径作为参数

javascript 复制代码
const SusRouter = (filePath)=>{
    // 获取加载的组建
    const ComName = React.lazy(()=>import('../pages/'+filePath));
    return <React.Suspense fallback={<div>loading</div>}><ComName></ComName></React.Suspense>
}
......
{
    path:'/login',
    element: SusRouter('Login')
}
.....

全局事件总线events

使用全局事件总线解决兄弟组件的传值问题

安装

csharp 复制代码
yarn add events

新建util/event.js, 导出事件对象, 导出事件对象的目的是为了组件中数据都是针对同一个事件对象

javascript 复制代码
import {EventEmitter} from 'events'
console.log(EventEmitter);
// EventEmitter 发布订阅模式
const eventObj = new EventEmitter();

export default eventObj;

比如说有2个兄弟组件 A1组件和 A2组件进行数据通信

A2组件订阅事件

javascript 复制代码
import React, { Component } from 'react'
import eventObj from '../../utils/event'
export default class A2 extends Component {
    // 相当于vue里的 mounted
    componentDidMount(){
        // 订阅一个自定义事件, 类似vue父子组建传值的时候, 通过@test="getData"
        eventObj.addListener('testEvent',(data)=>{
            console.log(data, '接收别的组建发布的数据');
        })
    }
  render() {
    return (
      <div>A2</div>
    )
  }
}

A1组件针对订阅事件进行发布数据

javascript 复制代码
import React, { Component } from 'react'
import eventObj from '../../utils/event'
export default class A1 extends Component {

    sendData = ()=>{
        // 针对自定义事件testEvent发布数据
        eventObj.emit('testEvent',{
            username:'alice'
        })
    }
  render() {
    return (
      <div>A1
        <button onClick={this.sendData}>发送数据</button>
      </div>
    )
  }
}

组件生命周期

任何事物都有生命周期, 从生到死的过程, 组件从创建到销毁就是一个完整的生命周期

生命周期主要分3个阶段:

1.挂载阶段 - > constructor构造器的初始化组件内部数据,组件的渲染render,渲染完毕后会走componentDidDmount

constructor-> render ->componentDidDmount

2.更新阶段, 通过setState修改组件内部数据, 父组件传的props属性有改变的时候, 都会触发组件的更新阶段,走render函数重新渲染模板, 通过钩子函数componentDidUpdate监听更新完毕

优化钩子函数,shouldCompentUpdate 我需要重新更新组件吗? 默认true

setState - > shouldCompentUpdate(true) - >render->componentDidUpdate

3.组件销毁阶段:可以在钩子函数componentWillUnmount处理, 可以在这里对定时器进行清除

常用的钩子函数 componentDidDmount, 你可以在这里获取模板中的某一个dom, 在这里调接口, 获取设置定时器

componentDidUpdate 类似vue里的updated, 基本不会用到

componentWillUnmount: 一般只有清除定时器的时候会用到

shouldCompentUpdate : 一般组件优化的时候会用下

外部样式module模块化

在组件中, 使用 样式文件名.module.scss的方式定义,

在组件中使用

javascript 复制代码
import style from './main.module.scss'

<div className={style.loading} style={this.styleObj}>
        <div className={style.box}>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
        </div>
 </div>

处理成功后, 会自动帮你加上hash一串字符 , 达到样式模块化目的,不会与其他组件样式冲突

mockjs使用步骤

1.安装mockjs

csharp 复制代码
yarn add mockjs

2.配置接口

在src目录下新建有mock/user.js模块

php 复制代码
import mockjs from "mockjs";
// 用户模块
// mock 登录接口
// url , method , data
mockjs.mock('/test/login','post', {
    code:1,
    msg:'登录成功',
    data:{
        username:'alice'
    }
})

3.在入口文件导入相应的mock模块文件

arduino 复制代码
import  './mock/user';

4.在页面中使用

javascript 复制代码
 axios({
      url:'/test/login',
      method:'post'
    }).then((res)=>{
      console.log(res.data);
    })

注明: 因为mock文件就放在项目里, 所以mock的接口和项目访问的域名和端口保持一致,

也就是说访问路由文件地址是 http://localhost:3000/login, mockjs的接口域就是http://localhost:3000

如果项目中mock的数据比较多, 分模块管理的时候, 建议在mock文件夹中新建一个index.js文件, 用来管理所有的mockjs文件

导入所有的模块 mock/index.js

arduino 复制代码
import  './mock/user';
import  './mock/role';

然后在入口文件中只需要引入

arduino 复制代码
import  './mock/index';

路由跳转说明

react 18版本之前, 类组件 props上有location可以让跳转, 但是目前react官方极力的推荐hooks开发, 所以取消原来的api

在类组件中暂时用 原生location 方式跳转

ini 复制代码
  window.location.href = '/home'  ;

项目中引入图片的2种方式

第一种: 通过commonJs的语法require

javascript 复制代码
require('../assets/images/1.jpg')

第二种:通过es6的语法

javascript 复制代码
import img1  from '../assets/images/1.jpg'

Modal模态框的封装

思路: 父组件传一个boolean作为显示或者隐藏的开关 给子组件 , 子组件获取到后, 通过 &&操作符去控制显示或者隐藏

父组件

kotlin 复制代码
 state = {
        modalObj:{
            isOpen:false,
            title:'新增'
        }
    }
    
  // 显示模态
    showModal = ()=>{
        // this.state.modalObj.isOpen = true;
        this.setState({
            modalObj:{...this.state.modalObj,isOpen:true}
        })
    }
    // 关闭模态框
    hideModal= ()=>{
        // es5的 语法 对象合并
        // Object.assign({},this.state.modalObj,{isOpen:false})
        // es6 对象合并
        // {...this.state.modalObj,isOpen:false}
        this.setState({
            modalObj:{...this.state.modalObj,isOpen:false}
        })
    }
xml 复制代码
  <ModelBox isOpen={modalObj.isOpen} close={this.hideModal} title={modalObj.title}>
       form表单
  </ModelBox>

子组件

javascript 复制代码
import React, { Component } from 'react'
import style from './modal.module.scss'
export default class ModelBox extends Component {
    closeFun = ()=>{
        // 子传父, 需要把父组件的isOpen属性设置为false
        this.props.close()
    }
  render() {
    const {isOpen,title} = this.props;
    return (
        <>
            {isOpen && <div className={style['box-modal']}>
                <div className='title'>
                    <span>{title}</span>    <span onClick={this.closeFun}>关闭</span>
                </div>
                
                {this.props.children}
           </div>}
        </>
      
    )
  }
}

需求补充:

  1. 需要传一个宽度width, 高度height自定义属性 给弹框组件控制模态框的大小
  2. 传一个boolean给弹框组件控制模态框的背景是否显示
  3. 需要对传的属性给出默认样式

弹框组件完整代码如下:

组件部分

javascript 复制代码
import React, { Component } from 'react'
import style from './modal.module.scss'
export default class ModelBox extends Component {
    static defaultProps = {
        width:'400px',
        height:'400px',
        isShadow:false
    }
    closeFun = ()=>{
        // 子传父, 需要把父组件的isOpen属性设置为false
        this.props.close(); // 相当于调用父组件的hideModal这个方法
    }
  render() {
    const {isOpen,title,width,height,isShadow} = this.props;
    return (
        <>
            {(isOpen && isShadow) && <div className={style['shadow']}></div>}
            {isOpen && <div className={style['box-modal']} style= {
                    {'--width':width,'--height':height}
                }>
                <div className={style['title']}>
                    <span>{title}</span>    <span className={style['close']} onClick={this.closeFun}>关闭</span>
                </div>

                <div className={style['content']}>
                    {this.props.children}
                </div>
                
                
           </div>}
        </>
      
    )
  }
}

modal.module.scss样式部分:

css 复制代码
.box-modal {
    // width: 400px;
    height: 400px;
    width: var(--width,300px);
    height:var(--height,300px);
    border: 1px solid #ccc;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
    border-radius: 5px;
    background: #fff;
    box-shadow: 0 0 5px 5px rgba(0,0,0,0.2);
    padding: 2px;
    z-index: 100;

    .title {
        display: flex;
        justify-content: space-between;
        background: #ff6600;
        height: 35px;
        align-items: center;
        padding-left: 10px;
        padding-right: 10px;
        border-top-left-radius: 5px;
        border-top-right-radius: 5px;
    }
    .content {
        padding: 20px;
       
    }
    .close {
        font-size: 12px;
        cursor: pointer;
        border: 1px solid #666;
        padding: 3px 8px;
        background: rgba(255,255,255,0.3);
        border-radius: 5px;
    }
}

.shadow {
    width: 100vw;
    height: 100vh;
    background: rgba(0,0,0,0.3);
    position: fixed;
    left: 0;
    top: 0;
    z-index: 1;
}

react中的插槽

父子组建传值的时候, 子组件props都有这个children属性, 不需要你传,父子组建传值的时候, 你传的属性不要和children重名, 直接使用this.props.children渲染父组件中自定义的内容

kotlin 复制代码
 {this.props.children}
相关推荐
SameX2 分钟前
初识 HarmonyOS Next 的分布式管理:设备发现与认证
前端·harmonyos
M_emory_29 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito32 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员1 小时前
响应式网页设计--html
前端·html
fighting ~1 小时前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录1 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
abments1 小时前
JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)
javascript·爬虫·python
mon_star°1 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184551 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
老码沉思录2 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js