0
点赞
收藏
分享

微信扫一扫

尚硅谷-React

—配合尚硅谷视频—

1.React初认识

(1)React是什么
react是用于构建用户 界面(视图) 的JavaScript库
也可以说react是一个将数据渲染为html视图开端的开源JS库

(2)React的功能是什么
操作Dom呈现页面

(3)为什么学React:原生JS的痛点
1 原生js操作dom效率低,繁琐

document.getElamentById('name')

2 使用js直接操作dom,浏览器会进行大量的重汇重排
3.原生js没有组件化的编码方案,代码复用率低
组件化:html、css、js都拆

(4)React的特点
1.采用组件化的模式,声明式编码,提高开发效率及组件的复用率
2.在React Native中可以使用React语法进行移动端开发
3.使用虚拟DOM+优秀的Diffing算法,尽量少的与真实的DOM的交互


2 React入门-HelloReact

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入react 核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom 用于支持react操作dom -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入bable 用于将jsx转化为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
        //1.创建虚拟dom
        const Vdom=<h1>hello-react</h1>
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    </script>
</body>
</html>

3 为什么用jsx?对比虚拟dom的两种创建方式

(1)使用jsx创建虚拟dom

    <script type="text/babel">
        //1.创建虚拟dom
        const Vdom=<h1><span>hello-react</span></h1>
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    </script>

(2)使用js创建虚拟dom
api:React.createElement(标签名,标签属性,标签内容):用于创建虚拟dom

    <script type="text/javascript">
        // 创建虚拟dom React.createElement(标签名,标签属性,标签内容)
        const Vdom=React.createElement('h1',{id:'title'},React.createElement('span',{id:'thespan'},'hello-react'))
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    </script>

(3)结论
jsx是js在react方面的语法糖


4.什么是虚拟dom

它其实是一个Object对象

关于虚拟dom

  • 虚拟dom本质是object类型的对象
  • 虚拟dom相比于真实dom“轻”(虚拟dom的属性较少),因为虚拟dom是react内部在用,所以无需真实dom那么多的属性
  • 虚拟dom最终会被react转化为真实dom,呈现在页面中
    请添加图片描述

5.JSX的语法规则

(1)关于JSX

  • jsx全称JavaScript XML
  • 是react定义的一种类似于XML的JS扩展语法JS+XML
  • 作用是用来简化创建虚拟DOM
  • 标签名任意:HTML标签或者其他标签

