React 1-7天学习笔记

React Day01

React day01

react简介

React就是起源于facebook的一个javascript库, react只是v层,只是负责UI找层界面的显示 , 需要周边环境的而一些支持

react特点

  1. 基于虚拟dom开发, 效率高
  2. 组件化开发思想:SPA,组件需求,JSX
  3. 社区完善,适合于大型的web应用开发
  4. facebook团队维护,版本稳定,持续更新
  5. 开发适用于多个场景
  6. 虚拟DOM
  7. DOM diff算法,jsx虚拟dom, js+xml ,你可以在js中写模板

React分了2分支: web(react-dom) 和 Mobile App(react-native) 混合式app

uniapp 和 react native是属于混合式app开发,

webApp就是使用html5 开发的app

ios和安卓开发 就是 原生

React开发web端的项目, 需要引入 react核心包 和 react-dom, 以前还有react-native之前, 其实也只有一个react包, 在v0.8版本后, 出现手机app, 才把react-dom 和 react-native分离出来

react核心包 : 包含了 web和手机公共的内容

react-dom:负责web端的dom渲染

react-native: 负责移动端app的渲染

创建虚拟dom

虚拟 DOM(Virtual DOM)是一种用 JavaScript 对象表示的内存中的抽象 DOM 结构,它是对真实 DOM 的一种轻量级的映射。

虚拟 DOM 的作用是在前端框架中提供高效的 DOM 操作和更新机制,以优化性能和提升用户体验。

