react-个人笔记

1. React 基础语法

0 .安装

- 直接创建 React

npx create-react-app my-app

- 通过 Vite 创建 React

# npm 6.x
npm init vite@latest my-react --template react

# npm 7+, 需要额外的双横线:
npm init vite@latest my-vue-app -- --template react

# yarn
yarn create vite my-react --template react
​
# pnpm
 pnpm create vite

1.React的开始

- ReactDOM.rander(element,container,callback)
ReactDOM.render(
  // 虚拟DOM
  <h1>我是react</h1>,
  // 插入元素容器
  document.querySelector('#app'),
  // 执行完成回调函数
  function () {
    console.log('我ok了');
  }
);

2. 元素渲染

1.元素是构成 React 应用的最小单位

注意事项: 定义 React 虚拟DOM 如果加“”则是文本渲染,标签不会渲染

- 面试:ReactDOM.render()方法有几个参数?
- 答题:3个   1.虚拟DOM 2.渲染的容器 3.回调函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aSzcxRAI-1660141981379)(E:\学习素材\Wed\Web-Train\note\React\assets\1658849336020.png)]

2. {} 的语法
- 注释需要 写在{} 内部
- 可以解析 `Nunber`、`String`等基本数据类型和表达式,但不支持objcet
- 面试: 什么叫JSX?
- 答: JSX 是一个看起来像 XML 的JS 语法

- 面试: JSX语法
- 1. JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 2. 它是类型安全的,在编译过程中就能发现错误。
- 3. 使用 JSX 编写模板更加简单快速。
3. 代替的标签属性
  • classNamehtmlFor 代替 class 和的 for
- 面试: JSX语法中标签的哪2个属性不能使用
- 答: 属性 class 属性for 使用 className 和 htmlFor 代替

3. React 组件分类

1. 函数组件

注意事项:命名规范 和 js 命名规范一样 , 首字母大写

// 函数组件 没有constructor 不能定义state
function Tick() {
  return (
    <div>
      <div>我是一个函数组件</div>
    </div>
  );
}
// <Tick></Tick> 使用的时候 把函数当成标签的形式使用
// React 函数组件 返回的是React 虚拟DOM元素
// 函数组件调用方式
ReactDOM.render(Tick(), document.querySelector('#app'));
ReactDOM.render(<Tick></Tick>, document.querySelector('#app'));
ReactDOM.render(<Tick />, document.querySelector('#app'));
2. 类组件
- 继承  React.Component 类
class Test extends React.Component {
  render() {
    return (
      <div>
        <div>我是一个类组件</div>
      </div>
    );
  }
}
// 标签直接解析
ReactDOM.render(<Test />, document.querySelector('#app'));
3. 复合组件
let isOn = false;
function App() {
  return <div>{isOn ? <Tick /> : <Test />}</div>;
}
ReactDOM.render(<App />, document.querySelector('#app'));

4. 状态管理

class Demo extends React.Component {
  // 构造器
  constructor() {
    // super 写在constructor 第一行
    super();

    // 定义状态
    this.state = {
      // 相当于 vue 的 data
      num: 1,
      time: new Date(),
    };
  }

  // 在类组件中 方法和方法之间不能写逗号(,)

  // 渲染Dom
  render() {
    return (
      <div>
        <h1>状态管理</h1>
        <div>{this.state.time.toLocaleString()}</div>
        <button>按钮</button>
      </div>
    );
  }
  // 定义一个方法 更新视图
  Time() {
    this.setState({
      time: new Date(),
    });
  }

  // 生命周期
  // 挂载后
  componentDidMount() {
    setInterval(() => {
      this.Time();
    }, 1000);
  }
}
// 小程序中 this.setData  React 中 this.setState
ReactDOM.render(<Demo />, document.querySelector('#app'));

5. 遍历

  • react 遍历 数组使用 map 函数
注意点:
1. map 遍历的时候如果输出元素,必须需要加 return
2. map 遍历数组时,需要在变量的元素属性加 key,唯一值

this.state.arr.map((item,index) => { return(<li key={index}>{item}</li>)})

6. 条件渲染

1. 三目运算符
{ this.state.flag ? (<div/>) : (<div>) }
2. || && 运算符
{ this.state.flag && <div/>  }
3. 函数
ions(num){
    if(num === 1){
      return <div>周一</div>
    }else if(num === 2){
      return <div>周一</div>
    }else if(num === 3){
      return <div>周一</div>
    }else if(num === 4){
      return <div>周一</div>
    }else if(num === 5){
      return <div>周一</div>
    }else if(num === 6){
      return <div>周一</div>
    }else if(num === 7){
      return <div>周一</div>
    }
  }

{this.ions(1)}

7. Css 样式

// 在官方 create-react-app 脚手架中不支持less 和 sass
// 1. num run eject 暴露config 文件  通过webpack.config.js 配置less sass
// 2. 通过vscode,webstorm自带的less sass插件实现

// React 样式处理 css in js 理念
    return(
      <div>
        {/*	1. 行内样式 - style	*/} 
        <h1 style={{color:"red"}}>样式</h1>
        <h2 style={this.state.style}>样式2</h2>
        {/*	2. 类名 - classNmae 注意全局污染,因为react 中没有样式隔离,不像vue中可以隔离样式	*/} 
        <h3 className='color'>样式3</h3>
        {/*	3. css module 直接用 module.css , 起名一定要带上module,引入的时候要用对象形式就可以了	*/} 
        <p className={aCss.p}>React样式处理 <span>css module</span></p>
        <p className={this.state.flag ? 'kk':''}>动态</p>
      </div>
    )

8. 事件绑定

  • 事件 on+首字母大写事件名 = {执行函数} 例如: onClick
export default class Tab extends Component {
    constructor(){
    	super()
        this.state = {}
        
        // 改变 func 的 this 指向
        this.func = this.func.bind(this)
    }

    func(){
        // this 指向自身 react重写函数  使用指向 undf
    }
    func2 = ()=>{
        // this 指向 class
    }
}

2. 组件 (Class)

1. 受控组件