(2)JSX语法规则

  • 定义虚拟dom的时候不要写引号
  • 标签中混入JS表达式时要用{ }
    <script type="text/babel">
        const myId="guigu"
        const myName="lihua"
        //1.创建虚拟dom
        const Vdom2=(
            <h2 id="{myId}">
                <span>{myName}</span>
            </h2>
        )
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom2,document.getElementById('test'))
    </script>
  • 样式的类名不要用class,要用className
        const Vdom2=(
            <h2 id="{myId}" className="title">
                <span>{myName}</span>
            </h2>
        )
  • 内联样式要用 style={{key:'value’,key2:‘value2’}} 的形式来写
        const Vdom2=(
            <h2 id="{myId}" className="title">
                <span style={{color:'white',fontSize:'40px'}}>{myName}</span>
            </h2>
        )
  • 虚拟dom必须只有一个根标签
  • 标签必须闭合
     <input type="text"/> 
     <input type="text"></input>
  • 标签首字母:
    (1)若小写字母开头,标签转化为html中同名标签,如果html找不到此标签-----报错
    (2)若大写字母开头,React就去渲染对应的组件,若组件没有定义-----报错
  • render中写注释的格式:
 {/*<input type="text" placeholder="点击按钮提示数据"  ref={currentNode=>{this.input1=currentNode;console.log(currentNode)}}/>&nbsp;*/}

6 React中的数据循环遍历

    <script type="text/babel">
        // 模拟数据
        const title="前端框架列表"
        const data=['angular','vue','react']
        //1.创建虚拟dom
        const Vdom=(
            <div>
                <h2>{title}</h2>
                <ul>
                    {
                        data.map((item,index)=>{
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            </div>
        )
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))

7 模块与组件、模块化与组件化的理解

(1)模块:

  • 理解:向外提供特定功能的js程序,一般就是一个js文件。
  • 为什么要拆分成模块:随着业务逻辑的增加,代码越来越复杂
  • 作用:复用js,简化js的编写,提高js运行效率
  • 模块化:当应用的js都以模块来编写,这个应用就是一个模块化的应用

(2)组件:

  • 理解:用来实现局部功能效果的代码和资源的集合 包括js、html、css等
  • 为什么:一个界面的功能复杂
  • 作用:复用编码,简化项目代码的编写,提高项目运行效率
  • 组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用

8 React组件

(1)分类

名称描述
函数式组件用函数定义的组件,适用于简单组件的定义
类式组件用类定义的组件,适用于复杂组件的定义

(2)函数式组件

    <script type="text/babel">
        //1.创建函数式组件
        function Demo(){
            //此处的this是undefined 因为bable翻译后开启了严格模式
            return <h2>我是函数定义的简单组件,适用于简单组件的定义</h2>
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

(3)类式组件

    <script type="text/babel">
        //1.创建类式组件 必须继承react中内置的类 React.Component
        class MyComponent extends React.Component{
            render(){
                return <h2>我是定义的类式组件</h2>
            }
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    </script>

9 组件三大属性之---- state

名称描述
简单组件没有state的
复杂组件有state的
(1)实例
script type="text/babel">
        class Weather extends React.Component{
            constructor(props){
                super(props) 
                // 初始化状态
                this.state={isHot:true}
                // 将原型对象上的方法转为实例自身上的方法
                this.changeWeather=this.changeWeather.bind(this)
            }
            changeWeather(){
                //1.类中的方法默认开启严格模式
                //2. 由于changeWeather是作为onclick事件的回调 不是通过实例调用的而是直接调用
                //结论:changeWeather中的this是undefined

                // 严重注意:状态不可直接跟改 要使用内置的api
                this.setState({isHot:!this.state.isHot})
            }
            render(){
                return <h2 id='title' onClick={this.changeWeather}>今天天气很{this.state.isHot?'炎热':'凉爽'}</h2>
            }
        }
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>

(2)使用this.setState更新state

这里的更新是一种合并不会影响其他属性,而不是覆盖

this.setState({isHot:!this.state.isHot})

(3)state的简写方式
主要运用原理:类中可以直接写赋值语句,含义是给car的实例对象添加一个属性。

    <script type="text/babel">
        class Weather extends React.Component{
            constructor(props){
                super(props)
            }
            // 类中可以直接写赋值语句 含义是给类的实例添加一个属性
            state={isHot:true}
            changeWeather=()=>{
                this.setState({isHot:!this.state.isHot})
            }
            render(){
                return <h2 id='title' onClick={this.changeWeather}>今天天气很{this.state.isHot?'炎热':'凉爽'}</h2>
            }
        }
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>

(4)总结state

  • 组件中rander方法中的this为组件的实例对象

  • 组件自定义的方法中this为undefined,如何解决?
    (1)使用bind强制改变指向
    (2)使用箭头函数

  • 状态数据不能直接修改,要用this.setState({})

10 组件三大属性之-----prop

(1)基本用法

    <script type="text/babel">
        class Person extends React.Component{
            constructor(props){
                super(props)
            }
            render(){
                console.log(this)
                return(
                <ul>
                  <li>姓名{this.props.name}</li>
                  <li>年龄{this.props.age}</li>    
                  <li>性别{this.props.sex}</li>        
                </ul>
                ) 
            }
        }
        ReactDOM.render(<Person name="tom" age="18" sex="man"/>,document.getElementById('test'))
    </script>

(2)对标签属性进行限制和设置默认值

    <!-- 引入prop-types用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel">
        class Person extends React.Component{
            constructor(props){
                super(props)
            }
            render(){
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>        
                </ul>
                ) 
            }
        }
        // 指定参数类型
        Person.propTypes={
            name:PropTypes.string.isRequired,
            sex:PropTypes.string,
            age:PropTypes.number,
            fun:PropTypes.func,
        }
        // 指定默认值
        Person.defaultProps={
            sex:'男',
            age:18
        }
        const p={name:'tom',age:"18",sex:'man'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test'))

(3)props值是只读的

            render(){
                this.props.age=19 //报错
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>        
                </ul>
                ) 
            }

(4)props简写形式
将propTypes和defaultProps放在类定义的内部

    <script type="text/babel">
        // 创建组件
        class Person extends React.Component{
            // 指定类型
            static propTypes={
               name:PropTypes.string.isRequired,
               sex:PropTypes.string,
               age:PropTypes.number,
               fun:PropTypes.func,
            }
            // 设置默认值
            static defaultProps={
               sex:'男',
               age:18
            }
            state={secname:'lihua'}
            render(){
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>
                  <li>state:{this.state.secname}</li>     
                </ul>
                ) 
            }
        }
        const p={name:'tom',age:18,sex:'man'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test'))
    </script>

(5)函数式组件中使用props

    <script type="text/babel">
        //1.创建函数式组件
        function MyComponent(props){
            return <h2 className='title'>我是{props.name}</h2>
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        const p={name:'tom',age:18,sex:'man'}
        ReactDOM.render(<MyComponent {...p}/>,document.getElementById('test'))
    </script>

11 类中的构造器

类中的构造器是否接收props,是否传递给super取决于:是否希望在构造器中通过this访问props
构造器可以直接删除

constructor(props){
     super(props)
}

12 组件的三大属性-----ref

(1)字符串形式的ref
不推荐
因为string类型的ref存在效率的问题。

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            //展示输入框的数据
            showData = ()=>{
                alert(this.refs.input1.value)
            }
            showData2 = ()=>{
                alert(this.refs.input2.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref="input1"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} ref="input2"/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

(2)回调函数形式的ref

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            //展示输入框的数据
            showData = ()=>{
                alert(this.input1.value)
            }
            showData2 = ()=>{
                alert(this.input2.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref={currentNode=>this.input1=currentNode}/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2}  ref={currentNode=>this.input2=currentNode}/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

(3)createRef API形式的ref

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            input1 = React.createRef()
            input2 = React.createRef()
            showData = ()=>{
                alert(this.input1.current.value)
            }
            showData2 = ()=>{
                alert(this.input2.current.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref={this.input1 }/>&nbsp;
                        <input type="text" placeholder="点击按钮提示数据"  ref={this.input2 }/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <button onClick={this.showData2}>点我提示左侧数据</button>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

13 React中的事件处理

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            showData2 = (event)=>{
                alert(event.target.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="失去焦点提示数据"   onBlur={this.showData2}/>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

14 React中的收集表单数据

(1)非受控组件

    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                let {username,password} = this
                alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`)
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" ref={c=>this.username=c}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" ref={c=>this.password=c}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

(2)受控组件

    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存用户名在状态中
            saveUsername=(event)=>{
               this.setState({username:event.target.value})
            }
            // 保存密码在状态中
            savePassword=(event)=>{
               this.setState({password:event.target.value})
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={this.saveUsername}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={this.savePassword}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

15 高阶函数,函数的柯里化

 saveUserinfo=(type)=>{
      return (event)=>{
          this.setState({[type]:event.target.value})
      }
}
        function add(a){
            return (b)=>{
                return (c)=>{
                    return a+b+c
                }
            }
        }
        let a = add(1)(2)(3)
        console.log(a)
    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存表单数据到状态中
            saveFomeData=(type)=>{
                return (event)=>{
                    // 使用[]来获取变量 因为直接写type会把type当做字符串而不是变量
                    this.setState({[type]:event.target.value})
                }
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={this.saveFomeData('username')}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={this.saveFomeData('password')}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

不用柯里化

  <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存表单数据到状态中
            saveFomeData=(type,event)=>{
                this.setState({[type]:event.target.value})
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={(event)=>this.saveFomeData('username',event)}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={(event)=>this.saveFomeData('password',event)}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

16 React的生命周期

(2)React生命周期(旧版本)
请添加图片描述
请添加图片描述

(2)React生命周期(新版本)
请添加图片描述

(16-1):componentDidMount

调动时机:组件挂载完毕时调用,一般用于做一些初始化的事情。(开启定时器,发送网络请求、订阅消息)

    <script type="text/babel">
        //创建组件
        class Life extends React.Component{
            state={opacity:1}
           //使组件从页面移除
           distory=()=>{
               ReactDOM.unmountComponentAtNode(document.getElementById('test'))
           }
           // 调动时机:组件挂载完毕时调用
           componentDidMount(){
                setInterval(()=>{
                   let {opacity} = this.state
                   opacity -= 0.1
                   if(opacity <= 0) opacity=1
                   this.setState({opacity})
               },200)
           }
           render(){
               return(
                   <div>
                      <h2 style={{opacity:this.state.opacity}}>ReactReactReactReact</h2>
                      <button onClick={this.distory}>消失</button>
                    </div>
               )
           }
        }
        //渲染组件到页面
        ReactDOM.render(<Life/>,document.getElementById('test'))
    </script>

(16-2):componentWillUnmount

调用时机:组件将要被卸载时调用,一般做一些收尾的事(关闭定时器、取消订阅消息)

卸载组件:this.distory

    <script type="text/babel">
        //创建组件
        class Life extends React.Component{
            state={opacity:1}
           //使组件从页面移除
           distory=()=>{
               ReactDOM.unmountComponentAtNode(document.getElementById('test'))
           }
           // 调用时机:组件将要被卸载时调用
           componentWillUnmount(){
            clearInterval(this.timer)
           }
           render(){
               return(
                   <div>
                      <h2 style={{opacity:this.state.opacity}}>ReactReactReactReact</h2>
                      <button onClick={this.distory}>消失</button>
                    </div>
               )
           }
        }
        //渲染组件到页面
        ReactDOM.render(<Life/>,document.getElementById('test'))

(16-3):新版本和旧版本的区别

1.新版本中要用

2 新版本出现两个新的钩子函数
使用的情况比较少

getDerivedStateFromProps:从props中得到一个派生的状态,若state中的值在任何时候都取决于props中时使用【一般不使用】

// getDerivedStateFromProps
//返回值为一个对象:state中的同名值被props替代且不能被修改
//返回值为null:没有影响
//返回值为props:state中的同名值被props替代且不能被修改
static getDerivedStateFromProps(props,state){
      console.log('Count-getDerivedStateFromProps',props,state)
      return props
}

getSnapshotBeforeUpdate:在更新之前获取快照 【不常用】

//getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(){
   console.log('Count-getSnapshotBeforeUpdate')
    return 'weijiamin'//会被componentDidUpdate接收
}

17 React脚手架

快捷键
rcc:快速生成React类式组件框架
rfc:快速生成React函数式组件

18 父子组件传值

1.父-子
父组件

export default class App extends Component {
  state = {
    todos: [
      { id: "001", name: "吃饭" ,done:true},
      { id: "002", name: "睡觉" ,done:false},
      { id: "003", name: "上班" ,done:false},
      { id: "004", name: "打游戏" ,done:false},
    ],
  };
  render() {
    return (
        <List todos={this.state.todos} />
    );
  }
}

子组件

export default class List extends Component {
  render() {
    return (
      <ul className="todo-main">
        {
          this.props.todos.map((item,index)=>{
            return <Item key={item.id} todo={item}/>
          })
        }
      </ul>
    );
  }
}

2.子-父:实际是方法的传递
父组件

export default class App extends Component {
  a=(data)=>{
    console.log('父组件接收到的值',data)
  }
  render() {
    return (
          <Header a={this.a}/>
    );
  }
}

子组件

export default class Header extends Component {
  handleKeyUp=(event)=>{
    this.props.a(event.target.value)
  }
  render() {
    return (
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"  onKeyUp={this.handleKeyUp}/>
    );
  }
}

19 消息订阅与发布机制-兄弟组件传值

组件1:订阅消息

  //初始化状态
  state={
    users:[],
    isFirst:true,//是否为第一次打开页面
    isLoading:false,//是否为加载状态
    err:''//存储请求相关的错误信息
  }
 // 接收到两个参数 参数1:消息的名称 参数2:消息传递的值
  componentDidMount(){
    this.token=PubSub.subscribe('message',(msg,data)=>{
      this.setState(data)
    })
  }
  //取消订阅
  componentWillUnmount(){
    PubSub.unsubscribe(this.token)
  }

组件2:发布消息

search=()=>{
    //获取用户输入
    let keyword = this.keyWordNode.current.value
    // 发送请求前通知List更新状态
    PubSub.publish('message',{isFirst:false,isLoading:true})
    //发送网络请求
    axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then(
      res=>{
        //请求成功后通知List更新状态
        PubSub.publish('message',{isLoading:false,users:res.data.items})
      },
      reason=>{
        //请求失败后通知更新List状态
        PubSub.publish('message',{isLoading:false,err:reason.message})
      }
    )
  }

20 -React 配置代理

方式1
方式:修改package.josn文件
注意:要重新启动脚手架
请添加图片描述
发送请求:

    axios.get('http://localhost:3000/students').then(
      res=>{console.log('成功',res.data)},
      error=>{console.log('失败')}
    )

方式2
在src文件夹下面添加setupProxy.js文件

const { createProxyMiddleware } = require("http-proxy-middleware")
module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1",{
      target: "http://localhost:5000", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api1": "" }, //重写请求路径
    }),
    createProxyMiddleware("/api2",{
      target: "http://localhost:5001", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api2": "" }, 
    })
  )
}

发送请求

  getStudentInfo = () => {
    axios.get("http://localhost:3000/api1/students").then(
      res => {
        console.log("成功", res.data);
      },
      error => {
        console.log("失败");
      }
    );
  };

21 fatch发送网络请求

没看

22 React路由

1.SPA的理解

2.路由的理解

3.react-route-dom的理解

4 路由的基本使用

import {Link,Route } from "react-router-dom";

请添加图片描述
注意:要用BrowserRouter包裹,因为都需要包裹使用直接在index.js中操作

请添加图片描述

22 封装NavLike

组件

import React, { Component } from 'react'
import {NavLink } from "react-router-dom";

export default class MyNavLink extends Component {
  render() {
    return (
        <NavLink className="list-group-item" activeClassName="active_nav" {...this.props}></NavLink>
    )
  }
}

使用

 <MyNavLink  to="/about">about</MyNavLink>
 <MyNavLink  to="/home">home</MyNavLink>

23 使用Switch组件控制路由匹配

import {Route,Switch } from "react-router-dom";
//结果:只显示Home组件
<Switch>
     <Route path="/about" component={About}/>
     <Route path="/home" component={Home}/>
     <Route path="/home" component={Test}/>
</Switch>

24 多层级路由刷新后样式丢失问题解决

        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
                <MyNavLink  to="/weijiamin/about">about</MyNavLink>
                <MyNavLink  to="/weijiamin/home">home</MyNavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                  {/* 注册路由 */}
                  <Switch>
                      <Route path="/weijiamin/about" component={About}/>
                      <Route path="/weijiamin/home" component={Home}/>
                  </Switch>
              </div>
            </div>
          </div>
        </div>

方法1.修改pubilc/index.js中的样式引入

   //不加./
    <link rel="stylesheet" href="/css/bootstrap.css">

方法2使用 %PUBLIC_URL%

    <link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">

方法3 使用 HashRouter 【不常用】

import { HashRouter} from "react-router-dom";
//渲染App组件到页面
ReactDOM.render(
   <HashRouter>
      <App/>
    </HashRouter>
,document.getElementById('root')
)

25 路由匹配规则

1.默认是模糊匹配(最左匹配)
可以匹配的情况

<MyNavLink  to="/about/weijiamin">about</MyNavLink>
<Route path="/about" component={About}/>
```html
<MyNavLink  to="/about">about</MyNavLink>
<Route path="/about" component={About}/>

2.开启精准匹配(全部都要相等)
exact
使用的原则:一般不使用

<MyNavLink  to="/weijiamin/about">about</MyNavLink>
<Route exact path="/weijiamin/about" component={About}/>

26 设置默认的路由,路由重定向-Redirect

使用Redirect【重定向】

 {/* 注册路由 */}
 <Switch>
       <Route path="/about" component={About}/>
       <Route path="/home" component={Home}/>
       <Redirect to="/home"></Redirect>
</Switch>

27 嵌套路由

export default class Home extends Component {
  render() {
    return (
      <div>
        <ul className="nav nav-tabs">
          <li>
              <MyNavLink to="/home/news">news</MyNavLink>
          </li>
          <li>
              <MyNavLink to="/home/message">message</MyNavLink>
          </li>
        </ul>
        {/* 注册路由 */}
        <Switch>
            <Route path="/home/news" component={News}/>
            <Route path="/home/message" component={Message}/>
            <Redirect to="/home/message"></Redirect>
        </Switch>
      </div>
    )
  }

28 路由传参

(28-1)路由传递params参数

{/* 像路由组件传递params参数 */}
<Link to={`/home/message/detail/${item.id}/${item.title}`}>{item.title}</Link>
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail}/>
组件接收参数
![请添加图片描述](https://img-blog.csdnimg.cn/8e67ffc97ed54c4bb46feed5e491f27e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2hiaTEzMXh4,size_15,color_FFFFFF,t_70,g_se,x_16)

(28-2)路由传递search参数

像路由组件传递search参数 
<Link to={`/home/message/detail?id=${item.id}&title=${item.title}`}>{item.title}</Link>
{/* search参数无需声明接收 正常声明即可*/}
<Route path="/home/message/detail" component={Detail}/>
import qs from 'qs'
接收saerch参数
let {search} = this.props.location
let {id,title}= qs.parse(search.slice(1))

(28-3)路由接收state参数

像路由组件传递state参数
<Link to={{pathname:'/home/message/detail',state:{id:item.id,title:item.title}}}>{item.title}</Link>
{/* state参数无需声明接收 正常声明即可*/}
<Route path="/home/message/detail" component={Detail}/>
接收state参数
const {state}=this.props.location
const {state}=this.props.location||{}

29 路由的push与replace

1.push(默认状态)

路由(栈)
http://localhost:3001/home/message/detail
http://localhost:3001/home/message
http://localhost:3001/home
http://localhost:3001/about

2.replace模式
开启replace模式

 <Link replace={true} to="/home/message/detail">{item.title}</Link>
路由(栈)
http://localhost:3001/home/message/detail
http://localhost:3001/home/message
http://localhost:3001/home
http://localhost:3001/about

30 编程式路由导航

设置按钮

<button onClick={()=>{this.showReplace(item.id,item.title)}}>replace查看</button>
<button onClick={()=>{this.showPush(item.id,item.title)}}>push查看</button>
  showReplace=(id,title)=>{
    // replace跳转 携带params参数
    this.props.history.replace(`/home/message/detail/${id}/${title}`)
    // replace跳转 携带search参数
    this.props.history.replace(`/home/message/detail/?id=${id}&title=${title}`)
    // replace跳转 携带state参数
    this.props.history.replace('/home/message/detail',{id,title})
  }
  
  showPush=(id,title)=>{
    // push跳转 携带params参数
    this.props.history.push(`/home/message/detail/${id}/${title}`)
    //  push跳转 携带search参数
    this.props.history.push(`/home/message/detail/?id=${id}&title=${title}`)
    //  push跳转 携带state参数
    this.props.history.push('/home/message/detail',{id,title})
  }

31 编程式路由前进和后退

<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.goByNum}>前进或后退几部</button>
  back=()=>{
    this.props.history.goBack()
  }
  forward=()=>{
    this.props.history.goForward()
  }
  goByNum=()=>{
    // 正数为前进 负数为后退
    this.props.history.go(-2)
  }

32 withRouter的使用

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'

class Header extends Component {
  back=()=>{
    this.props.history.goBack()
  }
  forward=()=>{
    this.props.history.goForward()
  }
  goByNum=()=>{
    // 正数为前进 负数为后退
    this.props.history.go(-2)
  }
  render() {
    return (
        <div className="page-header">
          <h2>React Router Demo</h2>
          <button onClick={this.back}>回退</button>
          <button onClick={this.forward}>前进</button>
          <button onClick={this.goByNum}>前进或后退几部</button>
        </div>
    )
  }
}

// 暴露的是withRouter加工后的Header
export default withRouter(Header)

33 BrowserRouter和HashRouter的区别

1.底层原理不同
BrowserRouter使用的H5的history API,不兼容IE9及以下
HashRouter使用的是URL的哈希值
2.url的表现形式不同
BrowserRouter:没#
HashRouter:有#
3.刷新后对路由state参数的影响
BrowserRouter:没有任何影响,因为state保存在location对象中
HashRouter:导致路由state丢失

备注:HashRouter可以用于解决一些路径错误相关问题。

34组件库 ant-design

(1)基本使用

请添加图片描述
(2)优化antd样式
修改主题色

  1. 安装依赖的包
  1. 修改样式引入
  1. 在根目录下创建 craco.config.js文件并修改配置
const CracoLessPlugin = require('craco-less');

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { '@primary-color': '#1DA57A' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

4.修改package.json文件

 "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

5.运行

npm start

35 redux

请添加图片描述

(35-1)redux的三个核心概念

1.action

2.reducer

3.store

(35-2)redux的精简使用

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'


//暴露store
export default createStore(countReducer)

conut_reducer.js

//该文件是用于创建一个为count服务的reducer,reducer的本质就是一个函数

//reducer函数的两个参数
//preState:之前的状态
//action:要做的动作
export default function countReducer(preState,action){
    //初始化状态
    if(preState === undefined) preState = 0
    const {type,data} = action
    switch(type){
        case 'increment':
            return preState + data
        case 'decrement':
            return preState - data
        default:
            return preState
    }
}

count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";

export default class Count extends Component {
  increment = () => {
    //通知reducter加value
    const { value } = this.selectNumber;
    store.dispatch({type:'increment',data:value*1})
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        {/* 获取store中的值 */}
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
      </div>
    );
  }
}

(35-4)redux完整版

constant.js

// 该模块是用于定义action对象中的type类型 防止在编写过程中出错

export const INCREMENT = 'increment'

export const DECREMENT = 'decrement'

count_action.js

// 该文件专为Count组件生成action

import { INCREMENT,DECREMENT } from "./constant"

export const createIncrementAction = data => {
  return { type: INCREMENT, data };
};

export const createDecrementAction = data => {
  return { type: DECREMENT, data };
};

conut_reducer.js

//该文件是用于创建一个为count服务的reducer,reducer的本质就是一个函数
import { INCREMENT,DECREMENT } from "./constant"

//reducer函数的两个参数
//preState:之前的状态
//action:要做的动作
export default function countReducer(preState,action){
    console.log(preState,action)
    //初始化状态
    if(preState === undefined) preState = 0
    const {type,data} = action
    switch(type){
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    }
}

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'


//暴露store
export default createStore(countReducer)


Count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";
//引入actionCreate 专门用于创建action对象
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

export default class Count extends Component {
  increment = () => {
    //通知reducter加value
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value*1))
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value*1))
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    const count = store.getState()
    if (count % 2 !== 0) {
        store.dispatch(createIncrementAction(value*1))
    }
  };
  incrementAsync = () => {
    setTimeout(() => {
      const { value } = this.selectNumber;
      store.dispatch(createIncrementAction(value*1))}, 2000);
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    );
  }
}

(35-5)异步action

action类型说明
对象类型同步action
函数类型异步action

count_action.js

//同步action
export const createIncrementAction = data => {
  return { type: INCREMENT, data };
};
//异步action
export const createDecrementAsyncAction = (data,time) => {
    return (dispatch)=>{
        setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time);
    }
  };

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore,applyMiddleware } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'
//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";


//暴露store
export default createStore(countReducer,applyMiddleware(thunk))


Count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";
//引入actionCreate 专门用于创建action对象
import { createDecrementAsyncAction,createDecrementAction } from "../../redux/count_action";

export default class Count extends Component {
  incrementAsync = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAsyncAction(value*1,2000))
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    );
  }
}

36 react-redux

请添加图片描述

(37-1) react-dedux的基本使用

  1. 将Count改名为CountUI,且只保存html结构
  2. 创建容器组件 在src下新建文件containers/Count/index用于放置CountUI的容器组件
//自定义容器组件

// 引入count的UI组件
import CountUI from '../../components/CountUI'
//引入connect用于链接容器组件和UI组件
import { connect } from "react-redux";


// 生成容器组件 并链接UI组件
const countContainer = connect()(CountUI)

export default countContainer

3.修改App.js中的引入,并向容器组件中传递store(到这一步就实现了容器和UI组件、容器和redux之间的连接)

import React, { Component } from 'react'
//渲染的是容器组件
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return (
      <div><Count store={store}></Count></div>
    )
  }
}

