React学习笔记

81 阅读11分钟

React基础

hello world

<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel"> // 此处一定要写babel
    // 创建虚拟DOM
    const VDOM = <h1>hello react</h1> //此处不要写引号,因为不是字符串
    // 渲染虚拟DOM到页面
    ReactDOM.render(VDOM, document.getElementById('test'))
  </script>
</body>

虚拟DOM的两种创建方式

  • 使用 JS
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>

  <script type="text/javascript">
    // 创建虚拟DOM
    const VDOM = React.createElement('h1', {
      id: 'title'
    },
      'Hello React'
    )
    // 渲染虚拟DOM到页面
    ReactDOM.render(VDOM, document.getElementById('test'))
  </script>
</body>
  • 使用 JSX
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel"> // 此处一定要写babel
    // 创建虚拟DOM
    const VDOM = (
      <h1 id="title">hello react</h1> //此处不要写引号,因为不是字符串
    ) 
    // 渲染虚拟DOM到页面
    ReactDOM.render(VDOM, document.getElementById('test'))
  </script>
</body>

JSX语法规则

<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel">
    const myId = "Tom"
    const myData = 'hEllo reAct'
    // 创建虚拟DOM
    const VDOM = (
      <h2 className="title" id={myId.toLowerCase()}>
        <span style={{ color: 'blue', fontSize: '28px' }}>{myData.toLowerCase()}</span>
      </h2>

    )
    // 渲染虚拟DOM到页面
    ReactDOM.render(VDOM, document.getElementById('test'))
    /**
     * 1. 标签中混入`JS表达式`时,要使用 {}
     * 2. 样式的类名指定不要用class, 要用className
     * 3. 内联样式要用style={{key:value,key:value}}的形式去写
     * 4. 只有一个根标签
     * 5. 标签必须闭合
     * 6. 标签首字母
     *      1. 小写字母: html标签
     *      2. 大写字母: 组件标签,若组件没有定义,就报错
     */
  </script>
</body>

React中定义组件

  • 函数式组件
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel">
    // 创建函数式组件
    function Demo() {
      console.log(this); //undefined ,babel会开启严格模式,禁止this指向window
      return <h2>我是函数式组件</h2>
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
  </script>
</body>
  • 类式组件
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel">
   //创建类式组件
   class MyComponent extends React.Component{
    // render放在了 MyComponent的原型对象上,供实例使用
    //因此render中的this是 MyComponent组件实例对象
     render() {
        console.log('render中的this--->',this);
         return <h2>我是类式组件</h2>
      }
   }
   // 渲染组件到页面
   ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  </script>
</body>

组件实例三大属性之——state

  • 复习:bind
<script>
    function fun() {
      console.log(this);
    }
    let x = fun.bind({ a: 1, b: 2 })
    fun() //window
    x() //{ a: 1, b: 2 }
</script>
  • 复习:类中方法中this指向
<script>
    // 类中所有方法都默认自动开启了局部严格模式
    class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }
      speak() {
        //speak方法放在了类的原型对象上,供实例使用,通过Person实例调用speak时,speak中的this指向的是Person实例
        console.log(this);
      }
    }
    const p1 = new Person('tom', 12)
    p1.speak() //通过实例调用speak方法
    const x = p1.speak
    // 当 x() 被调用时,输出的是 undefined,这是因为类方法中的 this 在非实例调用时,在严格模式下被绑定为 undefined。
    x() //非实例调用
  </script>
  • 复杂写法
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel">
    // 创建组件
    class Weather extends React.Component {
      // 构造器调用几次?----1次
      constructor(props) {
        super(props)
        this.state = { isHot: true }
        // 将原型上的changeWeather方法绑定this并赋值给实例对象上的changeWeather方法
        this.changeWeather = this.changeWeather.bind(this)
      }
      // render调用几次?----1+n次,1是初始化的那次,n是状态更新的次数
      render() {
        // console.log(this);
        // onclick要写onClick
        return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
      }
      //changeWeather调用几次? 点几次,调几次
      changeWeather() {
        console.log('changeWeather被调用了');
        // 由于changeWeather作为onClick的回调,不是通过实例对象调用的,而是直接调用,而且还由于类中的方法默认开启了严格模式,所以这里的this指向undefined
        // 注意:state不可直接更改,要借助一个内置的API来更改
        // this.state.isHot = !this.state.isHot //错误
        // 注意:状态必须通过 setState进行更新
        this.setState({ isHot: !this.state.isHot })
        console.log(this.state.isHot);
        console.log(this);
      }
    }
    //渲染页面
    ReactDOM.render(<Weather />, document.getElementById('test'))
  </script>