- 什么是受控组件?
- 答: input框被React组件 控制
实现步骤
1.组件的state中声明一个组件状态
2.给input 设置一个value 的state
3.给input 设置一个onChange 事件
4.将input 的 target值 更新 state

this.state = {
    InpVal:""
}

rander(){
	retrun (
        <div>
        <input type="text" value={InpVal} onChange={this.handleChangePublish} />
        </div>
    )
}
handleChangePublish(e)=>{
    // 通过e 获取元素value值
	this.setState({
      InpVal:e.target.value
    })
}

2. 非受控组件(操作DOM)

1. createRef

(React 17+ 的写法 官方推荐第一种 )

非受控组件是通过手动操作dom 方式获取input 的 value,input的value不受当前React组件控制的 state中的状态控制

实现步骤
1.导入 createRef 函数
2.通过 createRef 函数 创建一个ref对象 存储到myInp 实例中
3.为 Dom 元素 添加 ref 属性, ref={myInp}
4.在事件处理函数中,通过this.myInp.current 获取当前dom元 将value值 赋值给state


import React,{Component,createRef} from 'react'
export default class Tab extends Component {
    this.state = {
        InpVal = ''
    }
	myInp = createRef()
	rander(){
        retrun (
            <div>
            <input type="text" ref={myInp} onChange={this.handleChangePublish} />
            </div>
        )
	}

	handleChangePublish = () => {
        this.setState({
          InpVal:this.myInp.current.value
        })
	}
}
2. ref = {“值”}

(非空组件 React <17 的写法 已废弃 有bug)

实现步骤
1.属性值 ref = {"值"}是字符串 获取你的Dom元素
2.执行函数 this.resf.定义的字符串 获取Dom元素

rander(){
        retrun (
            <div>
            <input type="text" ref={"msg"} onChange={this.handleChangePublish} />
            </div>
        )
	}
handleChangePublish = ()=> {
    this.setState({
          InpVal:this.refs.msg.value
        })
}
3. ref ={()=>{}}

(React <17 的写法 没有废弃 )

​ ref ={(msg)=>{this.msg = msg }}

实现步骤
1.属性值 ref ={(msg)=>{this.msg = msg }} 是回调函数 获取你的Dom元素
2.执行函数 this.msg 获取Dom元素