4.Count容器向CountUI传props参数
请添加图片描述
请添加图片描述

5.容器组件和redux的数据沟通:

import CountUI from '../../components/CountUI'
import { connect } from "react-redux";

//这个函数有一个参数为redux的state
function a(state){
    return {count:state}
}
//这个函数有一个参数为dispatch 用于操作store的值
function b(dispatch){
    return {add:(data)=>{
        //通知redux执行increase
        dispatch({type:'increment',data:data})
    }}
}
const countContainer = connect(a,b)(CountUI)

export default countContainer

6.UI组件使用props

increment = () => {
    const { value } = this.selectNumber;
    this.props.add(1*value)
 };
<h1>当前求和为:{this.props.count}</h1>

7.优化:使用reduc_action

import CountUI from '../../components/CountUI'
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

function mapStateToProps(state){
    return {count:state}
}
function mapDispatchToProps(dispatch){
    return {
        increace:data=>dispatch(createIncrementAction(data)),
        decreace:data=>dispatch(createDecrementAction(data))
    }
}
const countContainer = connect(mapStateToProps,mapDispatchToProps)(CountUI)

export default countContainer

(37-2) react-dedux的优化

1.容器的优化

import CountUI from '../../components/CountUI'
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

//简写方式
const countContainer = connect(
    //传递state参数
    state=>{return {count:state}},
    //对象的形式 只需提供action 就可以自动调用dispatch
    {
        increace:createIncrementAction,
        decreace:createDecrementAction
    }
)
(CountUI)