</body>
  • 简便写法
<body>
  <div id="test">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>

  <script type="text/babel">
    // 创建组件
    class Weather extends React.Component {
      // 初始化状态
      state = { isHot: true }
      render() {
        console.log(this);
        return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
      }

      // 自定义方法(赋值语句 + 箭头函数的形式)
      changeWeather = () => {
        this.setState({ isHot: !this.state.isHot })
      }
    }
    //渲染页面
    ReactDOM.render(<Weather />, document.getElementById('test'))
  </script>
</body>

组件实例三大属性之——props

  • 基础用法
<body>
  <div id="test1">

  </div>
  <div id="test2">

  </div>
  <!-- 引入react -->
  <script src="../js/react.development.js"></script>
  <script src="../js/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="../js/babel.min.js"></script>
  <!-- 引入prop-types,用于对组件标签属性进行限制 -->
  <script src="../js/prop-types.js"></script>

  <script type="text/babel">
    //创建组件
    class Person extends React.Component {
      render() {
        const { name, age, sex } = this.props 
        // props是只读的
        // this.props.name = 'king'
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age + 1}</li>
          </ul>
        )
      }
    }
    //对标签属性值进行类型和必要性的限制
    Person.propTypes = {
      // 从React16开始,PropTypes不再是在React中引入,而是在prop-types中引入
      name: PropTypes.string.isRequired,
      sex: PropTypes.string,
      age: PropTypes.number,
      speak: PropTypes.func // 必须是函数,不能用function,要用func
    }
    //指定默认的标签属性值
    Person.defaultProps = {
      sex: '不男不女',
      age: 19
    }
    //渲染组件到页面
    ReactDOM.render(<Person name="tom" age={18} sex="男" speak={speak} />, document.getElementById('test1'))
    ReactDOM.render(<Person name="jerry" age={19} sex="男" />, document.getElementById('test1'))
    const p = { name: '老刘' }
    ReactDOM.render(<Person {...p} />, document.getElementById('test2'))

    function speak() {
      console.log('说话');
    }
  </script>
</body>
  • 简写方式
<script type="text/babel">
    //创建组件
    class Person extends React.Component {
      // 构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props
      // 开发中几乎不写构造器
      constructor(props){
        super(props)
        console.log(this.props);
        console.log(props);
      }
      //给类加属性,而不是给类的实例对象加属性,只需要在属性名前面加 static
      static propTypes = {
        name: PropTypes.string.isRequired,
        sex: PropTypes.string,
        age: PropTypes.number,
      }
      static defaultProps = {
        sex: '不男不女',
        age: 19
      }
      render() {
        const { name, age, sex } = this.props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age + 1}</li>
          </ul>
        )
      }
    }
    //渲染组件到页面
    ReactDOM.render(<Person name="tom" age={18} sex="男"/>, document.getElementById('test1'))
</script>
  • 函数式组件使用props
<script type="text/babel">
    // 函数式组件
    function Person(props) {
      const { name, age, sex } = props
      return (
        <ul>
          <li>姓名:{name}</li>
          <li>性别:{sex}</li>
          <li>年龄:{age + 1}</li>
        </ul>
      )
    }
    Person.propTypes = {
      name: PropTypes.string.isRequired,
      sex: PropTypes.string,
      age: PropTypes.number,
    }
    Person.defaultProps = {
      sex: '不男不女',
      age: 19
    }
    //渲染组件到页面
    ReactDOM.render(<Person name="tom" age={18} sex="男" />, document.getElementById('test1'))
</script>

组件实例三大属性之——refs

  • 字符串形式的ref