rander(){
        retrun (
            <div>
            <input type="text" ref={(msg)=>{this.msg = msg} onChange={this.handleChangePublish} />
            </div>
        )
	}
handleChangePublish = ()=> {
    this.setState({
          InpVal:this.msg.value
        })
}

3. 组件通信

组件是独立的单元,默认只能使用自己的状态数据

- 什么是组件通信?
- 答:为了能让各个组件直接进行互相沟通,这个过程就是组件传值
1. 父传子
1. props
实现步骤
1.父组件提供传递数据 state
2.给子组件标签 添加一个属性,他的属性值就是父组件定义的 state 数据
3.子组件通过 props 接收父组件中传过来的数据
注意:子组件是函数组件:	使用 props 接收传递数据(参数传值)
	 子组件是类组件:	使用this.pros 接收传递数据
 
// 定义父组件
export default class Fon extends Component {
    constructor(){
        super()
        this.state = {
            str:"给儿子的值"
            // 可以传 string,number,boolder,function,objcet....
            // 函数会携带参数 function (str){}
        }
    }
	render(){
        return(
       	<div>
            <h2>我是父组件</h2>
            {/*子组件* 定义一个属性 给子组件传值 /}
            <Son text={this.state.str} />
          <div/>
        )
    }
}

// 函数子组件
function Son({text}){
    // 结构赋值获取 props对象 中的 text参数
    return(
    	<>
        	<h2>我是子组件</h2>
        	<div>{text}</div>	// 给儿子的值
        </>
    )
}

// 类子组件
class Son extends Component{
    // 结构赋值获取 this.props对象 中的 text参数
    const {text} = this.props
    rander(){
        retrun(
        	<>
            	<h2>我是子组件</h2>
        		<div>{text}</div>	// 给儿子的值
            </>
        )
    }
}
2. children

children 相当于 vue2 中的插槽 可以传(文本/标签/函数)

- children 属性是什么?
- 答: 标识该组件的子节点,只要组件内部有子节点,props中就该有属性
- 父组件
export default class Child extends Component {
	constructor(){
        super()
            this.state = {
              num:1
        }
  	}
  	render(){
        return (
          <div>
          	<ChonA>
          	// 文本
          	123
          	// 标签
          	<h1>传递文本<h1>
          	// 函数
          	{this.add}
          	</ChonA>
          </div>
    	)
    }
    add(){
    	// 函数体
	}
}

- 子组件
function ChonA (porps){
  return(
    <>
      {porps.children[0]} // 123
      {porps.children[1]} // <h1>传递文本<h1>
      {porps.children[2]} // add 函数
    </>
  )
}
3. createContext

Provider 包裹需要传值的子组件(孙组件也能取值)

Consumer 包裹需要传值的函数

- 1. 引入 createContext  (可实现跨组件通信 父->子->子)
import React,{Component,createContext} from 'react'
- 2. 结构出 Provider,Consumer
const {Provider,Consumer} = createContext()
- 第二种类写法
const MyContext = createContext()
// MyContext.Provider	MyContext.Consumer 结合使用
// 类组件 声明 static contextType = MyContext;

- 父组件
export default class Context extends Component {
   state = {
    msg: "疯狂星期四"
  } 
   render(){
      return(
      <>
        // Provider 传入的数据 
        <Provider value={this.state.msg}>
            <h3>夸组件传值</h3>
            <ContextA/>
        </Provider>
      </>
    )
  }
}

- 子组件
function ContextA() {
  return (
    <div>
      <Component>
          {value=>{return <div>value</div>}}
       </Component>
       <ContextB></ContextB>
    </div>
  )
}

- 孙 函数组件
function ContextB() {
  return (
    <div>
      <Component>
          {value=>{return <div>value</div>}}
       </Component>
    </div>
  )
}

- 孙 类组件
class ContextB extends Component {
  // 第二种写法	静态声明后---可以直接获取 this.context
  static contextType = MyContext;
  render() {
    return (
      <>
        <div>ContextA</div>
        <div>{this.context}</div>
        <ContextB />
      </>
    );
  }
}
2. 子传父
- 事件传值
export  default class Input extends Component {
    constructor(){
    super()
    this.state = {num:1}
    }
    rander(){
        return(
        <>
           // 子组件传入自定义事件
           <Son handlClick="this.handlClick" />
        </>
        )
    }
    
    // 父级自定义事件
    handlClick(data){
        this.state.num = data // 10
	}
}


class Son extends Component {
    constructor(){
        super()
        this.state = {
            num:10
        }
  	}
     rander(){
        return(
        <>
           // 事件触发 传值给父组件
           <button onClick="this.props.handlClick(this.state.num)"></button>
        </>
        )
    }
}
3. 跨组件通信
1. pusbub.js 跨组件通信插件

npm i pubsub-js --save-dev 安装

// 引入插件
import PubSub from 'pubsub-js'
// 父组件
export default class Com extends Component {
	state = {
		msg:"你好"
	}
	render(){
        return(
          <>
          	<ComA />	// 传值给A
        	<ComB />
          </>
}
// 子组件 A
function ComA(props) {
	return(
		<>
			- pubsub.js 跨组件通信 订阅 PubSub.subscribe
			- msg 发布元素  data 接收的值
			<button onClick={()=>{PubSub.subscribe('xxx', (_,data) =>{console.log(data);} )}}>订阅消息</button>
		</>
	)
}

// 子组件 B
function ComB(props) {
  return (
    <>
     	- pubsub.js 跨组件通信 发布 PubSub.publish
     	- xxx 是发布名称 data 是发布数据
      	<button onClick={()=>{PubSub.publish('xxx',data)}}>给兄弟传个东西</button>
    </>
  )
}

4. 组件优化(Class)

  • Component 的 俩个问题
  1. 只要执行setState(),即使不改变状态数据,组件也会出现 rander()
  2. 只当前组件重新rander(),就会自动出现rander 子组件,纵使子组件没有用到父组件的任何数据
  • 原因

Component 中的 shouldComponentUpdate() 总数返回true

  • 解决
办法1:
	重写shouldComponentUpdate()方法
	比较新旧state 或 props 数据, 如果发生改变才返回true,如果没有返回false
办法2:
	使用PureComponent
    PureComponent重写了shouldComponentUpdate(), 只有state 或 props数据有变化才返回 true
	注意:
		只是进行state和props数据 浅比较,如果数据对象内部数据遍历,返回false

项目中一般使用PureCompoent 来优化


办法1:	shouldComponentUpdate 闸门判断

  // 渲染 rander 闸门判断
  shouldComponentUpdate(nextProps, nextState) {
    console.log(this.props, this.state); // 目前的props,state
    console.log(nextProps, nextState); // 接下来要改变的目标props,state 
      // 父级组件 props 没用传值则无视 值判断 state
      retrun !this.state.xxx === nextState.xxx
      // 子组件 通常没用子级的state  判断 props
      retrun !this.props.xxx === nextProps.xxx
 }
    	

方法2:	重写 Component --->  PureComponent
export default class Context extends Component {} ->
export default class Context extends PureComponent {}

不能对 state 进行操作

5. 插槽

原理: 利用组件传值原理 返回一个 rander函数 rander函数内部返回一个 Class组件 ,在预留插槽处写{ this.props.rander(参数) }

// 父组件
export default class Com extends Component {
	state = {
		msg:"你好"
	}
	render(){
        return(
          <>
            <ComA  rander={(name)=><ComB name={name}></ComB> /> // 给子组件 A 的插槽插入 组件 B 形成父子
          </>
}
// 子组件 A
function ComA(props) {
    let state = {
        name:"张三"
    }
	return(
		<>
        	{this.props.rander(state.name)}	// 预留插槽位 传参
		</>
	)
}

// 子组件 B
function ComB(props) {
  return (
    <>
      我是子组件
      {}
    </>
  )
}

6. setState

  • setState(setateChang,[callback]) — 对象式的 setState
    • setateChang 为状态改变对象
    • callback 是可选的回调函数, 它的状态更新完毕、界面也更新后(rebder调用后) 才开始调用
  • setState(updater,[callback]) — 函数式setState
    • updater 是返回stateChange对象的函数
    • updater 可以接收到state 和 props

总结:

  	1. 对象式的setState 是函数式的setState 的简写方式
  		2. 使用原则:
       			1. 如果新状态不依赖于原状态	==> 使用对象式
       			2. 如果新状态依赖于原状态 ==> 使用函数式
       			3. 如果需要在setState() 执行后获取最新的状态数据要在第二个callback函数中读取
1. 对象式
this.setState({num:this.state.num+1},()=>{
	console.log(num
	// 回调函数
})

2.函数式
this.setState((state,props)=>{
	retrun{num:state.num+1}
    },
    ()=>{
        // 回调函数
    }
)

3. 数据规则

class 类 三大关键字 extends 继承 super 继承 static 定义静态属性

- prop-types  props的效验 增加代码健壮性
- 对于组件来说,props是外部传入的,无法保证组件数据类型一致性

- 实现步骤
- 1. 安装  cnpm i prop-types --save-dev
- 2. 引入 PropTypes 包
- 3. 使用 组件名.propTypes = {} 给组件效验规则

- 常见结构:array , bool , func , number , objcet , string,symbol ,element 元素,any 泛型(所有)
- React元素类型 node 文本节点 


- props 效验默认值 defaultProps:xxx
- 必填项: isRequired

- 引入效验插件 
import PropTypes from 'prop-types'

组件.propTypes= {
  - 定义必填 末尾跟 .isRequired
  msg:PropTypes.约束  
  
  - any.isRequired 仅限制必填
  sgr:PropTypes.any.isRequired
  
  - oneOfType([]) 多可选类型
  obj:PropTypes.oneOfType([PropType.string,PropType.number])
  
  - oneOf([]) 多可选值 (必选之一)
  colors: PropTypes.oneOf(['red', 'blue'])
  
  - 结构效验(对象) shape({})
  people: PropTypes.shape({
    	name: PropTypes.string.isRequired,
    	id: PropTypes.number.isRequired
	})
}


class ComB extends Component {
	- static defaultProps:默认值
	static defaultProps = {
		msg:1 // 如果外面没有传递 msg 进来, msg 将默认为 1
	}
  	
}

4.Hooks (Func)

hooks 的本质,其实就一套能够使函数组件更强大,更灵活的“钩子”

1. useState 状态

函数组件 的useState 酷似 类组件的 state

// 引入
import {useState} from 'react'

export default function Hooks () {
    - 使用 useState
    const [num,setNum] = useState(0)
    
    - num 是 存储state的变量 setNum 是修改 num 的函数方法
    
    retrun (
    	<div>
        	<div>{num}</div>	// 0
            <button onClick={setNum(num+1)}></button> // 点击一次 num+1
        </div>
    )
}

总结:	
1. useState() 初始值 只会在组件第一次时渲染
2. useState() 执行后返回的是一个 [] 第一个参数是 初始值,第二个参数是操作state 的函数
3. useState() 不能嵌套在 if/for 其他函数中 

2. useEffect 生命周期

函数组件 的useEffect 酷似 类组件的 生命周期钩子

// 引入
import {useState,useEffect} from 'react'

export default function Hooks () {
    - 使用 useState
    const [num,setNum] = useState(0)
    const [str,setStr] = useState("123")
    - 使用 useEffect
    {
        useEffect(()=>{}) // 全部监听 (挂载,渲染)
        useEffect(()=>{},[]) // 不监听参数 监听挂载
        useEffect(()=>{return ()=>{}},[]) // 卸载组件
        useEffect(()=>{},[str,num]) // 监听指定的state改变 默认执行一次
        
    }
    
    
    retrun (
    	<div>
        	<div>{num}</div>	// 0
            <button onClick={setNum(num+1)}></button> // 点击一次 num+1
        </div>
    )
}

总结:
1. useEffect(()=>{}) 分为执行函数 / 检测对象
2. 如果没有 检测对象 就检测所有对象
3. 根据参数不同 实现的生命周期不同

类似 Class 组件
componentDidMount 组件挂载
componentDidUpdate 组件更新
componentWillUnmount 组件销毁前

3. useRef Dom操作

函数 的useRef 类似与 Class 组件 ceateRef

import {useRef} from 'react'

export default function Hooks () {
	
	rander(){
		return(
			<>
				<div ref={myRef}>我是Ref</div>
				<button onClick={getRef}>获取元素</button>
			</>
		)
	}
	
	 function getRef(){
      console.log(myRef); // 获取到元素
     }
}

4. useContext

函数 的useContext 接收 creactContext 传递的 useState 遍历和函数

import {Component,useContext,createContext} from 'react'

- 创建 reacteContext 对象
const MyContext = createContext()

function Hooks() {
  const [num, setNum] = useState(0);
  render(
  	retrun(
  		<>
  			<div>我是父级</div>
  			- 将 useState 状态传给子孙级
  			<MyContext.Provider value={{num, setNum}}>
  			</MyContext.Provider>
  		</>
  	)
  )
 }
 
 function A (){
 	retrun(
 		<>
 			<div>A</div>
 		</>
 	)
 }
 
 function B (){
 - 接收 父级 传递的 useState 状态
 const {num, setNum} = useContext(MyContext)
 
 const add = () => {
    setNum(num + 2);
 };
 
 	retrun(
 		<>
 			<div>B</div>
 			<div>B组件</div>
              <div>{num}</div>
              <button onClick={add}>num+2</button>
 		</>
 	)
		
}
 

5. 组件优化(Func)

1. memo()
  • 作用: 优化组件, 防止组件重新渲染
  • 缺点: 如果参数中,含有函数不能阻止
  • 优点: 缓存组件
  • 场景: 不给子组件传递函数,并且子组件根据自身State来渲染
import React, { memo } from 'react';
- 子组件
1. export default memo(CompA)
2. export default memo(()=>{
    	retrun(
    		<div></div>
    	)
  	})
2. useMemo
  • 作用: 优化组件, 防止组件重新渲染
  • 缺点: 如果参数中,含有函数不能阻止
  • 优点: 缓存State 状态
  • 场景: 不给子组件传递函数,并且子组件根据自身State来渲染
import React, { memo,useMemo, useState } from 'react';
- 父组件
export default function ContentA() {
    - 创建 State 状态
    let [str,setStr] = useState("Howw");
    let [num,setNum] = useState(0);
    - 创建函数 
    let add = () => {
        setNum(num+1)
        setStr("Howw")	- str 没用改变
    }
    - 调用 useMemo 对 State 进行缓存
    let userInfo = useMemo(()=>{
        return {num,str} // 缓存值
    },[str])  - 监听依赖 str  没用发生改变 子组件rander 不执行
}

- 子元素
memo((props)=>{
    return (
        <div>
          {props.userInfo.num}
          {props.userInfo.str}  
        </div>
  );
})


总结:
seMemo(()=>{return {} },[]) 
- 依赖为空 则不会 rander 传递数据的子组件 / 诺有 依赖 则 依赖改变 rander渲染
- 诺改变的 State 值 前后一致 则也不 触发 rander
3. useCallback
  • 作用: 优化组件, 防止组件重新渲染
  • 缺点: 不能缓存 State 值
  • 优点: 可以缓存函数 (返回值是函数,每一次都是新函数)
  • 场景: 调用函数改变State 数据
import React, { memo, useState,useCallback } from 'react';
- 父组件
export default function ContentA() {
    - 创建 State 状态
    let [str,setStr] = useState("Howw");
    let [num,setNum] = useState(0);
    - 创建 useCallback 函数 
    let useAdd = useCallback(() => {
        setNum(num+1)
        setStr("Howw")	- str 
        // 如果需要设置 依赖 则
        strStr(str=>"Howw")
    },[]) - 如果不设置依赖 则函数只能触发一次 , 设置后 只对依赖属性进行更改
}

- 子元素
memo((props)=>{
    return (
        <div>
          {props.userInfo.num}
          {props.userInfo.str}  
          <button onClick={props.useAdd}></button>
        </div>
  );
})

总结:
useCallback(() => {
  // 函数体
},[])
- 如果不设置依赖 则函数只能触发一次 , 设置后 只对依赖属性进行更改
- 如果函数执行 修改的数据与之前一样 则之后默认更新一次后 只执行一次

5.生命周期

1. 旧生命周期

18.x 以后 使用该生命周期 需要加上 UNSAFE_ 前缀

componentWillUpdate	渲染前
componentWillReceiveProps 组件将要接收prpos(第一次不触发)
componentWillMount	挂载前

2. 新生命周期

getDerivedStateFrompProps 状态值完全取决于Props  结合 static 使用 需要返回值 了解即可
getSnapshotBeforeUpdate   更新之前  返回快照(更新前的状态值)

3. 常用生命周期

// 生命周期三个阶段

1.挂载阶段   执行顺序  constructot -> render -> componentDidMount
constructot 		创建组件时,  先初始化(只执行一次) 
						作用:	1.初始化state 	2.创建Ref 	3.使用bind解决函数this指向问题
render 				每次组件渲染都会触发 
						作用: 渲染UI视图(不能调用 this.setState) 虚拟DOM 转化为 真实DOM
componentDidMount 	组件挂载(完成DOM渲染)后执行:初始化的时候(只执行一次)   
						作用:1.DOM操作

2.更新阶段   执行顺序 shouldComponentUpdate -> rander -> componentDidUpdate 
shouldComponentUpdate 	是否渲染页面 (retrun false 不渲染)             
							作用:阻止特定情况的页面渲染
rander             		每次组件渲染都会触发                              
							作用:渲染UI视图(注意:不能在 rander 中 调用 this.setState)
componentDidUpdate		(更新前的props值,更新前的state值,快照值)       组件更新后 (DOM渲染后)                            
							作用:1.DOM操作: 可以获取到更新后的DOM元素 2.发送网络请求

3.卸载阶段
componentWillUnmount 	组件卸载                                       
							作用:清除定时器/清除订阅

6. 路由

1. Router 5

安装

pnpm i react-router-dom@5

1. 路由组件

路由组件和一般组件的区别 : 路由组件 props接收三个固定的属性 history location math

history:
        go: ƒ go(n)							-	
        goBack: ƒ goBack()					-
        goForward: ƒ goForward()
        push: ƒ push(path, state)
        replace: ƒ replace(path, state)
location:
        pathname: "/"
        search: ""
        state: undefined
match:
        params: {}
        path: "/"
        url: "/"

路由跳转的俩种模式 replace 替换当前页面 push 默认模式

  • WithRouter 加工一般组件

  • BrowserRouter history 路由模式 因为state保存在 history 对象中 刷新数据不会丢失

  • HashRouter hash 路由模式(URL的哈希值) !因为 state 保存在 history 对象中 hash不调用 history 刷新会丢失数据

    • Link 路由导航 属性:to = “/a” 模糊匹配 /a 层级匹配 顺序不能乱 replace 重定向 (默认关闭)

      <Link to="/a"></Link>
      
    • NavLink 高亮导航 追加了一个属性 activeClassName

      <NavLink to="/a" activeClassName = 'active'}></NavLink>
      或者/
      <NavLink to="/a" className={isActive => {return isActive ? 'active' : ''}></NavLink>
      
    • Switch 提高 Route 匹配效率 通常配合Route使用

      • Route 注册路由 属性: path=“/a” 路由地址 component = {模块名称} 跳转模块 index=”/a“ 默认路由 (不能和path同时出现在标签上) exact={true} 严格匹配 (有可能导致二级路由出问题)
      <Switch>
      	<Route index="/a" component={ComA} />
      	<Route path="/a" component={ComA} />
      </Switch>
      
    • Redirect 重定向 当所有路径不匹配的情况下 调用

      <Switch>
      	<Route index="/a" component={ComA} />
      	<Route path="/a" component={ComA} />
      	<Redirect to="/a"></Redirect>
      </Switch>
      

2. 多级路由

// 引用 样式	
import ComA from './ComA';
import ComB from './ComB';
import { Redirect, Route, Switch, Link } from 'react-router-dom';
// 一级路由组件	B
function Aroute() {
  return (
    <div>
      <div className="Cont">
        <div>
         {/* Link 导航标签 to 去那 */}
          <Link to="/a">组件A</Link>
          <Link to="/b">组件B</Link>
        </div>
        <div className="Cont-box">
         {/* Switch 包裹 Route 防止 匹配多个同名路径 */}
         {/* {/* Route 注册 路由 path路由地址 component 跳转模块* index 默认展示(index和path不能同时存在) /} */}
         {/* <Route index="a" component={ComA} /> exact 开启精准匹配(不出问题不使用) */}
          <Switch>
            <Route path="/a" component={ComA} />
            <Route path="/b" component={ComB} />
            <Redirect to="/a"></Redirect>
          </Switch>
        </div>
      </div>
    </div>
  );
}

export default Aroute;

// 二级路由组件	C

import ComC from './ComC';
import ComD from './ComD';
import { Redirect, Link, Route } from 'react-router-dom';
function ComB() {
  return (
    <div>
      <h2>ComB</h2>
      <div>
        <Link to="/b/c">C组件</Link>
        <Link to="/b/d">D组件</Link>
      </div>
      <div>
        <Route path="/b/c" component={ComC}></Route>
        <Route path="/b/d" component={ComD}></Route>
        <Redirect to="/b/c"></Redirect>
      </div>
    </div>
  );
}

export default ComB;

3. 路由传值

1. params 传值
// 父组件  - 字符串拼接 /参数
<Link to={`{/a/${1}/${组件1}`}>1组件</Link>
<Link to={`{/a/${2}/${组件2}`}>2组件</Link>

- 声明接收 params	/:id 一一对应
<Route path="/a/:id/:text" component={ComE}></Route>

// 子组件  获取 params 对象
const {id, text} = props.match.params


2. search 传值
- 父组件
<Link to={`/b/d/e/?id=${item.id}&text=${item.text}`}>{item.name}</Link>;
<Route path="/b/d/e" component={Com}></Route>

- 子组件
console.log(props.location.search);  // urlencoded	"?id=xxx&text=xxx"
//  import qs from 'querystring' 转换方式已废弃

4. state 传值
- 父组件  - 传递一个对象
<Link to={{ pathname: '/b/d/e', state: { id: item.id, text: item.text } }} >
<Route path="/b/d/e" component={Com}></Route>
    
- 子组件
const { id, text } = props.location.state;

4. 编程式路由

1. 路由组件
- 通过代码 实现路由跳转

- 1. push 模式
props.history.push()
- 2. replace 
props.history.replace()

- 传值方式

// 1. params 传值
props.history.push(`/b/d/e/${id}/${text}`);
props.history.replace(`/b/d/e/${id}/${text}`);

// 2. search 传值
props.history.push(`/b/d/e/?id=${item.id}&text=${item.text}`);
props.history.replace(`/b/d/e/?id=${item.id}&text=${item.text}`);

// 3. state 传值
props.history.push({ pathname: '/b/d/e', state: { id, text } });
props.history.replace({ pathname: '/b/d/e', state: { id, text } });



- 前进 后退  go(1/-1) goBack()	goForward()

- 前进 
props.history.goForward()
props.history.go(n) 前进n步

- 后退
props.history.goBack()
props.history.go(-n) 后退n步
2. 一般组件

withRouter 加工一般函数 返回路以组件 (用于 非路由组件引用路由跳转的场景)

import {withRouter} from "react-router-dom" 

export default withRouter(xxx组件)

2. Router 6

安装

pnpm i react-router-dom@6

1. Router5-6 的区别

  • 组件 <Routes/> 替换 <Switch/> 新增 <Outlet/> <Navigate>

  • 属性 <Route/> 属性 component={About} 被替换为 element={<About/>}

  • 函数 新增 useParamsuseNavigateuseMatch

    1. <Navigate> 跳转 只要渲染就会切换路径 属性 replace = {false} 跳转模式 默认 false

      <Route path="/" element={<Navigate to="/a" />}></Route>
      
    2. <Route/> 属性 caseSensitive 区分大小写

    3. <NavLink/> 高亮 className 写法 end 子级选中了 父级失去高亮

      <NavLink to="/home" className={({isActive})=>{ isActive?'active','' }}> <NavLink/>.
      
    4. <Outlet/> 指定路由呈现的位置

2. Hooks 路由传值

1. useRoutes
  • 路由表
import React, { lazy } from 'react';
import { Navigate } from 'react-router-dom';
const ComA = lazy(() => import('../component/Aroute/ComA'));
const ComB = lazy(() => import('../component/Aroute/ComB'));
const ComC = lazy(() => import('../component/Aroute/ComB/ComC'));
const ComD = lazy(() => import('../component/Aroute/ComB/ComD'));
const routeList = [
  {
    path: '/a',
    element: <ComA />,
  },
  {
    path: '/b',
    element: <ComB />,
    children:[	-- 二级路由
      {
        path: 'c',
        element: <ComC />,
      },
      {
        path: 'd',
        element: <ComD />,
      },
    ]
  },
  {
    path: '/',
    element: <Navigate to="/a" />,
  },
];

export default routeList;


-- 父级 组件

import { useRoutes, Link } from 'react-router-dom';
import router from '../../routes';
-- useRoutes 
const elementRoutes = useRoutes(router);
{elementRoutes}

- B组件

import {  Outlet } from 'react-router-dom';
- Outlet 指定路由呈现的位置
<Outlet />

- C组件
<Link to="d">D组件</Link> <Link to="c">C组件</Link>
2. useParams
  • 获取路由 Params 值
- 路由
path: 'e/:id/:name/:text'
- 获取
import {  useParams } from 'react-router-dom';
const { id, name, text } = useParams();
3. useMatch
  • 获取路由 Params 值 (不常用)
- 路由
path: 'e/:id/:name/:text'
- 获取
import {  useMatch } from 'react-router-dom';
const { params } = useMatch('/b/d/e/:id/:name/:text');
const { id, name, text } = params;
4. useSearchParams
  • 获取路由 search 值
- 路由
path: 'e'
- 获取
import {  useSearchParams } from 'react-router-dom';
// 获取 search 获取 search 函数 、 setSearch 修改 search 数据
const [search,setSearch] = useSearchParams();
// 获取 search
const id = search.get('id');
const name = search.get('name');
const text = search.get('text');
// 修改 search
setSearch('id=2&name=jj&text=jb');
5. useLocation
  • 获取路由 search 值
- 路由
path: 'e'
import {  useLocation } from 'react-router-dom';
// 获取 search 获取 search 函数 、 setSearch 修改 search 数据
const data = useLocation();
// 获取 search
const {search} = data
  • 获取路由 state 值
- 路由
path: 'e'
import {  useLocation } from 'react-router-dom';
// 获取 search 获取 search 函数 、 setSearch 修改 search 数据
const data = useLocation();
// 获取 search
const {state} = data

3. Hooks 编程式导航

useNavigate
// 引入 useNavigate
import {  useNavigate } from 'react-router-dom';

// 获取 navigate
const navigate = useNavigate()

// 跳转
navigate('url',	   // 路由路径 
{   -- 可写
	replace:false, // 跳转模式
	state:{}	   // 携带 state 参数
})

// 前进
navigate(1)
// 后退
navigate(-1)

4. Hooks 其他

1. useInRouterContext
  • 判断:是否在路由上下文中
console.log(useInRouterContext());
2. useNavigationType
  • 判断: 是否进入路由的模式
console.log(useNavigationType());
3. useOutlet
  • 显示: 当前点击的路由组件 的嵌套组件(点中项)
console.log(useOutlet());
4. useResolvedPath
  • 给他一个 URL 以及参数 返回 一个URL解析对象
console.log(useResolvedPath('e/?a=1&b=2&#231'));
{pathname: '/b/d/e/', search: '?a=1&b=2&', hash: '#231'}
    hash: "#231"
    pathname: "/b/d/e/"
    search: "?a=1&b=2&"
}

3. 懒加载

  • lazy

  • Suspense

    // 引入
    import React, { lazy, Suspense } from 'react';
    // 引入 组件 lazy
    const ComA = lazy(() => import('./ComA'));
    
    // Suspense 包裹路由组件 	fallback 加载中提示组件
    <Suspense fallback={<h1>Loding....</h1>}>
        <Switch>
            <Route path="/a" component={ComA} />
            <Route path="/b" component={ComB} />
            {/* 多个 Route path 相同 会一同战术 !- 不推荐 */}
            <Route path="/b" component={PusSubA} />
    
            <Redirect to="/a"></Redirect>
        </Switch>
    </Suspense>
    

7. 错误边界

保证代码正常执行

- 在报错 子组件的引用父组件定义 getDerivedStateFromError 钩子 结合 static 捕获后代生命周期的错误 ----- 只用于生产环境(上线)


// 定义父组件
export default class Fon extends Component {

	state = {
		hasError:""  - 1. 存储 子组件的状态
	}
		
	- 2. 当 Fon 的子组件报错的时候,会触发 getDerivedStateFromError 调用,并携带错误信息
	static getDerivedStateFromError(error){
		console.log(error)
		retrun {hasError:error} - 需要返回一个对象
	}
	- 3.渲染组件时出错 (钩子)
	componentDidCatch(error,info){
	  - 统计错误 反馈给服务器
	  console.log(error,info)
	}
	
	render(){
        return(
       	<div>
            <h2>我是父组件</h2>
            - 4.处理子组件的错误 
            {this.state.hasError ? <h2>当前网络错误</h2>  : <Son />}
          <div/>
        )
    }
}

// 函数子组件
function Son(){
    return(
    	<>
        	<h2>我是子组件</h2>
        </>
    )
}

8. redux

安装 redux

pnpm add redux

安装 redux-thunk - 处理 异步 action

pnpm add redux-thunk

1. 文件布局

- 文件布局
	- App.jsx			// app 主文件
	- pages 			// 路由文件
	- components		// 组件UI
	- containers		// 容器层
	- redux
		- store.jsx		// 创建 store 对象
		- reducer		// 封装 reducer 工具函数
			- index.jsx // 合并 reducer 根据函数 然后导出
			- xxx.jsx	
		- action		// 生成 action 对象
			-xxx.jsx
		- constant.jsx	// 定义 action 对象 type类型 的常量值 (可写) (防止写错)

2. API

  • store.getState() 获取 store 的状态值 第一次调用 携带undf 记得设置默认值
  • store.dispatch() 调用封装在reducer 的工具函数 参数: action 执行对象 { type, data: value * 1 }
  • store.subscribe() 调用监听 store 函数 携带回调函数

3. 代码流程

1. store.jsx

  • createStore : 创建 redex (可能已废弃)
(1). 引入 createStore 创建 redex
// 引入 处理异步 action 的 中间件 applyMiddleware
import { createStore, applyMiddleware } from 'redux';

(2). 引入 reducer 组件
import countReducer from './reducer';
import pentReducer from './reducer/Pent';

// 引入 redux-thunk 用于支持异步 action 
import thunk from 'redux-thunk';
// 引入 redux-devtools-extension 用于支持 rudex 开发者工具
import { composeWithDevTools } from 'redux-devtools-extension';

(3). 合并 reducer 
const allReducer = combineReducers({ list:pentReducer, count:countReducer });

(4). 暴露 store
1. 有异步 无开发工具
export default createStore(allReducer, applyMiddleware(thunk));	
1. 无异步 有开发工具
export default createStore(allReducer, composeWithDevTools());	
1. 有异步 有开发工具
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)));	