export default countContainer

2.无需使用store.subscribe进行监测

3.App.js中传store的优化
不在App.js中传,在index.js中传

App.js

import React, { Component } from 'react'
//渲染的是容器组件
import Count from './containers/Count'

export default class App extends Component {
  render() {
    return (
      // 给容器组件传递store
      <div><Count></Count></div>
    )
  }
}

index.js

import React from 'react';
import App from './App';
import store from './redux/store';
import {Provider} from 'react-redux'
import { createRoot } from 'react-dom/client';


const container = document.getElementById('root');
const root = createRoot(container);
root.render( 
<Provider store={store}>
    <App />
</Provider>
);

4 整合容器组件和UI组件

import { connect } from "react-redux";
import React, { Component } from "react";
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

//UI组件
class Count extends Component {
    increment = () => {
      const { value } = this.selectNumber;
      this.props.increace(1*value)
    };
    decrement = () => {
      const { value } = this.selectNumber;
      this.props.decreace(1*value)
    };
    incrementIfOdd = () => {
      const { value } = this.selectNumber;
    };
    incrementAsync = () => {
        const { value } = this.selectNumber;
    };
    componentDidMount(){
    }
    render() {
      console.log("UI组件收到的props为:",this.props)
      return (
        <div>
          <h1>当前求和为:{this.props.count}</h1>
          <select name="" id="" ref={c => (this.selectNumber = c)}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
          </select>
          &nbsp;
          <button onClick={this.increment}>+</button>&nbsp;
          <button onClick={this.decrement}>-</button>&nbsp;
          <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
          <button onClick={this.incrementAsync}>异步加</button>&nbsp;
        </div>
      );
    }
}
//容器组件
const countContainer = connect(
    //传递state参数
    state=>{return {count:state}},
    //对象的形式 只需提供action 就可以调用dispatch
    {
        increace:createIncrementAction,
        decreace:createDecrementAction
    }
)
(Count)