<script type="text/babel">
    // 创建组件
    class Demo extends React.Component {
      //展示左侧输入框的数据
      showData = () => {
        const { input1 } = this.refs
        alert(input1.value)
      }
      showData2 = () => {
        const { input2 } = this.refs
        alert(input2.value)
      }
      render() {
        return (
          <div>
            <input ref="input1" type="text" />
            <button onClick={this.showData}>点我提示左侧数据</button>
            <input onBlur={this.showData2} ref="input2" type="text" placeholder="输入内容后请失去焦点" />
          </div>
        )
      }
    }
    //渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
  • 回调函数形式的ref
<script type="text/babel">
    // 创建组件
    class Demo extends React.Component {
      //展示左侧输入框的数据
      showData = () => {
        const {input1} = this
        alert(input1.value)
      }
      showData2 = () => {
        const {input2} = this
        alert(input2.value)
      }
      render() {
        return (
          <div>
            <input ref={c => this.input1 = c} type="text" />
            <button onClick={this.showData}>点我提示左侧数据</button>
            <input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="输入内容后请手动失去焦点" />
          </div>
        )
      }
    }
    //渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
 </script>
  • 回调ref中回调次数问题
<script type="text/babel">
    // 创建组件
    class Demo extends React.Component {
      state = { isHot: true }
      showInfo = () => {
        const { input1 } = this
        alert(input1.value)
      }
      changWeather = () => {
        const { isHot } = this.state
        this.setState({ isHot: !isHot })
      }
      //如果ref回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null,第二次传入参数DOM元素
      //这是因为在每次渲染时会创建一个新的函数实例,所以React清空旧的ref并设置新的
      //定义成class的绑定函数,可以避免上述问题
      saveInput = (c)=>{
        this.input1 = c
        console.log('@', c)
      }
      render() {
        const { isHot } = this.state
        return (
          <div>
            <h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
            {/*jsx的注释方式*/}
            {/*<input ref={(c) => { this.input1 = c; console.log('@', c); }} type="text" /><br/>*/}
            <input ref={this.saveInput} type="text" />
            <button onClick={this.showInfo}>点我</button>
            <button onClick={this.changWeather}>点我切换天气</button>
          </div>
        )
      }
    }
    //渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
  • createRef
<script type="text/babel">
    // 创建组件
    class Demo extends React.Component {
      /*
       React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点
       该容器是专人专用的,因此不能在多个组件间复用
      */
      myRef = React.createRef()
      myRef2 = React.createRef()
      //展示左侧输入框的数据
      showData = () => {
        console.log(this.myRef.current.value);
        alert(this.myRef.current.value)
      }
      showData2 = () => {
        console.log(this.myRef2.current.value);
        alert(this.myRef2.current.value)
      }
      render() {
        return (
          <div>
            <input ref={this.myRef} type="text" />
            <button onClick={this.showData}>点我提示左侧数据</button>
            <input onBlur={this.showData2} ref={this.myRef2} type="text" />
          </div>
        )
      }
    }
    //渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
</script>

React中的事件处理

/*
    通过onXxx属性指定事件处理函数
    React使用的是自定义事件,而不是原生的DOM事件————为了更好的兼容性
    React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)————为了高效
    通过event.target得到发生事件的DOM元素对象———— 不要过度使用ref
*/

React中的收集表单数据

  • 非受控组件
<script type="text/babel"> // 此处一定要写babel
    class Login extends React.Component {
      handleSubmit = (e) => {
        // 阻止表单的默认提交行为
        e.preventDefault()
        const { username, password } = e.target
        alert(`用户名:${username.value} 密码:${password.value}`)
      }
      render() {
        return (
          <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
            用户名:<input type="text" name="username" />
            密码:<input type="password" name="password" />
            <button>登录</button>
          </form>
        )
      }
    }
    ReactDOM.render(<Login />, document.getElementById('test'))
</script>
  • 受控组件
<script type="text/babel"> // 此处一定要写babel
    class Login extends React.Component {
      state = {
        username: '',
        password: ''
      }
      handleSubmit = (e) => {
        // 阻止表单的默认提交行为
        e.preventDefault()
        const { username, password } = this.state
        alert(`用户名:${username} 密码:${password}`)
      }
      //保存用户名到状态
      saveUsername = (e) => {
        // console.log(e.target.value);
        this.setState({ username: e.target.value })
      }
      savePassword = (e) => {
        this.setState({ password: e.target.value })
      }
      // 非受控组件:现用现取
      // 受控组件:页面中所有输入类的DOM,随着输入把数据维护到状态中,需要用到的时候从状态中取出来
      render() {
        return (
          <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
            用户名:<input onChange={this.saveUsername} type="text" name="username" />
            密码:<input onChange={this.savePassword} type="password" name="password" />
            <button>登录</button>
          </form>
        )
      }
    }
    ReactDOM.render(<Login />, document.getElementById('test'))