2. reducer.jsx


// 调用定义好的常量 (可写)
import { ADDCREMENT, DELCREMENT } from './constant';

(1). // 暴露 定义的 reducer  函数
expoer default function countReducer(preState = 0,action) {
// preState 之前的状态 第一次执行默认 undf 所以要设置默认值  action 执行对象
   
    (2). 从 action 中获取:type , data
    const {type,data} = action;
    
    (3). 根据type 决定执行的步骤方法
    switch (type) {
        case ADDCREMENT: // 如果是加
          return preState + data;
        case DELCREMENT: // 如果是减
          return preState - data;
        default:
      	  return preState; // 初始化
  }
}

3. action.jsx

(1). 定义 reducer 工具函数 需要的 action 对象
 
// 调用定义好的常量 (可写)
import { ADDCREMENT, DELCREMENT } from './constant';

const addcontAction = data => ({ type: ADDCREMENT, data });
const delcontAction = data => ({ type: DELCREMENT, data });

// 异步 action 返回的是 函数
const addAysnAction = (data, time) => {
  return dispatch => {
    setTimeout(() => {
      dispatch(addcontAction(data));
    }, time);
  };
};

(2). 暴露出去
export { addcontAction, delcontAction,addAysnAction };

4. constant.jsx

(1). 定义 action 对象 type类型 的常量
const ADDCREMENT = '+';
const DELCREMENT = '-';