export default countContainer

37 redux数据共享

多个组件共同使用redux的情况

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
// applyMiddleware:使异步action可以被接收
// combineReducers:合并reducer
import { createStore,applyMiddleware,combineReducers } from "redux";

// 引入为Count组件服务的reducer
import countReducer from './reducers/conut'
//引入person的reducer
import personReducer from "./reducers/person";

//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";

//汇总reducer
const allReducer= combineReducers({
    count:countReducer,
    person:personReducer
})
//暴露store
export default createStore(allReducer,applyMiddleware(thunk))

38 纯函数

39 redux扩展插件应用

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'

// 引入createStore 用于创建store
// applyMiddleware:使异步action可以被接收
// combineReducers:合并reducer
import { createStore,applyMiddleware,combineReducers } from "redux";

// 引入为Count组件服务的reducer
import countReducer from './reducers/conut'
//引入person的reducer
import personReducer from "./reducers/person";

//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";

//汇总reducer
const allReducer= combineReducers({
    count:countReducer,
    person:personReducer
})
//暴露store
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

40 打包react项目

1.执行:npm run build
生成build文件

2.安装库:npm i serve -g
用于开启服务器

3. 执行:serve build
以build文件夹作为根路径来启动一台服务器

扩展1-关于setState()

1.setState()的第一种用法

  //setState的第一种用法
  add = () => {
      const {count}=this.state
      this.setState({count:count+1},()=>{
          console.log("在回调函数中",this.state)
      })
      console.log("不在回调函数中",this.state)
  };