</script>

高阶函数_函数柯里化

  • 简单例子
<script>    
    function sum(a) {
      return (b) => {
        return (c) => {
          return a + b + c;
        }
      }
    }
    const res = sum(1)(2)(3)
    console.log(res);
</script>
<script type="text/babel">
    class Login extends React.Component {
      state = {
        username: '',
        password: ''
      }
      handleSubmit = (e) => {
        e.preventDefault()
        const { username, password } = this.state
        alert(`用户名:${username} 密码:${password}`)
      }
      /*
       高阶函数:如果一个函数符合下面两个规范中的任何一个,那该函数就是高阶函数
          1. 若A函数,接收的参数是一个函数,那么A就是高阶函数。
          2. 若A函数,调用的返回值是一个函数,那么A就是高阶函数。
          常见的高阶函数:Promise,setTimeout,arr.map()...........
        
       函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
      */
      saveFormData = (dataType) => {
        //返回一个函数,这个函数就是onChange的回调函数
        return (e) => {
          // console.log(dataType, e.target.value);
          this.setState({ [dataType]: e.target.value })
        }
      }
      render() {
        return (
          <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
            用户名:<input onChange={this.saveFormData('username')} type="text" name="username" />
            密码:<input onChange={this.saveFormData('password')} type="password" name="password" />
            <button>登录</button>
          </form>
        )
      }
    }
    ReactDOM.render(<Login />, document.getElementById('test'))
 </script>

React中组件的生命周期

  • 生命周期图(新)

image.png

  • 生命周期图(旧)

image.png