(2). 暴露出去
export { ADDCREMENT, DELCREMENT };

5. container 容器

- (1). 引入 Count 的 UI组件
import CountUI from '../../components/Comt/Comt';
- (2). 引入 action
import { addcontAction, delcontAction, addAysnAction } from '../../redux/action';

- (3). 引入 connect 用于 连接UI组件 与 redux
import { connect } from 'react-redux';

-(5). mapStateToProps 的返回值 传递给 ContUI组件 props 的 state 对象
function mapStateToProps(state) {
  return { count: state };
}

-(5). mapDispathToProps 的返回值 传递给 ContUI组件 props 的 操作 state 对象
function mapDispathToProps(dispath) {
  return {
    add: data => {
      dispath(addcontAction(data));
    },
    del: data => {
      dispath(delcontAction(data));
    },
    //  异步加
    asyncAdd: (data, time) => {
      dispath(addAysnAction(data, time));
    },
  };
}

(4). 使用 connect()()  创建并暴露一个 Cont 容器组件
export default connect(mapStateToProps, mapDispathToProps)(CountUI);

// 简写
export default connect(state => ({ count: state.count }), {
  add: addcontAction,
  del: delcontAction,
  asyncAdd: addAysnAction,
})(CountUI);

6. App.js

import React from 'react';
- (1). 引入 Cont 容器组件
import Cont from './containers/Cont';
- (2). 引入 store
import store from './redux/store';