2.setState()的第二种用法

  add=()=>{
      this.setState((state,props)=>{
          return {count:state.count+1}
      })
  }

扩展2-LazyLoad懒加载

// 引入lazy,Suspense
import React, { Component,lazy,Suspense } from "react";
import {NavLink,Route } from "react-router-dom";
//lazy和import一起使用 注册可以懒加载的组件
const Home= lazy(()=>import('./Home'))
const About= lazy(()=>import('./About'))

//创建并暴露App外壳组件s
export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* 在react考路由链接切换组件 */}
                <NavLink className="list-group-item" to="/about" >about</NavLink>&nbsp;
                <NavLink className="list-group-item" to="/home">home</NavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                  {/* 注册路由 */}
                  {/* 使用Suspense来控制组件还没有加载好时的状态,这里最好是一个loading的组件 */}
                    <Suspense fallback={<h1>loading...</h1>}>
                        <Route path="/about" component={About}/>
                        <Route path="/home" component={Home}/>
                    </Suspense>
              </div>
            </div>
          </div>
        </div>
    </div>
    )
  }
}

扩展3-Hooks

React.useState():使函数式组件能有state和修改state

export default function Demo() {
  //React.useState的参数为state的初始值
  //React.useState的返回值为一个数组 第一个值为state的值 第二个值为改变state的方法
  const [count,setCount] = React.useState(0);

  function add() {
    //setState的第一种写法
    setCount(count+1)
    //setState的第二种写法
    setCount(count=>count+1)
  }
  return (
    <div>
      <h1>现在的值为:{count}</h1>
      <button onClick={add}>点我加1</button>
    </div>
  );
}

注意:在更新对象类型时,切记要合并旧的状态,否则旧的状态会丢失。

const [params, setParams] = useState({
  rotate: 0,
  color: "#000000"
});

const handleInputChange = event => {
  const target = event.target;
  setParams({
    ...params,
    [target.name]: target.value
  });
};

注意:因为是要修改useState处理后的值,所有需要使用useCallback来处理,其他要进行一次浅拷贝

 const emailMouseLeave = useCallback((changeIndex)=>{
    let result = email.concat()
    result.forEach((item,index)=>{
      if(index === changeIndex){
        item.isHover = false
      }
    })
    setEmail(result)
  },[email])

React.useEffect():使函数式组件具备钩子函数