<script type="text/babel">
        class Life extends React.Component {
            state = {
                opacity: 1
            }
            death = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }

            //这个函数是要被Life的实例对象调用的,与render函数同级别
            //调用时机:组件挂载完毕
            componentDidMount() {
                this.timer = setInterval(() => {
                    let { opacity } = this.state
                    opacity -= 0.1
                    if (opacity <= 0) {
                        opacity = 1
                    }
                    this.setState({ opacity })
                }, 200);
            }
            //组件将要被卸载时调用
            componentWillUnmount(){
                clearInterval(this.timer)
            }
            //render调用时机:1.初始化渲染 2.状态更新之后
            render() {
                return (
                    <div>
                        <h2 style={{ opacity: this.state.opacity }}>react学不会怎么办</h2>
                        <button onClick={this.death}>不活了</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Life />, document.getElementById('test'));
 </script>
  • react生命周期(旧)
<script type="text/babel">
        class Count extends React.Component {
            constructor(props) {
                console.log('Count构造器')
                super(props)
                this.state = {
                    count: 0
                }
            }

            add = () => {
                let { count } = this.state
                this.setState({
                    count: count + 1
                })
            }
            force = () => {
                this.forceUpdate()
            }
            //组件将要挂载时
            componentWillMount() {
                console.log('Count will mount')
            }
            //组件挂载完毕
            componentDidMount() {
                console.log('Count did mount')
            }
            //将要卸载组件
            death = () => {
                console.log('Count will unmount');
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            //控制组件更新的阀门
            shouldComponentUpdate() {
                console.log('Count shouldComponentUpdate');
                return true
            }
            //组件将要更新
            componentWillUpdate() {
                console.log('Count will update');
            }
            //组件更新完毕
            componentDidUpdate() {
                console.log('Count did update');
            }
            render() {
                console.log('Count render')
                const { count } = this.state
                return (
                    <div>
                        <h2>当前求和为{count}</h2>
                        <button onClick={this.add}>点我加1</button>
                        <button onClick={this.death}>卸载组件</button>
                        <button onClick={this.force}>强制更新,不更改状态中的数据</button>
                    </div>
                )
            }
        }

        class A extends React.Component {
            state = {
                carName: '奔驰'
            }
            changeCar = () => {
                this.setState({
                    carName: '宝马'
                })
            }
            render() {
                console.log('A render')
                return (
                    <div>
                        <h2>我是A组件</h2>
                        <button onClick={this.changeCar}>换车</button>
                        <B carName={this.state.carName} />
                    </div>
                )
            }
        }
        class B extends React.Component {
            //组件将要接收新的属性
            componentWillReceiveProps(props) {
                console.log('B will receive props', props);
            }
            shouldComponentUpdate() {
                console.log('B shouldComponentUpdate');
                return true
            }
            //组件将要更新
            componentWillUpdate() {
                console.log('B will update');
            }
            //组件更新完毕
            componentDidUpdate() {
                console.log('B did update');
            }
            render() {
                console.log('B render')
                return (
                    <div>
                        <h2>我是B组件,接收到的车是:{this.props.carName}</h2>
                    </div>
                )
            }
        }
        ReactDOM.render(<Count />, document.getElementById('test'))
 </script>
  • react生命周期(新)
<script type="text/babel">
        class Count extends React.Component {
            constructor(props) {
                console.log('Count构造器')
                super(props)
                this.state = {
                    count: 0
                }
            }

            add = () => {
                let { count } = this.state
                this.setState({
                    count: count + 1
                })
            }
            force = () => {
                this.forceUpdate()
            }
            //用途:若state的值在任何时候都取决于props的值。不推荐使用
            static getDerivedStateFromProps(props,state){
                console.log('Count getDerivedStateFromProps',props,state)
                return null
            }
            //在更新之前获取快照
            //返回值将作为参数传递给componentDidUpdate
            getSnapshotBeforeUpdate(){
                console.log('Count getSnapshotBeforeUpdate');
                return 1
            }
            //组件挂载完毕
            componentDidMount() {
                console.log('Count did mount')
            }
            //将要卸载组件
            death = () => {
                console.log('Count will unmount');
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            //控制组件更新的阀门
            shouldComponentUpdate() {
                console.log('Count shouldComponentUpdate');
                return true
            }
      
            //组件更新完毕
            componentDidUpdate(preProps,preState,snapshotValue) {
                console.log('Count did update',preProps,preState,snapshotValue);
            }
            render() {
                console.log('Count render')
                const { count } = this.state
                return (
                    <div>
                        <h2>当前求和为{count}</h2>
                        <button onClick={this.add}>点我加1</button>
                        <button onClick={this.death}>卸载组件</button>
                        <button onClick={this.force}>强制更新,不更改状态中的数据</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Count count={122}/>, document.getElementById('test'))
  </script>
  • getSnapshotBeforeUpdate使用
<script type="text/babel">
    class NewsList extends React.Component {
      state = {
        newsArr: []
      }
      componentDidMount() {
        setInterval(() => {
          //获取原状态
          const { newsArr } = this.state
          //模拟一条新闻
          const news = '新闻' + (newsArr.length + 1)
          //更新状态
          this.setState({
            newsArr: [news, ...newsArr]
          })
        }, 1000);
      }
      getSnapshotBeforeUpdate(){
        return this.refs.list.scrollHeight
      }
      componentDidUpdate(preProps,preState,snapshotValue){
        this.refs.list.scrollTop += this.refs.list.scrollHeight - snapshotValue
      }

      render() {
        return (
          <div>
            <div className="list" ref="list">
              {
                this.state.newsArr.map((n, index) => {
                  return <div key={index} className="news">{n}</div>
                })
              }
            </div>
          </div>
        )
      }
    }
    // 渲染虚拟DOM到页面
    ReactDOM.render(<NewsList />, document.getElementById('test'))
</script>

React进阶

Create React App官网

css模块化

  • css文件命名为 xxx.module.css
  • 在组件中引用样式文件:import hello from './xxx.module.css'
  • 在组件中使用样式:<div className={hello.样式类名}></div>

vscode插件

  • ES7 React/Redux/GraphQL/React-Native snippets
  • rcc和rfc分别创建类式组件和函数式组件
  • imp 引入组件
  • 其他快捷命令可查看插件文档

prop-types安装

  • npm i prop-types

js基础语法

let obj = {a:1,b:2}
delete obj.a
console.log(obj)
// confirm要写成window.confirm,否则不识别
removeTodo = (id)=>{
  if(window.confirm('确定删除吗?')){
    this.props.removeTodo(id)
  }
}

脚手架配置代理

  • 安装axios:npm i axios

  • Create React App官方文档:https://create-react-app.dev/docs/proxying-api-requests-in-development/

  • React18使用 http-proxy-middleware代理跨域 npm install http-proxy-middleware --save

    src/setupProxy.js (配置好后一定重启项目)

    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    module.exports = function(app) {
      app.use(
        '/api',
        createProxyMiddleware({
          target: 'http://www.xxx.xxx',
          changeOrigin: true,
          pathRewrite: {
            '^/api': ''
          }
        })
      )
    }
    

兄弟组件间通信-消息订阅与发布

  • 安装:npm i pubsub-js --save
  • 使用:
    1. 引入:import PubSub from 'pubsub-js'
    2. 订阅:PubSub.subscribe('事件名称',回调函数)
    3. 发布消息:PubSub.publish('事件名称',数据)
    4. 要在组件的componentWillUnmount中取消订阅:PubSub.unsubscribe('事件名称')

react-router-dom

  • react-router分为三种:web、native、any 。其中web是专门给网页开发用的,native是专门给移动端开发的,any是通用的。
  • 安装:npm i react-router-dom
  • 基本使用
    1. 引入:import { Link, Route, Routes } from 'react-router-dom'
    2. 编写路由链接: <Link className="list-group-item" to="/home">Home</Link>
    3. 注册路由:
      <Routes>
        <Route path='/about' element={<About />} />
        <Route path='/home' element={<Home a={1}/>} />
      </Routes>
      
    4. 在index.js中引入BrowserRouter包裹App组件:<BrowserRouter> <App /> </BrowserRouter>
  • 路由组件与一般组件
    • 一般组件:<Home/>
    • 路由组件:<Route path="/home" component={Home}/>(旧) <Route path="/home" element={<Home a={1}/>}>(新)
    1. https://baimingxuan.github.io/react-router6-doc/components/nav-link
    2. 封装组件 MyNavLink.jsx
    import React, { Component } from 'react'
    import { NavLink } from 'react-router-dom'
    
    export default class MyNavLink extends Component {
      render() {
        console.log(this.props);
        return (
          <NavLink className={({ isActive }) => isActive ? "active" : ""} {...this.props} />
        )
      }
    }
    
    App.jsx
    <!-- 下面标签中标签体内容'Home'会被作为props.children传给NavLink组件,children属性决定了标签体的内容。NavLink中使用{...this.props}会将所有传来的props都传给<NavLink>组件,其中就包括children属性 -->
    <MyNavLink to="/home">Home</MyNavLink>
    
    1. V6 不再使用 <Switch>, 将所有的 <Switch> 元素升级为 <Routes>,与 Switch 相比, Routes 的主要优势在于:
    • <Routes> 中的所有 <Route><Link> 都是相对的。这导致在 <Route path> <Link to> 中的代码更精简和更可预测。
    • 路由的选择基于最佳匹配,而不是按顺序遍历。这避免了由于在<Switch> 中定义较晚而导致无法到达的错误。
    • 路由可以嵌套在一个地方,而不是分散在不同的组件中。
    1. 解决样式丢失问题
    • 丢失原因:浏览器地址位于例如/hello/home路由路径下,刷新浏览器后,找不到http://localhost:3000/hello/下有css样式文件,默认返回index.html页面
    • 解决办法:
    //方法一
    <link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.min.css">
    //方法二
    <link rel="stylesheet" href="/css/bootstrap.min.css">
    //方法三,依然用 ./css开头,但是路由模式使用 HashRouter
    <link rel="stylesheet" href="./css/bootstrap.min.css">
    <!-- index.js中 -->
    import React from 'react'
    import ReactDOM from 'react-dom/client'
    import { BrowserRouter,HashRouter } from 'react-router-dom'
    import App from './App'
    
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(
      <React.StrictMode>
        <HashRouter>
          <App />
        </HashRouter>
      </React.StrictMode>
    )