Virtual DOM 其实就是一棵以 JavaScript 对象(VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

简单来说,可以把 Virtual DOM 理解为一个简单的 JavaScript 对象,并且最少包含标签名 tag、属性 attrs 和子元素对象 children 三个属性。不同的框架对这三个属性的命名会有点差别。

1. 通过React.createElement创建虚拟dom

 
  1. <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  2. <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  3. <div id="app"></div>
  4. <script>
  5. // 原生的Es5的方式创建真实的dom
  6. // var dom = document.createElement('div');
  7. // dom.id='app1';
  8. // dom.className='add';
  9. // dom.innerHTML = 'hello';
  10. // document.body.append(dom)
  11. // 虚拟dom
  12. // React.createElement(type, props属性,内容)
  13. // React 是来自react核心的api
  14. let xDOM= React.createElement('div',{
  15. className:'add',
  16. style:{color:'red'}
  17. },'hello world');
  18. console.log(xDOM);
  19. // ReactDOM 是来自 react-dom这个包
  20. let root = ReactDOM.createRoot(document.getElementById('app'));
  21. // 通过ReactDOM.createRoot创建一个根节点, 通过根节点把虚拟dom渲染到页面
  22. // 根节点 会有一个render函数,把虚拟dom转化为真实dom
  23. root.render(xDOM);
  24. </script>

2.jsx创建虚拟dom

需要借助babel包,可以将es6的语法转换成es5的语法, 可以让浏览器识别, babel也内置了对jsx的支持

JSX = javascript and XML(HTML)

jsx特点:

1.开发代码更简洁

2.性能也会更好

3.执行代码更快, 对代码进行优化处理

需要引入babel

 
  1. <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
 
  1. <div id="app"></div>
  2. <script type="text/babel">
  3. // jsx创建虚拟dom
  4. var str1 = 'world';
  5. var style1= {color:'red'}
  6. var ulhtml = <ul>
  7. <li>web前端</li>
  8. <li>测试</li>
  9. </ul>
  10. // js + xml
  11. var vDom = <div className="add" style={style1}>hello {str1} {ulhtml}</div>;
  12. const root = ReactDOM.createRoot(document.getElementById('app'));
  13. root.render(vDom)
  14. </script>

注意点:需要在script标签上添加 type="text/babel"

  1. 需要引入babel的包
  2. 需要在script标签上添加 type="text/babel"
  3. 在jsx中使用{}渲染模板
  4. 样式style你看到有2个{}, 外层是react的模板渲染的语法, 里面是样式对象
  5. 引入外部class, jsx环境要求使用驼峰式 className

3. jsx逻辑处理

逻辑运算, 加,减,乘 , 除, & , ||

 
  1. <script type="text/babel">
  2. var str1 = '香蕉';
  3. var isBool = false; //bool值
  4. var vname = <span>alice</span>;
  5. var num1 = 10;
  6. var num2 = 2;
  7. // 三目运算符, && 操作符
  8. var vDOM= <div> {isBool?str1:'苹果'} { isBool && vname }
  9. {isBool || 'aaa'}
  10. ---
  11. {num1 * num2}
  12. --
  13. {num2/num1}
  14. </div>;
  15. var root = ReactDOM.createRoot(document.getElementById('app'));
  16. root.render(vDOM);
  17. </script>

类似原生js,可以在模板{}里写运算逻辑,

 
  1. {isBool?str1:'苹果'}
  2. {isBool && vname }
  3. {isBool || 'aaa'}
  4. {num1 * num2}

boolean值 不能直接在模板渲染, 如果你想打印boolean值来测试, 可以使用{JSON.stringify(isBool) }

4.jsx数据动态渲染

实践需求:

var names = [‘苹果’,’香蕉’,’桃子’];

利用jsx的语法把数组names 解析成列表进行渲染

列表结构<ul><li>香蕉</li></ul>

注意点:

jsx模板中不能直接渲染对象,但是可以直接渲染数组, 如果你需要数组渲染成特定格式,可以通过map方法 循环出你想要的数组结构,然后直接在模板里渲染数组

 
  1. <div id="app"></div>
  2. <script type="text/babel">
  3. // 简单数据类型 + 复杂数据类型(对象+数组)
  4. var str = 'aaa';
  5. // var arr = ['香蕉','苹果','菠萝'];
  6. var arr = [{
  7. name:'香蕉',
  8. price:10
  9. },{
  10. name:'苹果',
  11. price:20
  12. },{
  13. name:'菠萝',
  14. price:30
  15. }];
  16. var obj = {
  17. name:'小狗'
  18. }
  19. var arrResult = arr.map((item,index)=>{
  20. return <li key={index}>{item.name} -- {item.price} </li>
  21. })
  22. console.log(arrResult);
  23. var ulDom = <ul>
  24. {arrResult}
  25. </ul>
  26. // jsx中不支持对象的渲染, 支持数组的渲染
  27. var vDom = <div>{str}
  28. {ulDom}
  29. </div>
  30. var root = ReactDOM.createRoot(document.getElementById('app'));
  31. root.render(vDom);
  32. </script>

总结:

  1. React提供JSX进行动态遍历的时候,实际上就是通过map拼接成我们想要的数组形式

     
      
    1. {list.map(item => {})}---->[<li></li>,<li></li>]

    JSX模板是可以渲染数组,但是里面内容不能是对象

  2. 遍历过程中也需要加上key,必须唯一

  3. 数据渲染以后都是页面中进行遍历,这样数据一旦发生变化,页面自动更新

组件的概念

react就是组件式开发,组件式开发可以提高开发效率,提高组件的复用性,方便后期维护和扩展

react中定义组件有2种方式:

react 版本v16.8之前,基本我们都是使用类组件, 类组件比较强大, 有this, 目前引入hooks编程模式, 函数组件使用越来越多

1.类组件 class

2.函数组件 function

1. 复习class类

1.类名首字母必须是大写

2.使用static定义类的静态属性和方法, 直接通过类名.静态属性 或者 类名.静态方法访问

3.使用关键字extends实现类的继承

4.实现类的继承时, constructor必须有一个super来继承父类的属性, 但是可以省略,使用简写的方式

 
  1. class People {
  2. static school = '蜗牛'
  3. constructor(name,age){
  4. this.name = name;
  5. this.age = age;
  6. }
  7. static say(){
  8. console.log('我在说话');
  9. }
  10. run(){
  11. console.log(this.name + '跑步');
  12. }
  13. }
  14. // 访问静态的方法和数据
  15. // console.log(People.school);
  16. // People.say();
  17. //实例化一个对象
  18. // var p1 = new People('xiaohong',20);
  19. // p1.run();

继承父类

 
  1. // Teacher 继承 People
  2. class Teacher extends People {
  3. // 构造器
  4. // constructor(name,age, country){
  5. // super(name,age); // 使用super,你才可以使用this
  6. // this.country = country; // 自己组件的属性
  7. // }
  8. // 简写的形式
  9. country = '中国'
  10. // 定义自己的方法
  11. walk(){
  12. console.log(this.name + '走路,他是'+this.country+'人');
  13. }
  14. }
  15. var t1 = new Teacher('李老师',30,'中国')
  16. t1.run();
  17. t1.walk();
  18. // constructor是必须, 但是你都可以省略,你之所以可以省略,是因为es6会帮你自动添加这个构造器

2.在类中定义普通函数和箭头函数的区别

1.类中定义的箭头函数,没有挂载在类组件的原型上, 在类继承中, 箭头函数也被继承,但是被不是被挂载在原型上

2.一般情况下类中的this指向实例,类中方法实例调用的话,类方法中this就指向实例对象

3.在JavaScript中,类中的普通方法被存储在类的原型对象中。当你将一个方法复制给一个变量后再执行,方法中的this可以改变。这是因为this在JavaScript中是动态绑定的,它的值取决于函数被调用的上下文。

3.但是把类中的方法,赋值给变量之后再调用就相当于自调用了,就不是实例调用了。赋值方法this就是指向window,但是类中的方法是自动开启局部严格模式了,所以赋值方法this最终是undefined

this会根据是否是严格模式而指向undefined或者全局环境

注:ES6 的class内部默认为严格模式

4.类中的普通函数可以通过bind, call, apply改变函数的this指向, 箭头函数不能通过bind, call,apply改变他的this指向,类中的箭头函数this 一直都是指向类的实例化对象的

3.严格模式

1.严格模式下不能使用保留字,如: class, export , extends , import , super 不能做变量名

2.严格模式下this指向的变化

在全局作用域函数中this 指向 window 对象,但是在严格模式下全局作用域中函数的 this 是 undefind

 
  1. //平常模式下
  2. function fn(){
  3. console.log(this); //打印结果为 window对象
  4. }
  5. fn()
  6. //严格模式下
  7. function fn(){
  8. 'use strict'
  9. console.log(this); //打印结果为 undefined
  10. }

3.严格模式与正常模式的语法与行为上的不同

 
  1. //正常模式下
  2. num = 10;
  3. console.log(num); //正常显示 10
  4. //严格模式下
  5. 'use strict'
  6. // num = 10;
  7. // console.log(num); //严格模式下未申明变量会报错
  8. var num = 10;
  9. console.log(num); //声明之后会正常显示 10
  10. delete num //严格模式下删除已经申明的变量会报错

JavaScript严格模式_js关闭严格模式_Wxinin的博客-CSDN博客

4.类中计算器属性

通过get关键字定义

 
  1. get fullname(){
  2. return this.name + ' ' + this.lastname;
  3. }

计算器属性 会依赖 你使用的这几个属性, 一旦依赖的属性变化, 计算器属性的值也会重新计算

React中定义类组件

react中定义类组件的一些约定:

1.类组件首字母大写

2.类组件必须要通过extends继承React.Component父类,继承了react组件的所有方法

3.类组件必须有一个render方法, 这个方法并不需要手动调用,在使用组件的时候react会帮我们自动调用

4.render函数要有一个return 返回 虚拟dom, 必须有一个根节点

6.constructor可以使用简写的方式

 
  1. <div id="app"></div>
  2. <script type="text/babel">
  3. // Header就是react类组件
  4. class Header extends React.Component {
  5. // constructor(){
  6. // super();
  7. this.username = 'alice'
  8. // }
  9. // 简写
  10. username='alice';
  11. sayHello(){
  12. console.log('heelo');
  13. }
  14. // 类组件中必须有一个render, render返回的本身就是虚拟dom
  15. render(){
  16. return <div>hello world</div>
  17. }
  18. }
  19. // 类组件 渲染在根节点app中
  20. var root = ReactDOM.createRoot(document.getElementById('app'));
  21. root.render(<Header />)
  22. </script>

定义类组件的内部数据

使用 this.state = 内部数据

 
  1. constructor(){
  2. super();
  3. this.username = 'alice' //不要直接使用别的变量的方式
  4. this.state = {
  5. obj:{
  6. name:'xiaodan'
  7. },
  8. from:'hubei'
  9. }
  10. }

在render函数中 通过this.state.属性名 访问, 一般可以通过解构的方式,解构出state里的数据,然后渲染

 
  1. render(){
  2. const {obj,from} = this.state;
  3. return <div>
  4. hello world
  5. {obj.name} -- {from}
  6. </div>
  7. }

修改类组件中内部数据

通过setState的语法修改数据

 
  1. // 简写的方式定义组件内部数据
  2. state = {
  3. obj:{
  4. name:'xiaodan'
  5. },
  6. from:'hubei'
  7. }
  8. 修改内部数据
  9. updateData=()=>{
  10. // 使用箭头函数的目的,就是你不用再担心this指向了,永远都是指向类组件的实例化对象的
  11. console.log('updateData');
  12. this.setState({
  13. from:'上海'
  14. })
  15. }

1.建议使用简写的方式定义组件内部数据,使用state

2.修改内部数据使用setState, 不要使用this.state.属性名 = 值的方式 直接去修改

3.setState修改数据是异步, 使用setState修改数据后, 会重新调用render函数,再次渲染模板

 
  1. // 方式一:
  2. this.setState({
  3. from:'上海'
  4. })
  5. 方式二:
  6. // setState可以接收2个参数,第一个是箭头函数,箭头函数的参数 prev就是state中所有的数据
  7. // 异步修改后, 可以在第二个参数里获取到最新修改的数据
  8. this.setState((prev)=>{
  9. // prev:就是state所有的数据
  10. return {
  11. from:'上海'
  12. }
  13. },()=>{
  14. console.log(this.state.from);
  15. // 可以获取到最新修改数据
  16. })

事件绑定

1.事件绑定使用onClick的方式,onClick后必须跟的是一个函数

 
  1. <button onClick={this.updateData}>修改数据</button>

2.建议使用箭头函数定义组件内部方法,可以一劳永逸的解决方法的this指向,指向类组件的实例化对象

 
  1. updateData=()=>{
  2. console.log('updateData');
  3. }

事件绑定传参方式

1.使用data-变量传参, 这个是h5原生的方式

 
  1. <button data-age="20" onClick={this.updateData}>修改数据</button>
  2. 获取参数:
  3. updateData=(ev)=>{
  4. console.log(ev.target.dataset.age);
  5. }

2.使用箭头函数传参

 
  1. <button onClick={()=>this.updateData('20')} >修改数据</button>
  2. 获取参数:
  3. updateData=(data)=>{
  4. console.log(data);
  5. }

建议大家使用箭头函数传参

类组件的props属性

组件传值的方式,直接在使用组件的时候, 通过props传值,类似vue组件props传值

 
  1. <Footer title="蜗牛" bg="red" height="50px" />

1.可以使用构造器constructor直接接收props

 
  1. constructor(props){
  2. super();
  3. console.log(props);
  4. }
  5. 模板中
  6. {this.props.title}

2.简写,省略构造器constructor

直接在模板中获取props,然后渲染

3.样式style,以 - 连接的属性, 比如 backgeound-color, font-size等都需要编程小驼峰式 backgroundColor ,fontSize

4.可以使用static defaultProps设置默认属性

 
  1. static defaultProps = {
  2. height:'100px',
  3. bg:'blue'
  4. }

设置了默认属性后, 如果你传,就使用传的值,不传就使用默认值

5.外部数据props是只读,不能被直接修改,react也是单向数据流,但是如果属性是对象,可以修改对象中某个属性值

 
  1. <script type="text/babel">
  2. // height , backgeound-color, title
  3. class Footer extends React.Component {
  4. // constructor(props){
  5. // super();
  6. // console.log(props);
  7. // }
  8. render(){
  9. const {title, bg,height} = this.props;
  10. // backgeound-color - > backgroundColor
  11. return <div style={{backgroundColor:bg,height:height}}>footer {title}</div>
  12. }
  13. }
  14. var root = ReactDOM.createRoot(document.getElementById('app'));
  15. root.render(<Footer title="蜗牛" bg="red" height="50px" />)
  16. </script>

搭建react环境

使用官方create-react-app

 
  1. npx create-react-app 项目名称, node版本高于 v14

总结:

  1. npx这个命令无需安装,node安装npx命令自动就有。

  2. create-react-app脚手架名字,本地安装临时脚手架,项目创建完毕后,销毁

下载插件:

在实际开发过程中,由于这些组件和函数组件它的结构都是固定的,所以可以使用一些插件将其生成出来

image-20230206115912857

使用rcc生成类组件,使用rfc生成函数组件

项目目录结构

src:存放开发的资源

—-components:代表组件

—-pages/views:代表页面(一般跟路由相关的页面)

—-utils:存放工具

—-http:封装异步请求

index.js:代表项目入口文件,启动项目首先加载这个文件

App.js这个文件代表项目根组件。默认在Index.js被加载

React中组件有两种后缀:以js结尾的文件,jsx结尾文件(推荐)

安装scss预处理

create-react-app安装的项目 ,已经内置了scss的配置, 你不需要配置,只需要安装node-sass sass-loader

 
  1. yarn add node-sass sass-loader -D //安装在开发包

表单组件

在react中 实时获取 表单组件 input , select, checkbox, radio 的值, 不能像vue使用v-model, 需要通过onChange监听数据的改变,通过setState

1.受控组件

表单value值交给react组件在管理,数据放在state, 通过this.setState来管理表单状态, 就可以理解为是受控组件

1.通过onChange监听表单事件

2.通过setState修改表单数据

 
  1. import React, { Component } from 'react'
  2. export default class AboutUs extends Component {
  3. state = {
  4. username:'小波'
  5. }
  6. updateName=(ev)=>{
  7. let curValue = ev.target.value; //获取当前input的value值
  8. this.setState({
  9. username:curValue
  10. })
  11. }
  12. render() {
  13. const {username} = this.state;
  14. return (
  15. <div>
  16. AboutUs --- {username}
  17. <input type="text" value={username} onChange={this.updateName} />
  18. </div>
  19. )
  20. }
  21. }

2.非受控组件

React 类组件Day02

2.非受控组件

表单的value交给dom来管理

2.1 使用React.createRef创建变量

1.定义ref变量

 
  1. 方式一;可以在构造器里定义
  2. constructor(){
  3. super();
  4. this.ageRef = React.createRef();
  5. }
  6. 方式二:或者通过简写的方式
  7. ageRef = React.createRef(); //创建一个ref的变量
  8. ageRef是自定义个一个变量

2.在表单上使用ref变量

 
  1. <input type='text' id='age' ref={this.ageRef} />

3.获取表单的dom以及value

 
  1. this.ageRef.current // 表单的dom
  2. this.ageRef.current.value
2.2 通过箭头函数获取事件对象
  1. 在表单上添加ref , 接收一个箭头函数,把事件对象event赋值给类组件的某一个变量,变量名是自定义, 这个我取了一个名字inputEl
 
  1. <input type="text" ref={(ev)=> this.inputEl = ev} />

2.获取表单的dom以及value

 
  1. this.inputEl // 表单的dom
  2. this.inputEl.value //表单的value值

注意: 初始的value 需要通过defaultValue 给, 这个defaultValue 就相当于是value值,只是非受控组件的时候需要使用下

提交表单数据

1.使用受控组件的方式提交表单

 
  1. import React, { Component } from 'react'
  2. export default class NewsList extends Component {
  3. state = {
  4. formData:{}
  5. }
  6. getFormValue=(ev)=>{
  7. console.log(ev.target.value); //当前dom的value
  8. var keyname = ev.target.name; //当前dom的name
  9. let values = this.state.formData;
  10. let newdata = {...values,[keyname]:ev.target.value };
  11. console.log(newdata);
  12. this.setState({
  13. formData:newdata
  14. })
  15. }
  16. // 提交form表单
  17. sendData= (e)=>{
  18. console.log(this.state.formData);
  19. e.preventDefault(); //阻止默认行为
  20. }
  21. render() {
  22. return (
  23. <>
  24. {/* <ul>
  25. <li>新闻1</li>
  26. <li>新闻1</li>
  27. </ul> */}
  28. <form >
  29. <ul>
  30. <li>
  31. <span>姓名:</span> <input type="text" name="username" onChange={this.getFormValue} />
  32. </li>
  33. <li>
  34. <span>密码:</span> <input type="text" name="password" onChange={this.getFormValue} />
  35. </li>
  36. <li>
  37. <span>手机号:</span> <input type="text" name="mobile" onChange={this.getFormValue} />
  38. </li>
  39. <li>
  40. <span>性别:</span>
  41. <label htmlFor="g1"> <input type="radio" name="gender" id='g1' value='1' onChange={this.getFormValue} /> 男</label>
  42. <label htmlFor="g2"> <input type="radio" name="gender" id='g2' value='2' onChange={this.getFormValue} />女</label>
  43. </li>
  44. <li>
  45. <span>城市</span>
  46. <select name='city' onChange={this.getFormValue}>
  47. <option value='1'>北京</option>
  48. <option value='2'>上海</option>
  49. </select>
  50. </li>
  51. </ul>
  52. <button type='button' onClick={this.sendData}>提交</button>
  53. </form>
  54. </>
  55. )
  56. }
  57. }

2.使用非受控组件提交表单

 
  1. import React, { Component } from 'react'
  2. export default class FormList extends Component {
  3. formData={};
  4. uernameRef = React.createRef();
  5. passwordRef = React.createRef();
  6. submitForm = (ev)=>{
  7. // 通过ref获取input 的值
  8. let username = this.uernameRef.current.value,
  9. password = this.passwordRef.current.value;
  10. let datas = {
  11. name:username,
  12. password:password
  13. }
  14. console.log(datas);
  15. ev.preventDefault(); //阻止默认行为,不让刷新页面
  16. }
  17. render() {
  18. return (
  19. <form onSubmit={this.submitForm}>
  20. <ul>
  21. <li>
  22. <span>姓名:</span><input type="text" name="username" ref={this.uernameRef} />
  23. </li>
  24. <li>
  25. <span>密码:</span><input type="text" name="password" ref={this.passwordRef} />
  26. </li>
  27. <button type='submit' >提交</button>
  28. </form>
  29. )
  30. }
  31. }

todolist作业讲解

给state中数组添加数据

 
  1. <input type="text" ref={(ev)=>this.nameEl=ev} />
  2. <button onClick={this.addTask}>添加</button>

js代码

 
  1. addTask = ()=>{
  2. let taskvalue = this.nameEl.value;
  3. let newtask = {
  4. id:+new Date(),
  5. name:taskvalue,
  6. isFinished:false
  7. };
  8. //方式一:
  9. // 如果你直接修改state里的数据,不会重新渲染模板,需要通过setState
  10. this.state.todolist.push(newtask);
  11. this.setState({
  12. todolist:this.state.todolist
  13. },()=>{
  14. this.nameEl.value = '';
  15. })
  16. //方式二:通过箭头函数的方式
  17. // this.setState((prev)=>{
  18. // // 获取todolist原来的数据,与新加的解构合并
  19. // let newdata = [...prev.todolist, newtask]
  20. // return {...prev,todolist:newdata}
  21. // },()=>{
  22. // // 可以获取到最新的数据
  23. // this.nameEl.value = '';
  24. // })
  25. }

父子组建传值

父传子

父传子通过属性props传值

父组件

 
  1. <TaskList todolist={todolist} ></TaskList>

子组件接收数据

 
  1. this.props.todolist //todolist是自定义一个属性, 通过父组件的属性传递

子传父

思路:在父组件里定义一个方法, 然后在使用子组件的时候, 传递一个函数类型的属性给子组件, 然后z在子组件通过调用这个方法进行传递

父组件

 
  1. delete: 自定义的一个函数类型属性
  2. <TaskList todolist={todolist} delete={this.deleteTask} ></TaskList>
  3. //删除任务
  4. deleteTask = (index)=>{
  5. this.state.todolist.splice(index,1); //splice删除某一项
  6. this.setState({
  7. todolist:this.state.todolist
  8. })
  9. }

子组件里:

 
  1. delete = (index)=>{
  2. // 把index给父组件
  3. this.props.delete(index); // 子传父组件 ,相当于调用父组件deleteTask方法
  4. }

React Day03

React Router路由

路由的2种方式 hash, history模式

hash:路由上会带有一个# 号,localhost:8081/#/home, 通过监听hash和hashChange来改变路由的

history:没有#号,看起来更好看点,localhost:8081/home, 通过j监听pushState和popState来改变路由

react router的最新版本 v6

react-router-dom: 浏览器 web环境路由,包含了react-router

react-router-native: 混合react-native的路由包含了react-router

所以我们在开发web端系统的时候,因为react-router-dom包含了react-router, 只需要安装react-router-dom

安装路由:

 
  1. yarn add react-router-dom // npm install react-router-dom

配置路由:

hash: HashRouter

history: BrowserRouter

 
  1. import {HashRouter,BrowserRouter} from 'react-router-dom';
  2. import Home from './pages/Home';
  3. import News from './pages/News';
  4. import Login from './pages/Login';
  5. function App() {
  6. return (
  7. // 路由器
  8. <HashRouter>
  9. <Routes>
  10. <Route path='/home' element={<Home></Home>}></Route>
  11. <Route path='/news' element={<News></News>}></Route>
  12. <Route path='/login' element={<Login></Login>}></Route>
  13. <Route path='/' element={<Login></Login>}></Route>
  14. </Routes>
  15. </HashRouter>
  16. );
  17. }

1.路由最外层一定需要有一个路由器 HashRouter 、BrowserRouter

2.HashRouter 或者BrowserRouter 必须要包裹住Routes

3.v6版本是通过Routes 来管理route

4.路由配置必须是在根组件app.js文件

路由的重定向

通过Navigate实现路由的重定向

 
  1. import {HashRouter,BrowserRouter,Routes,Route,Navigate} from 'react-router-dom';
  2. <Routes>
  3. ...
  4. <Route path='/' element={<Navigate to='/home'></Navigate>}></Route>
  5. ..
  6. </Routes>

404路由

当路由表里匹配不到 你访问的路由,可以使用404 组件 提示用户, 可以新建 404页面, 导入然后使用

 
  1. <Route path='*' element={<div>404页面,找不到了</div>}></Route>

路由的嵌套

Route可以嵌套Route 配置二级路由, 二级路由Route的配置和 第一级完全一样

第二级路由的path, 不写/

 
  1. <Routes>
  2. <Route path='/home' element={<Home></Home>}>
  3. <Route path='' element={<Navigate to='role'></Navigate>}></Route>
  4. <Route path='role' element={<Role></Role>}></Route>
  5. <Route path='user' element={<User></User>}></Route>
  6. </Route>
  7. </Routes>

第二级路由的path, 写/

 
  1. <Routes>
  2. <Route path='/home' element={<Home></Home>}>
  3. {/* /home/role */}
  4. <Route path='/home' element={<Role></Role>}></Route>
  5. <Route path='/home/role' element={<Role></Role>}></Route>
  6. <Route path='/home/user' element={<User></User>}></Route>
  7. </Route>
  8. </Routes>

第二级路由的path, 可以写/, 也可以不写;

不写/的时候,访问二级路由的实际地址: 父路由path+/+子路由path

如果写/形式 , 必须是 父路由path+/+子路由path

注意:配置二级路由的时候, 需要在父路由的模板里添加 Outlet

 
  1. import {Outlet} from 'react-router-dom'
  2. {/* Outlet类似vue里的view-router.作为路由容器,渲染子路由模板 */}
  3. <Outlet></Outlet>

类似a标签, 点击Link标签来导航到另一个页面进行渲染

 
  1. import {Link} from 'react-router-dom';
  2. <div className='menu'>
  3. <Link to='/news'>新闻</Link>
  4. <Link to='/home'>首页</Link>
  5. <Link to='/login'>登录</Link>
  6. </div>

NavLink是Link导航组件的升级版本,会添加一个当前激活的className

 
  1. import {NavLink} from 'react-router-dom';
  2. <div className='menu'>
  3. <NavLink to='/news' >新闻</NavLink>
  4. <NavLink to='/home'>首页</NavLink>
  5. <NavLink to='/login'>登录</NavLink>
  6. </div>

react路由和vue路由有什么不同, vue路由属于配置式路由, react属于编程式路由

路由的封装

1.先配置路由表
 
  1. import React from 'react'
  2. import Home from '../pages/Home';
  3. import News from '../pages/News';
  4. import Login from '../pages/Login';
  5. import User from '../pages/manage/User';
  6. import Role from '../pages/manage/Role';
  7. // 配置式
  8. let routeOption = [
  9. {
  10. path: '/home',
  11. element: Home,
  12. children: [{
  13. path: 'role',
  14. element: Role
  15. }, {
  16. path: 'user',
  17. element: User
  18. }]
  19. },
  20. {
  21. path: '/news',
  22. element: News
  23. }
  24. , {
  25. path: '/login',
  26. element: Login
  27. }]
  28. export default routeOption;

2.在组件中拼接成Routes管理Route, 导出这个路由组件

 
  1. import React, { Component } from 'react'
  2. import {Routes,Route,Navigate} from 'react-router-dom';
  3. import routeOption from './routes';
  4. export default class RouterIndex extends Component {
  5. render() {
  6. return (
  7. <Routes>
  8. {routeOption.map((option,index)=>{
  9. return <Route key={index} path={option.path} element={<option.element></option.element>}>
  10. {option.children && option.children.map((item)=>{
  11. return <Route path={item.path} element={<item.element></item.element>}></Route>
  12. })}
  13. </Route>
  14. })}
  15. </Routes>
  16. )
  17. }
  18. }

3.根组件中引入, 引入的是一个组件, 可以当成标签使用

 
  1. import RouteIndex from './router/RouterIndex'
  2. ...
  3. <RouteIndex></RouteIndex>

4.在组件中一定要有一个根标签

空标签,不会生成标签

 
  1. <></> 类似 vue里的 <template></template>

路由的懒加载

相当于把路由打包文件进行拆分, 减小整体打包的体积,属于按需加载

第一步:

导入路由懒加载的组件

 
  1. import React,{lazy,Suspense} from 'react';

第二步:

Suspense包裹住路由器

 
  1. <React.Suspense fallback={<div>loading</div>}>
  2. <HashRouter>
  3. ....
  4. </HashRouter>
  5. </React.Suspense>

第三步:

使用懒加载组件lazy

 
  1. const Home = React.lazy(()=>import('./pages/Home'));
  2. const News = React.lazy(()=>import('./pages/News'));
  3. <React.Suspense fallback={<div>loading</div>}>
  4. <HashRouter>
  5. ....
  6. <Route path='/home' element={<Home></Home>}></Route>
  7. <Route path='/news' element={<News></News>}></Route>
  8. </HashRouter>
  9. </React.Suspense>

注意:

  1. 组件名第一个字母必须大写
  2. 如果不使用封装的方式配置, 注释掉 import RouteIndex from './router/RouterIndex'

props属性

用来接收父组件给子组件传的属性,支持各种数据类型,字符串string,number, boolean,函数 等

属性的默认值

 
  1. export default class Loading extends Component {
  2. // props默认属性
  3. static defaultProps = {
  4. color:'green',
  5. size:'small'
  6. }
  7. }

属性的校验

react老版本中需要安装prop-types插件,新版本的react v18已经内置了 prop-types,所以在新版本中不需要安装,直接使用

先引入

 
  1. import propTypes from 'prop-types'

在组件里使用

 
  1. export default class Loading extends Component {
  2. // props属性校验
  3. static propTypes = {
  4. color:propTypes.string,
  5. size:propTypes.string
  6. }
  7. }

相当于color,size接收的是字符串类型的属性

可以校验的属性包括string, number, object,bool,array等

loading组件的封装

 
  1. import React, { Component } from 'react'
  2. import propTypes from 'prop-types'
  3. import './load.scss';
  4. var obj = {
  5. min:0.5,
  6. small:1,
  7. big:2
  8. }
  9. export default class Loading extends Component {
  10. // props默认属性
  11. static defaultProps = {
  12. color:'green',
  13. size:'small'
  14. }
  15. // props属性校验
  16. static propTypes = {
  17. color:propTypes.string,
  18. size:propTypes.string
  19. }
  20. constructor(props){
  21. super();
  22. this.styleObj = {
  23. '--color':props.color,
  24. transform:'scale('+obj[props.size]+')'
  25. }
  26. }
  27. render() {
  28. return (
  29. <div className="loading" style={this.styleObj}>
  30. <div className="box1">
  31. <div></div>
  32. <div></div>
  33. <div></div>
  34. <div></div>
  35. </div>
  36. <div className="box2">
  37. <div></div>
  38. <div></div>
  39. <div></div>
  40. <div></div>
  41. </div>
  42. </div>
  43. )
  44. }
  45. }
  46. // Loading.defaultProps = {
  47. // color:'green',
  48. // size:'small'
  49. // }

全局事件总线events

使用全局事件总线events,解决兄弟组件传值

安装

 
  1. yarn add events //或者 npm install events

属于发布订阅模式

先导出EventEmitter对象,为了数据针对同一个event对象进行发布和订阅的

新建util/event.js

 
  1. import {EventEmitter} from 'events'
  2. let eventObj = new EventEmitter();
  3. export default eventObj;

在页面中使用

父组件使用了A1,和A2组件

 
  1. import A1Com from '../../components/A1Com'
  2. import A2Com from '../../components/A2Com'
  3. export default class Role extends Component {
  4. render() {
  5. return (
  6. <div>Role
  7. <A1Com></A1Com>
  8. <A2Com></A2Com>
  9. </div>
  10. )
  11. }
  12. }

A1组件订阅,

 
  1. import eventObj from '../utils/event'
  2. // 订阅某一个数据 senddata
  3. // 类似vue里的mounted函数
  4. componentDidMount(){
  5. // addListener订阅自定义事件
  6. eventObj.addListener('senddata',(data)=>{
  7. console.log(data,'获取到a2组件发送的数据');
  8. })
  9. }

A2组件发布

 
  1. import React, { Component } from 'react'
  2. import eventObj from '../utils/event'
  3. export default class A2Com extends Component {
  4. // 针对订阅的 senddata 进行发布
  5. sendFun = ()=>{
  6. eventObj.emit('senddata',{
  7. username:'alice',
  8. age:20
  9. })
  10. }
  11. render() {
  12. return (
  13. <div>
  14. A2Com
  15. <button onClick={this.sendFun}>发送数据</button>
  16. </div>
  17. )
  18. }
  19. }

组件生命周期

React 18 废弃了三个生命周期:

  • componentWillMount,componentWillReceiveProps,componentWillUpdate
  • 新增了两个生命周期:static getDerivedStateFromProps(nextProps, prevState)getSnapshotBeforeUpdate(prevProps, prevState)

1.挂载阶段:constructor构造器的初始化数据,以及组件的渲染 constructor - > render -> componentDidMount

2.运行,更新阶段:修改内部数据state, 或者外部数据props修改, 都会引起render的重新渲染及重新

shouldComponentUpdate 我需要更新组件吗? 默认是true,更新, 一般性能优化

当你的state,props改变后, componentDidUpdate基本不用

shouldComponentUpdate - > render -> componentDidUpdate

3.卸载阶段: 可以在componentWillUnmount里进行定时器的清除工作等

组件的生命周期流程图

mm

componentDidMount: 类似vue里mounted,组件渲染完毕,一般在这里调接口,调函数,设置定时器等, 或者获取模板中的某个dom, 只会执行一次

componentDidUpdate:类似vue里updated, 组件更新完成后, 一般不会用到

componentWillUnmount: 类似vue里的destroyed,组件销毁

getDerivedStateFromProps:静态方法, 用来做父子组建传值的时候, 监听props的改变

 
  1. // 用来监听props属性的更新的
  2. static getDerivedStateFromProps(props,state){
  3. // 如果需要更新模板,返回一个对象,你可以返回null
  4. return {
  5. newName:props.username
  6. }
  7. }

一般返回一个对象, 如果不需要更新可以返回null

生命周期演示文件

 
  1. import React, { Component } from 'react'
  2. export default class News extends Component {
  3. constructor(){
  4. super();
  5. this.state = {
  6. count:10
  7. }
  8. console.log('1.constructor');
  9. }
  10. updateCount = ()=>{
  11. let oldcount = this.state.count;
  12. // 改变了组件内部数据,引发组件更新阶段
  13. this.setState({
  14. count:oldcount+1
  15. })
  16. }
  17. shouldComponentUpdate(props,state){
  18. console.log('我需要更新组件吗');
  19. return true
  20. }
  21. componentDidUpdate(){
  22. console.log('组件修改后, 已经渲染完毕');
  23. }
  24. componentDidMount(){
  25. console.log('3.componentDidMount,数据渲染完毕了');
  26. // 1. 调接口
  27. // 2. 获取模板中的dom
  28. // 3. 设置定时器
  29. this.timer = setInterval(()=>{
  30. console.log('打印数据');
  31. },2000)
  32. }
  33. componentWillUnmount(){
  34. console.log('组件被销毁');
  35. clearInterval(this.timer);
  36. }
  37. render() {
  38. console.log('2. render函数');
  39. return (
  40. <div>
  41. <button onClick={this.updateCount}>修改数据</button>
  42. </div>
  43. )
  44. }
  45. }

父子组建传值 getDerivedStateFromProps监听演示

父组件:

 
  1. import React, { Component } from 'react'
  2. import A1Com from '../../components/A1Com'
  3. export default class Role extends Component {
  4. state = {
  5. username:'alice'
  6. }
  7. updateName = ()=>{
  8. this.setState({
  9. username: '小刚'
  10. })
  11. }
  12. render() {
  13. return (
  14. <div>Role
  15. <button onClick={this.updateName}>改变username</button>
  16. <A1Com username = {this.state.username}></A1Com>
  17. </div>
  18. )
  19. }
  20. }

子组件:

 
  1. import React, { Component } from 'react'
  2. import eventObj from '../utils/event'
  3. console.log(eventObj);
  4. export default class A1Com extends Component {
  5. constructor(props){
  6. super();
  7. this.state = {
  8. newName:props.username
  9. }
  10. }
  11. // 用来监听props属性的更新的
  12. static getDerivedStateFromProps(props,state){
  13. // 如果需要更新模板,返回一个对象,不需要更新就返回null
  14. return {
  15. newName:props.username
  16. }
  17. }
  18. render() {
  19. return (
  20. <div>
  21. {this.props.username}
  22. <p>--------</p>
  23. {this.state.newName}
  24. </div>
  25. )
  26. }
  27. }

React Day4

路由跳转

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

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

 
  1. window.location.href = '/home' ;

mockJS使用步骤

基本使用

第一步:安装

 
  1. yarn add mockjs

第二步:新建mockjs/index.js

 
  1. import mockjs from "mockjs";
  2. mockjs.mock('/api/login','post',{
  3. code:1,
  4. msg:'成功',
  5. data:{
  6. username:'alice',
  7. token:'123456'
  8. }
  9. })

mockjs.mock(接口路径,接口方法,接口数据)

第三步:在入口文件index.js中引入

 
  1. import './mockjs/index.js'

如果有多个mock文件, 可以在mockjs新建多个, user,role, index

user.js, 默认导出一个函数

 
  1. import mockjs from "mockjs";
  2. export default ()=>{
  3. mockjs.mock('/api/user','get',{
  4. code:1,
  5. msg:'成功',
  6. data:{
  7. username:'alice',
  8. token:'123456'
  9. }
  10. })
  11. mockjs.mock('/api/login','post',{
  12. code:1,
  13. msg:'成功',
  14. data:{
  15. username:'alice',
  16. token:'123456'
  17. }
  18. })
  19. }

role.js,默认导出一个函数

 
  1. import mockjs from "mockjs";
  2. export default ()=>{
  3. mockjs.mock('/api/role','get',{
  4. code:1,
  5. msg:'成功',
  6. data:{
  7. username:'alice',
  8. token:'123456'
  9. }
  10. })
  11. }

在index.js中导入user.js和role.js

 
  1. import role from './role.js'
  2. import user from './user.js'
  3. role();
  4. user();

在项目入口文件

 
  1. import './mockjs/index.js'

拦截配置baseURL

说明: 如果项目开发过程中, 本来你已经有了baseURL, 你可以使用一个自定义的标签mocktag进行判断是否使用mock

 
  1. axiosEL({
  2. method:'post',
  3. url:'/api/login',
  4. data:this.state.formData,
  5. mocktag:true // mocktag 是自定义的
  6. }).then((res)=>{
  7. })

在axiosEL实例封装的时候, 使用请求拦截, 当mocktag是true, 使用mock接口, 修改baseURL

 
  1. import axios from "axios";
  2. let axiosEL= axios.create({
  3. baseURL:'http://www.test.com',
  4. timeout:5000
  5. });
  6. axiosEL.interceptors.request.use((config)=>{
  7. //mocktag 自定义标签,来判断是否使用mockjs
  8. if(config.mocktag) {
  9. config.baseURL = '/';
  10. }
  11. return config
  12. })
  13. // 相应拦截
  14. axiosEL.interceptors.response.use((res)=>{
  15. return res.data;
  16. })
  17. export default axiosEL;

在定义接口的时候, 也不需要使用域名 直接写

 
  1. mockjs.mock('/api/user','get',{
  2. code:1,
  3. msg:'成功',
  4. data:{
  5. username:'alice',
  6. token:'123456'
  7. }
  8. })

message弹框封装

1. 使用工具函数封装的方式

 
  1. // message: '恭喜你,这是一条成功消息',
  2. // type: 'success'
  3. // duration: 3000
  4. // showClose :是否显示关闭按钮
  5. // onClose: 被关闭时的回调函数
  6. function showMessage(obj){
  7. let body = document.querySelector('body');
  8. const {message, type='default',duration="3000",showClose=false,onClose} = obj;
  9. const clsNname = {
  10. success:'success',
  11. error:'error',
  12. warning:'warning',
  13. default:'default'
  14. }
  15. var msgbox = document.createElement('div');
  16. // 判断有没有message
  17. var boxs = document.querySelectorAll('.message-box');
  18. var boxHeight = 0,
  19. boxLen = boxs.length;
  20. if(boxLen>0) {
  21. Array.from(boxs).forEach((item)=>{
  22. boxHeight = boxHeight + item.clientHeight;
  23. })
  24. }
  25. msgbox.className=`message-box ${clsNname[type]}`;
  26. msgbox.style = `position:absolute; width:300px; border:1px solid #ccc;
  27. border-radius:5px; left:50%; font-size:13px; padding:10px; background:#fff; top:${10 * boxLen +boxHeight}px; transform:translateX(-50%)`;
  28. msgbox.style.zIndex = 1000;
  29. msgbox.innerHTML = message;
  30. if(showClose) {
  31. var close = document.createElement('div');
  32. close.innerText = 'X';
  33. close.style = `position:absolute; right:20px; top:10px; cursor:pointer;`;
  34. msgbox.appendChild(close);
  35. close.addEventListener('click',()=>{
  36. body.removeChild(msgbox);
  37. onClose && onClose(); // 关闭后执行代码
  38. })
  39. }
  40. body.appendChild(msgbox);
  41. setTimeout(()=>{
  42. msgbox && body.removeChild(msgbox);
  43. onClose && onClose(); // 关闭后执行代码
  44. },duration)
  45. }
  46. export default showMessage

页面中使用

 
  1. import message from '../utils/message'
  2. message({
  3. // type:'success',
  4. // duration:3000,
  5. showClose:true,
  6. message: '恭喜你,这是一条成功消息',
  7. // onClose:function(){
  8. // console.log('ooooo');
  9. // }
  10. })

2.使用组件的形式封装message

 
  1. import React, { Component } from 'react'
  2. import './msg.scss'
  3. export default class MsgTip extends Component {
  4. closeMsg = ()=>{
  5. this.props.send(); // 相当于调用父组件getMsgData
  6. }
  7. // componentDidUpdate(){
  8. // // 你也不能 在这里使用setState
  9. // }
  10. // 你不能在componentDidUpdate和 render使用setState
  11. render() {
  12. const {visible,content} = this.props;
  13. if(visible) {
  14. setTimeout(()=>{
  15. this.closeMsg();
  16. },3000)
  17. }
  18. return (
  19. <>
  20. {visible && <div className='msg-box'>
  21. <span onClick={this.closeMsg} className='close'>X</span>
  22. {content}
  23. </div>}
  24. </>
  25. )
  26. }
  27. }

使用message组件

 
  1. import MsgTip from '../components/messgeinfo/MsgTip'
  2. .....
  3. // 是用来接收子组件给我的数据
  4. getMsgData = (data)=>{
  5. console.log(data);
  6. this.setState({
  7. visible:false
  8. })
  9. }
  10. ....
  11. <MsgTip visible={this.state.visible} send={this.getMsgData} content={this.state.content}></MsgTip>

点击某一个按钮触发message的显示和隐藏

 
  1. this.setState({
  2. visible:true,
  3. content:'成功了'
  4. })

在项目中导入图片的2种方式

使用es6的方式

 
  1. import Testimg from '../../images/1.jpg'
  2. <img src={Testim}></img>

使用commonJS的方式

 
  1. let imgp = require("../../images/5.jpg")
  2. <img src={imgp}></img>

弹框组件封装

父组件使用

 
  1. <ModalBox visible={visible} title={title} send={this.getFromData}>
  2. form表单
  3. <div>aa</div>
  4. </ModalBox>

子组件

 
  1. import React, { Component } from 'react'
  2. import './model.scss';
  3. export default class ModalBox extends Component {
  4. // 关闭弹框
  5. closePop = ()=>{
  6. this.props.send(); //相当于调用父组件getFromData方法
  7. }
  8. render() {
  9. const {visible, title,children} = this.props;
  10. // this.props.children //获取组件内部自定义的内容
  11. return (
  12. <>
  13. {visible && <div className='modal-box'>
  14. <div className='title'> {title}
  15. <span className='close' onClick={this.closePop}>X</span> </div>
  16. <div className='content'>
  17. {children}
  18. </div>
  19. </div>}
  20. </>
  21. )
  22. }
  23. }

插槽

通过this.props.children渲染父组件自定的内容

如果不使用插槽, 直接在弹框里面 修改数据,我们需要注意父组件给子组件的数据不能直接修改, 需要把当前的数据赋值给 组件内部定义的数据

 
  1. //如何监听父组件给我的数据
  2. static getDerivedStateFromProps(props,state) {
  3. return {
  4. currentData:props.current
  5. }
  6. }

然后在修改input 的时候

 
  1. updateProduct = (ev)=>{
  2. this.state.currentData[ev.target.name] = ev.target.value; // 先赋值,再通过setState修改
  3. this.setState({
  4. currentData: this.state.currentData
  5. })
  6. }

Warning: A component is changing an uncontrolled input of type text to be controlled 报错分析_Michael18811380328的博客-CSDN博客

React Day05

时间格式转化

把时间格式 2022-12-04T02:00 转化为 2022-12-4 02:00:00的方式

方式一:

 
  1. endTime = endTime.replace(/T/, ' ');

方式二:

 
  1. endTime = new Date(endTime).toLocaleString().replace(/\//g,'-');

判断某个时间是否在当前时间之前或之后

传入一个时间格式为 2022-12-23 00:00:00 或者 2022/12/23 00:00:00 ,然后转化为毫秒比较

 
  1. compareTime(time2){
  2. // time2 = 2022-12-23 00:00:00
  3. var time1 = new Date(); // 今天当前的时间
  4. var time2 = new Date(time2); // 活动结束时间
  5. // time2 大于 time1 ,正在进行中, 小于 是已经结束
  6. if(time2.getTime() > time1.getTime()){
  7. return true;
  8. } else {
  9. return false;
  10. }
  11. }

计算2个时间相差多少天

 
  1. // time1, time2 ,计算2个时间之间相差 多少天
  2. var calcTime = (time1,time2)=>{
  3. var t1 = new Date(time1);
  4. var t2 = new Date(time2);
  5. let total = Math.abs(t1.getTime() - t2.getTime()) ;
  6. // total/1000/60/60 //小时
  7. // total/1000/60/60/24 天
  8. return total/1000/60/60/24;
  9. }

分页组件

样式文件 page.scss

 
  1. .page-number {
  2. display: flex;
  3. padding: 20px;
  4. margin-bottom: 20px;
  5. .num {
  6. margin-left: 20px;
  7. margin-right: 20px;
  8. }
  9. .pageing {
  10. span { margin-right: 20px; border: 1px solid #ccc; width:20px; height:20px; display: inline-block;}
  11. }
  12. }

分页组件PageNumber.jsx

 
  1. import React, { Component } from 'react'
  2. import './page.scss';
  3. export default class PageNumber extends Component {
  4. static defaultProps = {
  5. pageSize:10,
  6. pageIndex:1
  7. }
  8. // this.props.total , 当前页码pageIndex ,每页显示的条数 pageSize
  9. get totalPage(){
  10. // 总条数 除以 每页显示的条数 1.2
  11. return Math.ceil(this.props.total / this.props.pageSize)
  12. }
  13. pagebar = ()=>{
  14. let arr = [];
  15. for(let i=1;i<=this.totalPage;i++) {
  16. arr.push(<span key={i} onClick={()=>this.updatePage('pageIndex',i)}>{i}</span>)
  17. }
  18. return arr
  19. }
  20. // 修改pageIndex, pageSize
  21. updatePage = (kind,num)=>{
  22. // let num = typeof ev == 'number' ? ev : ev.target.value;
  23. this.props.paging({
  24. [kind]: Number(num)
  25. })
  26. }
  27. render() {
  28. const {pageIndex,pageSize} = this.props;
  29. return (
  30. <div className="page-number">
  31. <span>你当前在{pageIndex}页 </span>
  32. <span>总共有{this.totalPage}页</span>
  33. <span className="num">每页显示:
  34. <select name="" id="" onChange={(ev)=>this.updatePage('pageSize',ev.target.value)} defaultValue={pageSize}>
  35. <option value="5" >5</option>
  36. <option value="10">10</option>
  37. <option value="20" >20</option>
  38. </select>
  39. </span>
  40. <div className="pageing">
  41. {this.pagebar()}
  42. </div>
  43. </div>
  44. )
  45. }
  46. }

页面中使用

 
  1. import PageNumber from '../../components/paging/PageNumber';
  2. state = {
  3. ...
  4. pageObj:{
  5. total:0, //数据总共有多少条
  6. pageIndex:1, // 当前在哪个页面
  7. pageSize:10 //每页显示多少条
  8. }
  9. ...
  10. }
  11. // 用来接收分页组件给我的数据
  12. getPageData = (data)=>{
  13. // console.log(data,'得到数据');
  14. // 更新 pageIndex , 或者 pageSize
  15. this.setState({
  16. pageObj:{...this.state.pageObj, ...data}
  17. },()=>{
  18. // 调接口
  19. })
  20. }
  21. // 获取列表
  22. getShopList = async ()=>{
  23. // 调接口获取到实时的 pageIndex, pageSize
  24. const {pageIndex,pageSize} = this.state.pageObj;
  25. console.log({pageIndex,pageSize}, '最新的分页数据');
  26. let resData = await axiosEL({
  27. url:'/ticket/list',
  28. method:'get',
  29. params:{
  30. index:pageIndex,
  31. size:pageSize
  32. },
  33. mocktag:true
  34. })
  35. this.setState({
  36. shopList:resData.tempList,
  37. pageObj:{...this.state.pageObj, total:resData.total}
  38. })
  39. }
  40. {/* 数据总共有多少条 total,
  41. 当前在哪个页面 pageIndex
  42. 每页显示多少条 pageSize */}
  43. <PageNumber total={pageObj.total} pageIndex={pageObj.pageIndex} pageSize={pageObj.pageSize} paging={this.getPageData}></PageNumber>

分页思路: 通过 传 总共的数据条数 total, 当前在显示的页码数 pageIndex, 每页显示多少条 pageSize传给子组件, 然后通过total计算出 分页页码 , 在点击页码 和 修改 每页显示条数的时候, 需要给子组件回传 相应的参数 pageIndex, pageSize, 通过给子组件传一个类型为函数的属性paging, 在子组件中调用 this.props.paging() 来给父组件传值, 父组件获取到数据后更新当前的 pageIndex , pageSize,然后再调用接口

线性进度条

样式:

 
  1. .outer {
  2. width: var(--width);
  3. height:var(--height,10px);
  4. background: var(--bg);
  5. border-radius: 10px;
  6. position: relative;
  7. .inner {
  8. height: 100%;
  9. position: absolute;
  10. left: 0;
  11. top: 0;
  12. background: var(--color);
  13. border-radius: 10px;
  14. animation: run 2s linear forwards;
  15. }
  16. }
  17. @keyframes run {
  18. 0% {
  19. width: 0%;
  20. }
  21. 100% {
  22. width: var(--percent);
  23. }
  24. }

forwards: 代表动画执行完停留在结束的状态

backwards:代表动画执行完回到最初的状态, 默认值

进度条组件:

 
  1. import React, { Component } from 'react'
  2. import './main.scss';
  3. export default class ProgressBar extends Component {
  4. // bg="#000" color="red" height="20px" percent="20%"
  5. static defaultProps = {
  6. bg:'#ccc',
  7. color:'#ff6600',
  8. height:'10px',
  9. width:'100%',
  10. percent:'0%'
  11. }
  12. render() {
  13. const {bg,color,height,percent,width} = this.props;
  14. return (
  15. <div className='outer' style={{'--bg':bg,
  16. '--color':color,
  17. '--height':height,
  18. '--width':width,
  19. '--percent':percent}}>
  20. <div className='inner'></div>
  21. </div>
  22. )
  23. }
  24. }

页面中使用:

 
  1. {/* 底色背景, 百分比颜色color, height 进度条的百分比 percent */}
  2. <ProgressBar percent="10%" ></ProgressBar>

form表单补充

form表单数据比较多, 可以使用受控组件 ,value 与onChange配合一起使用监听input 的改变, 使用setState管理表单数据,

数据比较少的时候, 可以使用非受控ref

另外可以使用原生的方式

通过 new FormData(表单的dom) 可以获取到表单所有的数据, 是formdata ,Iterator类型数据,可以通过for of循环

 
  1. formObj={};
  2. submitForm = (ev)=>{
  3. // 拿到form 对象
  4. let formDom = ev.target;
  5. let bgdata = new FormData(formDom);
  6. // Iterator类型数据,可以使用for of循环
  7. for(let [key,value] of bgdata) {
  8. console.log(key);
  9. console.log(value);
  10. this.formObj[key] = value;
  11. }
  12. console.log(this.formObj, '表单数据');
  13. //调接口
  14. ev.preventDefault(); //阻止默认行为,不让刷新页面
  15. }

圆环进度条

arc 用法

 
  1. arc(x, y, radius, startAngle, endAngle, anticlockwise)

画一个以(x,y)为圆心的以 radius 为半径的圆弧(圆),从 startAngle 开始到 endAngle 结束,按照 anticlockwise 给定的方向(默认为顺时针)来生成。

方法有六个参数:

x,y 为绘制圆弧所在圆上的圆心坐标。

radius 为半径。

startAngle 以及 endAngle 参数用弧度定义了开始以及结束的弧度。

这些都是以 x 轴为基准。

参数 anticlockwise 为一个布尔值。

为 true 时,是逆时针方向,否则顺时针方向。

注意:arc()函数中表示角的单位是弧度,不是角度。

角度与弧度的 js 表达式:

 
  1. 弧度=(Math.PI/180)*角度

圆环组件代码:

 
  1. import React, { Component } from 'react'
  2. // 自定义的属性:
  3. // 百分比, 颜色 ,线的宽度 底色 ,画布的大小
  4. export default class ProgressCircle extends Component {
  5. drawCircle = ()=>{
  6. let canvasDom = document.getElementById('mycanvas');
  7. let ctx = canvasDom.getContext('2d'); // 2d的笔触
  8. // 把原始坐标点 移到 100 100
  9. const {color,bg,percent} = this.props;
  10. // 获取画布大小
  11. let canvasWid = canvasDom.width,
  12. canvasHeight = canvasDom.height;
  13. // 那就在canvas translate前save,后再restore
  14. // translate移动坐标点之前,保存现场
  15. ctx.save();
  16. ctx.translate(canvasWid/2,canvasHeight/2);
  17. // 底色圆环
  18. // 弧度=(Math.PI/180)*角度。
  19. ctx.beginPath(); // 开始绘制路径
  20. // (Math.PI / 180) * 360 = 2 * Math.PI
  21. ctx.arc(0,0,canvasWid/2 - 10,0,2 * Math.PI); // 绘制一个圆
  22. ctx.lineWidth = 10;
  23. ctx.strokeStyle = bg;
  24. ctx.stroke(); // 绘制路径
  25. ctx.closePath(); // 结束前面的路径
  26. // 百分比的圆环
  27. var innerCircle = ()=>{
  28. ctx.beginPath(); // 开始绘制新的路径
  29. // (Math.PI / 180) * 360 * percent = 2 * Math.PI * percent
  30. ctx.arc(0,0,canvasWid/2-10,0,2 * Math.PI* percent,false); // 绘制一个圆 , 默认是顺时针false , true逆时针
  31. ctx.lineWidth = 10;
  32. ctx.lineCap = 'round';
  33. ctx.strokeStyle = color;
  34. ctx.stroke(); // 绘制路径
  35. }
  36. innerCircle();
  37. ctx.restore(); //恢复到 translate之前的状态
  38. }
  39. componentDidMount(){
  40. this.drawCircle();
  41. }
  42. render() {
  43. return (
  44. <canvas id='mycanvas' width={this.props.width} height={this.props.height} style={{border:'1px solid #ccc'}}></canvas>
  45. )
  46. }
  47. }

页面中使用组件

 
  1. {/* <ProgressBar percent="10%" ></ProgressBar> */}
  2. <ProgressCircle bg="#ccc" color="blue"
  3. width="200px" height="200px" percent={0.4}></ProgressCircle>

React Day6

vite创建项目

vite是vue团队开发的一个打包工具, 他的对手是webpack

官网地址:

搭建第一个 Vite 项目

1.创建项目

 
  1. yarn create vite

2.输入项目的名称

3.选择你创建的项目,vue/ react

3.cd进入到你创建的项目目录, 安装依赖

 
  1. yarn
  1. 启动项目

     
      
    1. yarn dev // npm run dev

在项目cmd控制台, 按h可以查看快捷键

r: 重启服务

u:显示服务地址

o:从浏览器打开网页

c:清空控制台的信息

q:退出系统

安装css预处理Less环境

antd是react的ui框架, 底层是less开发的, 所以我们直接安装less的环境

 
  1. yarn add less

路由的搭建

和之前类组件创建路由的方式一样

 
  1. import {BrowserRouter,Routes,Route,Navigate} from 'react-router-dom'
  2. <BrowserRouter>
  3. <h1>Vite + React</h1>
  4. <div className='content'>
  5. <Routes>
  6. <Route path='/home' element={<Home></Home>}></Route>
  7. <Route path='/about' element={<About></About>}></Route>
  8. <Route path='/login' element={<Login></Login>}></Route>
  9. <Route path='/' element={<Navigate to='/login'></Navigate>}></Route>
  10. </Routes>
  11. </div>
  12. </BrowserRouter>

函数组件和类组件的区别

类组件特点:

1.类组件是基于面向对象class开发,有this, 有状态

2.类组件里有完整的内部数据,通过setState来管理数据

3.render函数返回一个虚拟dom

函数组件特点:

1.基于函数式编程,内部没有this,没有状态

2.函数组件没有内部状态,我们依赖其他的方案 (hooks)

3.react自动开启了严格模式, 函数内部的this指向 undefined, 内部的变量必须要先定义 再使用

4.return 返回一个虚拟dom

函数组件hooks编程

react 16.8版本以后, 官方推荐使用函数组件编程,引入hooks函数,按需引入

useState的使用

 
  1. import React,{useState} from 'react'
  2. export default function Login() {
  3. const [username,setUserName] = useState('alice')
  4. const updateUsername = function(data){
  5. console.log(data);
  6. setUserName(data);
  7. }
  8. return (
  9. <div>Login {username}
  10. <button onClick={()=>updateUsername('xiaowang')}>修改</button>
  11. </div>
  12. )
  13. }

1.hooks函数先引入再使用

2.useState定义内部变量

 
  1. const [username,setUserName] = useState('alice')
  2. 变量是自定义, 修改变量的函数的名字,约定 set + 变量, 方便维护管理代码,
  3. 变量的初始值 ,可以在使用 useState添加初始的value

3.定义的这个set函数也是异步, 修改数据后,不能马上打印修改的数据,类似类组件中的setState

4.定义内部数据的时候,可以定义字符串,数字,boolean,对象,数组等类型

5.使用useState定义变量的时候, 你的初始值是什么类型, 然后使用set函数的时候,也要保持类型的一致

 
  1. const [num,setNum] = useState(1);
  2. 方式一:
  3. <button onClick={()=>setNum(num+1)}>更新数字</button>
  4. 方式二:
  5. <button onClick={()=>setNum((oldnum)=>oldnum+1)}>更新数字</button>

注意:你通过useState定义的变量是不可以变, 不能直接修改,如果需要修改, 你可以进行一个拷贝再追加数据

 
  1. const addData = ()=>{
  2. let data1 = {name:'小刚',age:30};
  3. // 给数组添加新的数据,定义的内部数据是不可变
  4. let newdata = [...studentArr,data1];
  5. setStudentArr(newdata);
  6. }

因为set函数式异步,不能马上获取到修改的数据, 需要借助其他的hooks函数监听

useEffect的使用

函数组件里没有生命周期函数,可以借助useEffect模拟生命周期

类组件中生命周期函数

 
  1. componentDidMount 组件渲染完毕, 只调一次
  2. componentDidUpdate 组件更新完毕 , 每车次数组更新都会
  3. componentWillUnmount 组件卸载
  4. shouldcomponentUpdate 性能调试, 我需要更新组件吗?
模拟 componentDidMount
 
  1. import React, { useEffect,useState } from 'react'
  2. ....
  3. useEffect(()=>{
  4. // componentDidMount,
  5. console.log('只会执行一次, 在这里获取dom, 调接口,定时器等');
  6. },[])

useEffect接收2个参数, 第一个是箭头函数, 第二个是空的数组

模拟componentDidUpdate
 
  1. useEffect(()=>{
  2. console.log('0000');
  3. })

当useEffect的第二参数不写, 监听所有内部数据的更新

如果你想监听某个变量的更新,类似vue里的watch,第二个参数 以中括号形式, 写入你要监听的变量

 
  1. useEffect(()=>{
  2. console.log(username,'我获取新的值了 username');
  3. console.log(num,'我获取新的值了num ');
  4. },[username,num])
模拟componentWillUnmount
 
  1. useEffect(()=>{
  2. // componentDidMount, 只会执行一次, 在这里获取dom, 调接口,定时器等
  3. timer = setInterval(()=>{
  4. console.log('我是定时器');
  5. },1000)
  6. return ()=>{
  7. clearInterval(timer)
  8. }
  9. },[])

useRef的使用

1.获取dom对象

2.保存数据,当成普通的变量存取数据

类组件使用是React.createRef , 一般函数组件中使用 useRef来创建 ref变量

React.createRef() 和 useRef区别:

1.createRef每次渲染的时候, 会返回一个新的引用, 而useRef每次返回的是相同的引用

在函数组件中,你可以使用 React.createRef 和 useRef, 但是建议使用 useRef

1.获取dom对象
 
  1. import React, { useEffect, useRef } from 'react'
  2. export default function About() {
  3. const userRef = useRef(null); // 定义某一个ref变量
  4. useEffect(()=>{
  5. // componentDidMount, 只会执行一次
  6. // 可以获取模板中的dom
  7. let userDom = userRef.current;
  8. console.log(userDom);
  9. },[])
  10. return (
  11. <div ref={userRef}>
  12. About
  13. </div>
  14. )
  15. }
  1. 引入useRef ,import { useRef } from 'react'
  2. 定义一个ref变量 const userRef = useRef(null);
  3. 在模板上某个标签上使用ref变量 <div ref={userRef}>
  4. 获取ref变量, let userDom = userRef.current;

注意: react中属于严格, 不能使用关键字作为你的变量名;

2.保存数据,当成普通的变量存取数据

useRef用来保存数据的时候, 他数据的改变不会影响模板的重新, 类似普通变量;

但是通过useState定义的变量,一旦修改, 模板会重新渲染

 
  1. import React, { useEffect, useRef } from 'react'
  2. export default function About() {
  3. const testRef = useRef({username:'alice'});// 变量,保存数据
  4. useEffect(()=>{
  5. testRef.current = {username:'xiaohong '};
  6. },[])
  7. return (
  8. <div>
  9. About
  10. </div>
  11. )
  12. }

父子组件传值

父传子props属性传值

父组件

 
  1. import React from 'react'
  2. import LoadingBar from '../components/loading/LoadingBar'
  3. export default function News() {
  4. const getData = (data)=>{
  5. console.log(data,'接收子组件的数据');
  6. }
  7. return (
  8. <div>News
  9. <LoadingBar color='red' bg="#ccc" send={getData}></LoadingBar>
  10. </div>
  11. )
  12. }

子组件:

 
  1. import React from 'react'
  2. export default function LoadingBar(props) {
  3. console.log(props);
  4. const sendData = ()=>{
  5. props.send({from:'loading'})
  6. }
  7. return (
  8. <div>
  9. LoadingBar
  10. <button onClick={sendData}>发送</button>
  11. </div>
  12. )
  13. }

子组件接收父组件的值, 通过props, 然后子组件给父组件传 , 就是通过父组件在使用子组件的时候, 通过传一个类型为函数的属性, 然后在子组件 调用

默认属性defaultProps

子组件里定义

 
  1. LoadingBar.defaultProps = {
  2. color:'#000'
  3. }

LoadingBar是你自定义的函数名, 写在函数组件的外面

类型检验
 
  1. import propTypeO from 'prop-types'; // prop-types是新版react中内置的,不需要安装
  2. LoadingBar.propTypes = {
  3. color:propTypeO.string
  4. }

loading加载动画函数组件完整js代码

 
  1. import React from 'react'
  2. import propTypes from 'prop-types'
  3. import './load.less'
  4. var obj = {
  5. min:0.5,
  6. small:1,
  7. big:2
  8. }
  9. // props默认属性
  10. Loading.defaultProps = {
  11. color:'green',
  12. size:'small'
  13. }
  14. // props属性校验
  15. Loading.propTypes = {
  16. color:propTypes.string,
  17. size:propTypes.string
  18. }
  19. export default function Loading(props) {
  20. const styleObj = {
  21. '--color':props.color,
  22. transform:'scale('+obj[props.size]+')'
  23. }
  24. return (
  25. <div className="loading" style={styleObj}>
  26. <div className="box1">
  27. <div></div>
  28. <div></div>
  29. <div></div>
  30. <div></div>
  31. </div>
  32. <div className="box2">
  33. <div></div>
  34. <div></div>
  35. <div></div>
  36. <div></div>
  37. </div>
  38. </div>
  39. )
  40. }

样式文件:

 
  1. .loading {
  2. margin: 50px;
  3. position: relative;
  4. width: 60px;
  5. height: 60px;
  6. }
  7. .loading .box1,.loading .box2 {
  8. position: absolute;
  9. width: 100%;
  10. height: 100%;
  11. }
  12. .loading .box1 div,.loading .box2 div {
  13. position: absolute;
  14. width: 10px;
  15. height: 10px;
  16. // background: blue;
  17. background: var(--color,blue);
  18. border-radius: 50%;
  19. animation: loadingRun 1.5s linear infinite;
  20. }
  21. .loading .box2 { transform: rotate(45deg)}
  22. .loading .box1 div:nth-child(1) { left: 0; top: 0; animation-delay: 0; }
  23. .loading .box2 div:nth-child(1) { left: 0; top: 0; animation-delay: -0.2s; }
  24. .loading .box1 div:nth-child(2) { right: 0; top: 0; animation-delay: -0.4s;}
  25. .loading .box2 div:nth-child(2) { right: 0; top: 0; animation-delay: -0.6s;}
  26. .loading .box1 div:nth-child(3) { right: 0; bottom: 0; animation-delay: -0.8s;}
  27. .loading .box2 div:nth-child(3) { right: 0; bottom: 0; animation-delay: -1s;}
  28. .loading .box1 div:nth-child(4) { left: 0; bottom: 0; animation-delay: -1.2s;}
  29. .loading .box2 div:nth-child(4) { left: 0; bottom: 0; animation-delay: -1.4s;}
  30. @keyframes loadingRun {
  31. 0% {
  32. transform: scale(1);
  33. }
  34. 100% {
  35. transform: scale(0);
  36. }
  37. }

文件中使用

 
  1. import Loading from '../components/loading/Loading'
  2. ...
  3. <Loading color={'red'} size="big"></Loading>
  4. ...

进度环组件封装

组件ProgressCircle.jsx代码:

 
  1. import React, { useEffect } from 'react'
  2. ProgressCircle.defaultProps = {
  3. color:'#ff6600',
  4. bg:'#000',
  5. percent:0.4,
  6. width:'200px',
  7. height:'200px'
  8. }
  9. export default function ProgressCircle(props) {
  10. let timer = null;
  11. const drawCircle = ()=>{
  12. let canvasDom = document.getElementById('mycanvas');
  13. let ctx = canvasDom.getContext('2d'); // 2d的笔触
  14. const {color,bg,percent} = props;
  15. // 获取画布大小
  16. let canvasWid = canvasDom.width,
  17. canvasHeight = canvasDom.height;
  18. let x1 = canvasWid/2,
  19. y1 = canvasHeight/2;
  20. // 底色圆环
  21. // 弧度=(Math.PI/180)*角度。
  22. ctx.beginPath(); // 开始绘制路径
  23. // (Math.PI / 180) * 360 = 2 * Math.PI
  24. ctx.arc(x1,y1,canvasWid/2 - 10,0,2 * Math.PI); // 绘制一个圆
  25. ctx.lineWidth = 10;
  26. ctx.strokeStyle = bg;
  27. ctx.stroke(); // 绘制路径
  28. ctx.closePath(); // 结束前面的路径
  29. // 百分比的圆环
  30. var innerCircle = (range)=>{
  31. ctx.beginPath(); // 开始绘制新的路径
  32. // 度数转化为 弧度公式:
  33. // 弧度 = (Math.PI / 180) * 角度
  34. ctx.arc(x1,y1,canvasWid/2-10,0, (Math.PI / 180) * range ,false); // 绘制一个圆 , 默认是顺时针false , true逆时针
  35. ctx.lineWidth = 10;
  36. ctx.lineCap = 'round';
  37. ctx.strokeStyle = color;
  38. ctx.stroke(); // 绘制路径
  39. }
  40. // 思路:添加一个定时器, 每隔一段时间重复绘制,形成动画
  41. var beginRate = 0;
  42. timer = setInterval(()=>{
  43. beginRate+=5; // 度为单位
  44. // 360 * percent , 传度数进去
  45. if(beginRate<=360 * percent) {
  46. innerCircle(beginRate);
  47. } else {
  48. clearInterval(timer);
  49. }
  50. },16)
  51. innerCircle();
  52. }
  53. useEffect(()=>{
  54. drawCircle();
  55. },[])
  56. return (
  57. <canvas id='mycanvas' width={props.width} height={props.height} style={{border:'1px solid #ccc'}}></canvas>
  58. )
  59. }

角度转成弧度的公式如下:

弧度 = (Math.PI / 180) * 角度

页面中使用组件:

 
  1. import ProgressCircle from '../components/progress/ProgressCircle';
  2. ...
  3. <ProgressCircle bg="#ccc" color="red" percent={0.5}></ProgressCircle>
  4. ...

React Day7

约束性和非约束性组件

菜单封装

路由useRoutes

useMemo计算器属性

memo 用来缓存组件

useCallBack缓存函数

antd布局

约束性和非约束性组件

约束性(受控组件)

变量的value交给函数组件useState来管理, 使用 set函数修改数据

 
  1. import {useState } from 'react'
  2. const [username,setUserName] = useState('alice');
  3. const updateUsername = function(data){
  4. setUserName(data);
  5. }
  6. ....
  7. <input type="text" value={username} onChange={(ev)=>updateUsername(ev.target.value)} />

非约束性(非受控组件)

表单中变量的value值交给dom管理

想获取非受控组件里的value值, 通过ref变量

 
  1. import { useRef } from 'react'
  2. const inputRef = useRef(); //用来管理dom
  3. const getInput = ()=>{
  4. let inputDom = inputRef.current;
  5. console.log(inputDom.value);
  6. }
  7. <p>非受控组件</p>
  8. <input type="text" defaultValue={username} ref={inputRef} />
  9. <button onClick={getInput}>获取ref变量的值</button>

注意:1.所有的hooks函数,都需要先引入再使用

​ 2.hooks函数只能在函数组件中使用,不能在类组件中使用

​ 3.defaultValue默认值不能被修改,

map与flatMap,flat

flat()扁平化数组

flat用来扁平化数组

如果参数为空, 默认是1

 
  1. 用法
  2. var newArray = arr.flat(depth); // depth是number或者 Infinity

1.扁平化嵌套数组

 
  1. var arr = [1,2,3,[6,7,8,['aa','bb','cc']]];
  2. arr.flat(1);
  3. arr.flat(2);

2.Infinity扁平化任意深度

 
  1. var arr = [1,2,3,[6,7,8,['aa','bb','cc']]];
  2. arr.flat(Infinity); //无限极扁平 ,直到是一维数组

3.扁平化空数组[]

如果数组中有空的[], 可以通过过滤 或者for循环处理, 也可以通过flat扁平化处理,但是不能扁平化undefined

 
  1. var arr2 = ['t1','t2',[]];
  2. 经过flat扁平化后
  3. arr2.flat() // ['t1','t2'];

特别注意:不能扁平化undefined

map与flatMap的用法

 
  1. 需求:把数组中的每一项都乘以2
  2. const obj1 = [1, 2, 3, 4, 5];

第一种方式:使用map

 
  1. let newarr1 = obj1.map((value)=>{
  2. return value * 2
  3. })
  4. console.log(newarr1);

第二种:使用flatMap

 
  1. let newarr1 = obj1.flatMap((value)=>{
  2. return [value * 2]
  3. })
  4. console.log(newarr1);

使用flatMap与map不同的地方是需要用[]括起来

扁平化实际运用,准备数据

 
  1. const arr1 = [{
  2. name:'alice',
  3. age:20
  4. },{
  5. name:'laney',
  6. age:10
  7. },{
  8. name:'song',
  9. age:5
  10. }]

需求:过滤出年龄age大于5的数据, 然后对过滤后的数据重新组装, 比如年龄乘以 2

方式一:使用map处理数据

 
  1. let arr = personList.map((item)=>{
  2. if(item.age>5){
  3. item.from = 'china';
  4. item.age = item.age *2;
  5. return item
  6. } else {
  7. return []
  8. }
  9. })
  10. console.log(arr.flat());

1.对不符合条件的项, 必须要返回空的数组[],为了方便使用 flat扁平化

2.对过滤的数据使用flat arr.flat()

方式一:使用flatMap处理数据

 
  1. let arr = personList.flatMap((item)=>{
  2. if(item.age>5){
  3. item.from = 'china';
  4. item.age = item.age *2;
  5. return item
  6. } else {
  7. return [] // 不符合的条件, 这里一定要返回一个空的数组
  8. }
  9. })
  10. console.log(arr);

1.flatMap和map的使用方式一样,只是flatMap帮我们做了扁平化,我们得到的数据不需要再次使用flat过滤空项了,但是一样需要对不符合条件的项要返回一个[]

React中通过递归实现菜单的渲染

menus.js代码如下:

 
  1. var menudata = [
  2. {
  3. name:'菜单一',
  4. child:[
  5. {name:'a1',child:[{name:'a1-1'},{name:'a1-2'}]},
  6. {name:'a2'},
  7. {name:'a3',child:[{name:'a3-1'},{name:'a3-2',child:[{name:'a3-2-1'},{name:'a3-2-2'}]}]}
  8. ]
  9. },
  10. {
  11. name:'菜单二',
  12. child:[{name:'b1'},{name:'b2'},{name:'b3'}]
  13. },
  14. {
  15. name:'菜单三',
  16. child:[{name:'c1'}]
  17. },
  18. {
  19. name:'',
  20. child:[{name:'d1'}]
  21. },
  22. {
  23. name:'菜单四'
  24. }
  25. ];
  26. export default menudata;

方式一:map遍历+flat扁平化数组

 
  1. const [menuArr,setMenuarr] = useState([]);
  2. //map方式 , 递归 + []
  3. const menuFormat = (menus)=>{
  4. let menuToal = menus.map((item,index)=>{
  5. if(item.name) {
  6. return <li key={index}><span>{item.name}</span>
  7. {item.child && <ul> {menuFormat(item.child)} </ul>}</li>
  8. } else {
  9. return [] //返回一个空的数组,是为了使用flat拍平
  10. }
  11. })
  12. return menuToal;
  13. }
  14. useEffect(()=>{
  15. let menuAll = menuFormat(menudata).flat();
  16. setMenuarr(menuAll)
  17. },[])

方式二:flatMap

flatMap使用方式基本和map一样, 不一样的地方时flatMap已经处理了数据扁平化的工作,不需要再次使用flat处理

 
  1. const menuFormat = (menus)=>{
  2. let menuToal = menus.flatMap((item,index)=>{
  3. if(item.name) {
  4. return <li key={index}><span>{item.name}</span>{item.child && <ul> {menuFormat(item.child)} </ul>}</li>
  5. } else {
  6. return [] //返回一个空的数组,flatMap会自动扁平化
  7. }
  8. })
  9. return menuToal;
  10. }
  11. useEffect(()=>{
  12. let menuAll = menuFormat(menudata);
  13. setMenuarr(menuAll)
  14. },[])

方式三:filter过滤+map循环 递归

先通过filter把符合条件的数据过滤出来, 然后再对过滤好的数据进行map循环遍历,以及递归拼接虚拟dao的菜单格式

 
  1. const menuFormat = (menus)=>{
  2. let menuA = menus.filter((option)=>{
  3. return option.name
  4. })
  5. let menuTotal = menuA.map((item,index)=>{
  6. return <li key={index}><span>{item.name}</span> {item.child && <ul>{menuFormat(item.child)}</ul> } </li>
  7. })
  8. return menuTotal;
  9. }
  10. useEffect(()=>{
  11. let menuAll = menuFormat(menudata);
  12. setMenuarr(menuAll)
  13. },[])

路由useRoutes

useRoutes可以帮我们简化路由的配置,可以类似vue里的配置式路由, 处理好会生成Routes + Route的配置结构

在src目录新建router/RouterIndex.jsx

第一步:导入useRoutes

 
  1. import {useRoutes} from 'react-router-dom'

第二步:配置路由 表

 
  1. let routes = [{
  2. path:'/about',
  3. element:<About></About>
  4. },{
  5. path:'/login',
  6. element:<Login></Login>
  7. },
  8. {
  9. path:'/layout',
  10. element:<Layout></Layout>,
  11. children: [
  12. {
  13. path: '',
  14. element: <Navigate to='home'></Navigate>
  15. },
  16. {
  17. path: 'home', // 第二级
  18. name:'首页',
  19. element: <Home></Home>
  20. },
  21. {
  22. path: 'users', // 第二级
  23. name:'用户',
  24. element: <User></User>
  25. }]
  26. }
  27. ]

第三步:在函数组件中使用useRoutes

 
  1. export default function RouterIndex() {
  2. let routerList = useRoutes(routes)
  3. return routerList;
  4. }

第四步:在根组件app.jsx中 导入 路由组件

 
  1. import RouterIndex from "./router/RouterIndex"
  2. ...
  3. <BrowserRouter>
  4. <RouterIndex></RouterIndex>
  5. </BrowserRouter>
  6. ...

注意: RouterIndex必须放在 路由容器BrowserRouter 或者 HashRouter里面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸡前端选手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值