export default function Demo() {
   const [count,setCount] = React.useState(0);
  //React.useEffect可以传入两个参数 
  //第一个参数 :为一个函数,这个函数就相当于一个生命周期的回调函数
  //第二个参数 :为一个数组,里面的值相当于监测谁 
  //          不写:谁都监测,
  //          空数组:谁都不监测,此时第一个参数相当于componentDidMount的回调
  //          [count,name]:监测count和name,此时第一个参数相当于componentDidUpdate的回调

  //React.useEffect 返回值为一个函数,这个函数就相当于componentWillUnmount的回调
  React.useEffect(() => {
      let timer=setInterval(()=>{
          setCount(count=>count+1)
      },1000)
      return ()=>{
          clearInterval(timer)
      }
  },[]);
  //卸载组件的回调
  function unMount(){
    ReactDom.unmountComponentAtNode(document.getElementById('root'))
  }
  return (
    <div>
      <h1>现在的值为:{count}</h1>
      <button onClick={unMount}>卸载组件</button>
    </div>
  );
}

React.useRef():使函数组件支持ref

export default function Demo() {
   const myRef=React.useRef()
   function show(){
       alert(myRef.current.value)
   }
   return (
     <div>
       <input type="text" name="" id="" ref={myRef}/>
       <button onClick={show}>点击按钮展示内容</button>
     </div>
   );
 }

扩展4-Fragment

import React, { Component,Fragment } from 'react'
import Demo from './components/4_Fragment'

export default class App extends Component {
  render() {
    return (
        <Fragment>
          <Demo></Demo>
        </Fragment>
    )
  }
}

扩展5-Context

使用context

1.创建context容器对象,并取出Provider

const UserNameContext=React.createContext()
const {Provider,Consumer} = UserNameContext

2.渲染子组件的时候。外面要包裹Provider,通过value属性给后代传递数据

export default class A extends Component {
  state={username:'tom'}
  render() {
    return (
      <div className='parent'>
          <h3>我是A组件</h3>
          <h4>我的用户名是{this.state.username}</h4>
          //必须是value 不允许改名
          <Provider value={this.state.username}>
            <B/>
          </Provider>
      </div>
    )
  }
}

3.后代读取数据
(1)方法一:仅用于类式组件

class C extends Component {
  //声明接收
   static contextType=UserNameContext
    render() {
      console.log(this)
      return (
        <div className='grand'>
          <h3>我是B组件</h3>
          <h4>我从A组件接收到的用户名是:{this.context}</h4>
        </div>
      )
    }
}

(2)方法二:函数式组件和类式组件都可以使用

function C(){
  return (
    <div className='grand'>
      <h3>我是B组件</h3>
      <h4>我从A组件接收到的用户名是:
        <Consumer>
          {
            value=>{
              return `${value.username}`
            }
          }
        </Consumer>
      </h4>
    </div>
  )
}

扩展6-PureComponent

  shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState); //要变化成的目标props,state
    console.log(this.props, this.state); //变化前的props,state
    if (this.state.carName === nextState.carName) return false;
  }
import React, { PureComponent } from "react";
import "./index.css";

export default class Parent extends PureComponent {
  state = { carName: "car1" };
  changeCar = () => {
    this.setState({ carName: "car2" });
  };
  shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState); //要变化成的目标props,state
    console.log(this.props, this.state); //变化前的props,state
    if (this.state.carName === nextState.carName) return false;
  }
  render() {
    return (
      <div className="parent">
        <h2>我是Parent</h2>
        <button onClick={this.changeCar}>点我换车</button>
        <h4>我的车是:{this.state.carName}</h4>
        <Child carName={this.state.carName}/>
      </div>
    );
  }
}

class Child extends PureComponent {
//   shouldComponentUpdate(nextProps, nextState) {
//     console.log(nextProps, nextState); //要变化成的目标props,state
//     console.log(this.props, this.state); //变化前的props,state
//     if (this.props.carName === nextProps.carName) return false;
//   }
  render() {
    return (
      <div className="child">
        <h2>我是Child</h2>
        <h4>我接到的车是:{this.props.carName}</h4>
      </div>
    );
  }
}

扩展7-renderProps

import React, { Component } from "react";
import "./index.css";

export default class Parent extends Component {
  state = { username: "tom" };
  render() {
    return (
      <div className="parent">
        <h3>我是Parent组件</h3>
        <h4>我的用户名是{this.state.username}</h4>
        //传参
        <A render={(name) => <B name={name}/>} />
      </div>
    );
  }
}

class A extends Component {
  state = { name: "tom" };
  render() {
    const {name} = this.state
    console.log(this.props);
    return (
      <div className="child">
        <h3>我是A组件</h3>
        //调用子组件
        {this.props.render(name)}
      </div>
    );
  }
}

//类式组件接收
class B extends Component {
  render() {
    return (
      <div className="grand">
        <h3>我是B组件</h3>
        //接收
        <h4>我接到的名字:{this.props.name}</h4>
      </div>
    );
  }
}

扩展8:-ErrorBoundary错误边界

import React, { Component } from "react";
import Child from "./Child";

export default class Prrent extends Component {
  //hasError:用于标识子组件是否产生错误
  state={hasError:''}
  //当parent的子组件出现错误的时候会调用getDerivedStateFromError并且携带错误信息
  static getDerivedStateFromError(error){
    console.log(error)
    return {hasError:error}
  }

  //渲染组件出错会调用这个函数,一般用于做统计错误,反馈给服务器,用于通知编码人员进行bug修复
  componentDidCatch(){
    console.log('渲染组件出错')
  }
  
  render() {
    return (
      <div>
        <h2>我是Parent组件</h2>
        {this.state.hasError?<h2>出错啦...</h2>:<Child />}
      </div>
    );
  }
}

扩展9-组件之间通信方式的总结

1.组件见的关系

2.组件之间的通信方式

3.比较好的搭配

41 react-router6

(41-1)router6的基本使用

