css补充
calc计算
css
.main-home {
// flex-grow: 1;
width: calc(100vw - 200px);
background: blue;
}
在计算某一个盒子的宽度的时候, 可以使用calc这个属性参与计算, 使用方式 calc(总宽度 - 某一个宽度 ) ,
注意:减号 左右必须有空格
宽度的单位可以是px, vw, 百分比单位
React 路由懒加载
vue路由属于配置式的二路由, React 属于编程式路由
React 路由懒加载为了做性能优化, 按照路由按需加载的, 减少某个页面白屏等待时间,减少打包体积
配置路由懒加载
- 通过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>}
</>
)
}
}
需求补充:
- 需要传一个宽度width, 高度height自定义属性 给弹框组件控制模态框的大小
- 传一个boolean给弹框组件控制模态框的背景是否显示
- 需要对传的属性给出默认样式
弹框组件完整代码如下:
组件部分
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}