function App() {
  return (
    <div>
     - (3). 添加 store
      <Cont store={store} />
    </div>
  );
}

export default App;

// 或
import React from 'react';
// 引入 Cont 容器组件
import Cont from './containers/Cont';
// 引入 store
import store from './redux/store';
//
+ import { Provider } from 'react-redux';

function App() {
  return (
	<>
+      <Provider store={store}>
        <Cont />
+      </Provider>
    </>
  );
}

export default App;


7. component 组件引入

1. rudex - component
(1). 引入 store 用于获取全局状态
import store from './redux/store';

(2) 引入 action 用于创建 action对象
import { addcontAction, delcontAction,addAysnAction } from './redux/action';

// 加法
addNum = type => {
    const { value } = this.select;
    // (4). 调用 reducer 工具
    store.dispatch(addcontAction(value * 1));
  };


// 减法
delNum = () => {
    const { value } = this.select;
    // (4). 调用 reducer 工具
    store.dispatch(delcontAction(value * 1));
 };
 
 // 异步
  yibu = () => {
    const { value } = this.select;
    // 调用reducer 工具 异步任务
    store.dispatch(addAysnAction(value * 1, 1000));		-- 可以不适应 异步rudex
    
    // 正常异步任务
    setTimeout(()=>{
		 store.dispatch(addcontAction(value * 1));
    },1000)
  };

 render() {
    return (
      <div>
        {/* (3). store.getState() // 接收reducer 返回的数据 第一次调用 返回默认值  */}
        <h1>当前求和{store.getState()}</h1>
        <div>
          <input type="text" ref={c => (this.select = c)} />
        </div>
        <div>
          <button onClick={this.addNum}>+</button>
          <button onClick={this.delNum}>-</button>
          <button onClick={this.yibu}>异步</button>
        </div>
     </div>
  );
     
 (5). 检测 redex 中 状态的改变 调用 this.setState({}) 重新渲染 render
 componentDidMount() {
   // 检测 redux 中状态的变化
   store.subscribe(() => {
     this.setState({});
   });
 }
 // 或 在 mian.jsx 文件内
     
 // 引用 store 用于获取全局状态
import store from './redux/store';

// 监听 store 的数据改变 改变则重新渲染
store.subscribe(() => {
  ReactDOM.createRoot(document.getElementById('root')).render(<App />);
});
 
2. react-rudex - component
import React, { Component } from 'react';

export default class Anli extends Component {
  constructor() {
    super();
    this.state = {};
  }

  componentDidMount() {}

  addNum = () => {
    const { value } = this.select;
    // 调用 this.props 传入的方法
    this.props.add(value * 1);
  };

  delNum = () => {
    const { value } = this.select;
    // 调用 this.props 传入的方法
    this.props.del(value * 1);
  };

  tongbu = () => {
    const { value } = this.select;
    // 调用 this.props 传入的方法
    if (this.props.count % 2 === 1) {
    // 调用 this.props 传入的方法
      this.props.add(value * 1);
    }
  };

  yibu = () => {
    const { value } = this.select;
    // 调用 this.props 传入的方法
    this.props.asyncAdd(value * 1, 1000);
  };

  render() {
    return (
      <div>
        <h1>当前求和{this.props.count}</h1>
        <div>
          <input type="text" ref={c => (this.select = c)} />
        </div>
        <div>
          <button onClick={this.addNum}>+</button>
          <button onClick={this.delNum}>-</button>
          <button onClick={this.tongbu}>奇数加</button>
          <button onClick={this.yibu}>异步加</button>
        </div>
      </div>
    );
  }
}

3. 整合 constant 、component
import React, { Component } from 'react';
// 引入 action 按需引入
import { addcontAction } from '../../redux/action';

// 引入 connect 用于 连接UI组件 与 redux
import { connect } from 'react-redux';

// UI组件
class ALLComt extends Component {
  addNum = () => {
    const { value } = this.select;
    this.props.add(value * 1);
  };
  render() {
    return (
      <div>
        {/* store.getState() // 接收reducer 返回的数据 第一次调用 返回默认值  */}
        <h1>当前求和{this.props.count}</h1>
        <div>
          <input type="text" ref={c => (this.select = c)} />
        </div>
        <div>
          <button onClick={this.addNum}>+</button>
        </div>
      </div>
    );
  }
}

// 简写 导出
export default connect(state => ({ count: state.count }), // 映射状态
{add: addcontAction}) // 映射方法
(ALLComt);

9. 路径请求

public > index 引入文件路径

  • 文件路径/开头
  • 文件路径%PUBLIC_URL%/
  • 路由模式 `hash

10. 其他

1. 高阶函数

参数或者返回值 是 函数的函数 就是高阶函数

- 高阶函数:如果一个函数符合下面2个规范中的其中一个,那该函数就是高阶函数
	-1.诺A函数,接收的参数是一个函数,那么A就可以称之为高阶函数.
	-2.诺A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数.常见的高阶函数有:Promise、setTimeout、arr.map()等

- 函数柯理化:通过函数调用继续返回函数的方式,实现多次接收参数最后一次统一处理的函数编码
	function sum(a){
		return(b)=>{
			return(c)=>{
				return a+b+c
			}
		}
	}

2. 纯函数

  • 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
  • 必须遵守以下一些约束
    • 不得改写参数数据
    • 不会产生任何副作用,例如网络请求,输入和输出设备
    • b不能调用Date.new() 或者 Math.random() 等不纯方法
    • redux 的 reducer 函数必须是一个纯函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值