import React from "react";
import {NavLink,Routes,Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
export default function App() {
  return (
    <div>
      <div className="row">
        <div className="col-xs-offset-2 col-xs-8">
          <div className="page-header">
            <h2>React Router Demo</h2>
          </div>
        </div>
      </div>
      <div className="row">
        <div className="col-xs-2 col-xs-offset-2">
          <div className="list-group">
            {/* 在react考路由链接切换组件 */}
            <NavLink className="list-group-item" to="/about"> about</NavLink>
            <NavLink className="list-group-item" to="/home">Home</NavLink>
          </div>
        </div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由 */}
              <Routes>
                  <Route path="/about" element={<About/>} />
                  <Route path="/home" element={<Home/>} />
              </Routes>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

(41-2)router6的重定向

1.注册路由时使用

{/* 注册路由 */}
<Routes>
    <Route path="/about" element={<About/>} />
    <Route path="/home" element={<Home/>} />
    <Route path="/" element={<Navigate to={'/about'}/>} />
</Routes>

2.路由跳转时使用

import React ,{useState}from 'react'
import {Navigate} from 'react-router-dom'

export default function Home() {
  const [state,setState] = useState({num:1})
  function addStateNum(){
    setState({num:state.num+1})
  }
  return (
    <div>
      <h2>Home组件的内容</h2>
      //值为2的时候跳转到about组件
      {state.num === 2?<Navigate to='/about'/>:<h4>当前sum的值为:{state.num}</h4>}
      <button onClick={addStateNum}>点我+1</button>
    </div>
  )
}

(41-3)router6的NavLink高亮效果

基本使用:

<NavLink className={({isActive})=>isActive?'weijiamin':'list-group-item'} to="/about"> about</NavLink>

优化:

  function computedClassName({isActive}){
    return isActive?'list-group-item weijiamin':'list-group-item'
  }
{/* 在react考路由链接切换组件 */}
<NavLink className={computedClassName} to="/about"> about</NavLink>
<NavLink className={computedClassName} to="/home">Home</NavLink>

(41-4)router6的路由表的使用

import {useRoutes} from "react-router-dom";
路由表
  const elements=useRoutes([
    {path:'/about',element:<About/>},
    {path:'/home',element:<Home/>},
    {path:'/',element:<Navigate to={'/about'}/>},
  ])
    <div>
      <div className="row">
        <div className="col-xs-offset-2 col-xs-8">
          <div className="page-header">
            <h2>React Router Demo</h2>
          </div>
        </div>
      </div>
      <div className="row">
        <div className="col-xs-2 col-xs-offset-2">
          <div className="list-group">
            {/* 注册路由链接 */}
            <NavLink className="list-group-item" to="/about"> about</NavLink>
            <NavLink className="list-group-item" to="/home">Home</NavLink>
          </div>
        </div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由 */}
              {elements}
            </div>
          </div>
        </div>
      </div>
    </div>
import Home from "../pages/Home";
import About from "../pages/About";
import { Navigate } from "react-router-dom";

const routes = [
  { path: "/about", element: <About /> },
  { path: "/home", element: <Home /> },
  { path: "/", element: <Navigate to={"/about"} /> },
];

export default routes;

(41-5)router6嵌套路由

注册路由表
  {
    path: "/home",
    element: <Home />,
    children: [
      { path: "news", element: <News /> },
      { path: "message", element: <Message /> },
    ],
  },
import React from 'react'
import {NavLink,Outlet} from 'react-router-dom'
    <div>
      <h2>Home组件的内容</h2>
      <div>
          <div className="list-group">
            <NavLink className="list-group-item" to="news">about</NavLink>
            <NavLink className="list-group-item" to="message">about</NavLink>
          </div>
          <div>
            {/* 指定路由组件呈现的位置 */}
            <Outlet/>
          </div>
      </div>
    </div>

(41-6)router6路由传参

1.传递params参数:

<NavLink to={`detail/${item.id}/${item.title}/${item.content}`}>{item.title}</NavLink>
{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail/:id/:title/:content",
       element: <Detail />,
    },
  ],
},

1.传递search参数:

<NavLink to={`detail?id=${item.id}&title=${item.title}&content=${item.content}`}>{item.title}</NavLink>
{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail",
       element: <Detail />,
    },
  ],
},
import React from "react";
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [search,setSearch] = useSearchParams()
  return (
    <div>
      <ul>
        <li>
          <button onClick={()=>{setSearch('id=11&title=哈哈&content=呜呜')}}>点我更新收到的serch参数</button>
        </li>
        {/* 调用serrch身上的get方法来获值 */}
        <li>{search.get('id')}</li>
        <li>{search.get('title')}</li>
        <li>{search.get('content')}</li>
      </ul>
    </div>
  );
}

3.传递state参数

<NavLink to="detail" state={{id:item.id,title:item.title,content:item.content}}>
    {item.title}
</NavLink>
{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail",
       element: <Detail />,
    },
  ],
},

(41-7)编程式路由导航

import { NavLink,Outlet,useNavigate} from "react-router-dom";
  const navigate = useNavigate()
  function toDeatil(item){
    //第一个参数是要跳转的路径、第二个参数是一个配置对象
    navigate('detail',{
      replace:false,
      //目前只能携带state参数 如果要携带其他类型的参数,拼在路径中
      state:{
        id:item.id,
        title:item.title,
        content:item.content
      }
    })
  }
  function goForward(){
    navigate(1)
  }
  function goBack(){
    navigate(-1)
  }
<button onClick={()=>{toDeatil(item)}}>点我查看详情</button>
<button onClick={goForward}>前进</button>
<button onClick={goBack}>后退</button>

(41-8)Hook-useInRouterContext

import { useInRouterContext} from "react-router-dom";

console.log(useInRouterContext())

(41-9)Hook-useNavigationType

import { useNavigationType} from "react-router-dom";

console.log(useNavigationType())

(41-10)Hook-useOutlet

import { useOutlet} from "react-router-dom";

console.log(useOutlet())

(41-11)Hook-useResolvePath()

import { useResolvePath} from "react-router-dom";

console.log(useResolvePath('/user?id=88&name=777'))
举报

相关推荐

尚硅谷react教程_扩展_stateHook

JVM 尚硅谷

ShardingSphere-尚硅谷

散列表,尚硅谷

【尚硅谷】子查询

尚硅谷javaweb笔记

0 条评论