React的使用

247 阅读10分钟

React

React概述

React是一个用于构建用户界面的JavaScript库。用户界面:HTML页面
React主要用来写HTML页面,或构建web项目。
组件是React最重要的内容
React完全利用js语言自身的能力来编写ul,而不是造轮子增强HTML功能。
数据驱动视图(UI user interface)

创建React

1.安装两个react包命令:npm i react react-dom
2.创建一个react元素:(第一种了解)

    const e1 = React.createElement('h1',{title:'标题'},'hello React')
    //返回值:React元素
    //第一个参数:要成创建的React元素名称
    //第二个参数:该React元素的属性
    //第三个及其以后参数:该React元素的子节点

第二种:(通常用这种)

    ReactDOM.render(el,document.getElementById('root'))
    //第一个参数:要渲染的React元素
    //第二个参数啊:DOM对象,用于指定渲染到页面中的位置

React脚手架的使用

初始化项目命令:npx create-react-app my-app这个可以自己设置项目名称
启动项目:npm start
在脚手架中已经有react和react-dom这两个包了,无需在安装了,直接引入

    import React from 'react';
    import ReactDOM from 'react-dom';

JSX(React的核心内容)

JSX是JavaScript XML的简写,表示在JavaScript代码中写XML(HTML)格式的代码
优势:声明式语法更加直观,与HTML结构相同,降低了学习成本,提升开发效果

使用JSX创建react元素
   const title = <h1>Hello JSX</h1> 
   渲染到react元素
   ReactDOM.render(title,document.getElementById('root'))

image.png

image.png

JSX里面写js逻辑要把js写在花括号{}里面,嵌入表达式用花括号{}括起来

    const name = 'Tom'
    const tittl = (<div>Hello {name}</div>)
    //会输出Hello Tom
JSX的列表渲染

如果要渲染一组数据,可以使用数组的map()方法

    cosnt arr = [1,2,3]
    const list = (
     <ul>
      {arr.map(item => <li key='item.id'>{item}</li>)}
      //<li>1</li><li>2</li><li>3</li>
      //渲染列表时应该添加key属性,key属性的值要保证唯一
      原则:map()遍历谁,就给谁加key属性,尽量避免使用索引号作为key
      js逻辑用{}包裹,item表示数组中的每一个数据
     </ul>
    )
JSX的样式处理

1.行内样式style用花括号{}添加,不推荐
2.类名--className,在JSX里面类名要用className添加,读取到HTML中还是会显示为class = ‘title’。

React组件基础

1.React组件的介绍

组件就是将冗长的代码或重复的代码拆分成比较清晰,简短的组件,然后再组合起来实现完整的页面功能,组件化的好处就是便于编写人员读取和修改
特点:可复用,独立,可组合

2.React组件的两种创建方式

函数组件:使用js的函数(或箭头函数)创建组件

    functionn Hello() {
      return(
        <div>这是我的第一个函数组件</div>
      )
    }
    ReactDOM.render(<Hello />,root);
    //const Hello = () => <div>这是我的第一个函数组件</div>(箭头函数组件)
    //组件必选是大写字母开头,return也是必须值,如果想什么都不渲染null  
    //渲染函数组件:用函数名作为组件标签名<Hello />
    //React中标签必须闭合
    //要让组件能被其他文件引用并使用要用 export default Hello;将组件抛出去

类组件:使用ES6的class创建的组件

    class Hello extends React.Compinent {
      rander() {
        return <div>Hello Class Component</div>
      }
    }
    ReactDOM.render(<Hello />,root)
    //1:类组件也必须首字母大写
    //2:类组件应该继承React Component父类,从而可以使用父类中提供的方法和属性
    //3:类组件必须提供render()方法
    //4:render()方法必须有返回值,表示该组件的结构

一般将每个组件放到单独的js文件中:组件作为一个独立的个体,一般会放到一个单独的js文件中,方便阅读或修改
创建一个组件文件Hello.jsx,然后将组件export default Hello导出
需要引入的文件引入即可使用,用import Hello from './Hello';

3.React事件处理

语法:on+事件名称={事件处理程序},比如:onClick={() => {}};
React事件采用驼峰命名发,比如:onMouseEnter、onFocus

    class App extends React.Component {
    //点击button后触发的事件程序,写在点击事件onClick里面引入,当点击button就会打印点击事件触发了
    handleClick() {
    console.log('点击事件触发');
    }
    render() {
    return(
    <button onClick={this.handleClick}>点我</button>
    //函数组件里面直接用函数名,类里面有this指向问题,所以要写this
        )
      }
    }

事件对象
可以通过事件处理程序的参数获取到事件对象
React中的事件对象叫做:合成事件(对象)
合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题

    function handleClick(e) {
      e.preventDefault()
      console.log('事件对象',e)
    }
    <a onClick={handleClick}>点我,不会跳转页面</a>

函数组件叫做无状态组件,类组件叫做有状态组件
状态(state)即数据
函数组件没有自己的状态,只负责数据展示(静)
类组件有自己的状态,负责更新UI,让页面”动“起来

组件中的state和setState()

state的基本使用:
状态(state)即数据,是组件内部的私有数据,只能在组件内部使用,state的值是对象,表示一个组件中可以有多个数据
setState()修改状态,状态是可变的
语法:this.setState({要修改的数据}),不要直接修改state中的值,只是错误的

    class App extends React.Component {
      state = {
      count: 0,
      test:'1'
      }
    render() {
    return(
    <div>
      <div>计数器:{this.state.count}</div>
      <button onClick={() => {
        this.setState({
          count:this.state.count+1
        //只改变局部内容,不会重新加载整个页面,减少运行,数据驱动视图(UI)
        })
      }}>+1</button>
    </div>
        )
      }
    }

从JSX中抽离事件处理程序:将js逻辑代码抽离到单独的方法中,保证JSX结构清晰

事件绑定this的指向
  1. 箭头函数:利用箭头函数自身不绑定this的特点,箭头函数上没有this指向,会指向外层,就能使方法中的this指向外层的实例。 如:<button onClick = {() => {this.handleClick}}>点击</button>
  2. function.prototype.bind():利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起,只要这个函数绑定了bind(this)不管谁调用都会指向绑定好的这个this。
    在constructor里面绑定: this.事件方法 = this.事件方法.bind(this);
  3. class的实例方法:利用箭头函数形式的class实例方法:在constructor里构造方法时写成:onClick = (() => {事件处理})

表单组件

    //受控组件
    state = {txt:''}
    //在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
    <input type = ”text“ value={this.state.txt} onChange = {e => this.setState{txt: e.target.value}}/>
    //给表单绑定change事件,将表单元素的值,设置为state的值(控制表单元素值的变化)
    

非受控组件

image.png

组件通信

组件是封闭的,要接收外部数据应该通过props来实现,props的作用:接收传递给组件的数据

    <Hello name="jack" age={19}/>//非字符串型的数据都要用花括号{}
    function Hello(props) {
    console.log(props)
    return(
    <div>接收到数据:{props.name}</div>  jack
    )
    }
    //函数组件用参数props接收数据,类组件通过this。props接收数据

props的特点;

  1. 可以给组件传递任意类型的数据
  2. props是只读的对象,只能读取属性的值,无法修改对象
  3. 使用类组件时,如果写了构造函数,涂改将props传递给super(),否则,无法在构造函数中获取到props。
Context(跨组价传递参数)多个组件时使用
  1. React.createContext()创建一个Provider(提供数据)和Consumer(消费数据)两个组件
    const = {Provider , Consumer} = React.createContext()
  1. 使用Provider组件作为父节点
    <Provider>
    <div className = "App">
    <Child />
    </div>
    </Provider>
  1. 设置value属性,表示要传递的数据
    <Provider value="pink">
    </Provider>
  1. 调用Consumer组件接收数据,写在想要接收数据的组件中
    <Consumer>
    {data => <span>data参数表示接收到数据——{data}</span>}
    </Consumer>
    //data接收value传递过来的数据
props深入
  1. children属性:表示组件标签子节点,组件标签中有子节点的时候,才会有,这个值可以是任意值,通过props.children可以捕捉到。
  2. props校验:允许在创建组件的时候,就指定props的类型、格式等。
    App.propsType = {
    colors:PropsType.array//指定props输入类型为数组
    }
    //1. 安装包prop-types(npm i props-types)
    //2. 导入prop-types包
    //3. 使用组件名.propTypes={}来给组件的props添加校验规则

props校验,1.常见类型:array、bool、func、number、object、string.
2.React元素类型:element
3.必填项:isRequired

组件的生命周期

组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程
只有类组件才有生命周期

生命周期的三个阶段
  1. 创建时(挂载阶段)
    执行时机:组件创建时(页面加载时)
    钩子函数的执行顺序:construcot()->render()->componentDidMount()

image.png

  1. 更新时
    执行时机:1.setState() 2.forceUpdate() 3.组件接收到新的props,这三种任意一种变化,组件就会重新渲染
    钩子函数执行顺序:render->componentDidUpdaate()

image.png

3.卸载时
执行时机:组件从页面中消失
钩子函数:

image.png

高阶组件

高阶组件是为了实现状态逻辑复用,采用包装模式,包装过后,该组件就具备原来它不具备的功能
使用步骤

  1. 创建一个函数,名称约定以with开头
  2. 指定函数参数,参数应该以大写字符开头(作为要渲染的组件)
  3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回类组件
  4. 在该组件中,渲染参数组件,同时将状态通过props传递给参数组件
  5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
    function withMouse(WrappedComponent){
      calss Mouse extends React.Component {}
      return Mouse
    }
    
    //Mouse组件的render方法中:
    //渲染组件并将状态通过props传递给参数组件
    return<WrappedComponent {...this.state} />

设置displayName 使用高阶组件存在的问题:得到两个组件名称相同
一般情况下,react使用组件名称作为displayName
解决方式:为高阶组件设置display便于调试时区分不同的组件
设置方式:

    Mouse.dispalyName = 'WithMouse${getDisplayName(WrappedComponent)}'
    
    function getDisplayName(WrappedComponent) {
    return WrappedComponent.displayName || WrappedComponent.name//组件名称 ||‘Component’
    }

高阶组件传递props丢失
解决办法:渲染WrappedComponent时,将state和this.props一起传递给组件

    <WrappedComponent{...this.state} {...this.props} />
react里面引入图片的常用方法
react引入站内图片的两种方式:
1. 通过import引入图片
   import kill from “../images/jisha.png”;//引入图片
   然后调用<img src = {kill} alt = "" />//img是单标签,标签要闭环
2. 在src当中使用require方法
   <img src = {require("../images/jisha.png").default} alt = "">

React原理

setState()的说明

  1. 更新数据
    setState()是异步更新数据的
    注意:使用该语法时,后面的setState()不要依赖于前面的setState()
    可以多次调用setState(),只会触发一次重新渲染(render)
  2. 推荐语法
    使用setState((state,props) => {})语法
    参数state:表示最新的state
    参数props:表示最新的props
    this.setState((state,props) =>{
    return(
    count:state.count+1
    )
    })
    console.log(thsi.state.count)//1

setState()的第二个参数
语法:setState((state,props) =>{},callback)

    this.setState(
    (state,props) => {},
    () => {console.log("这个回调函数会在状态更新后立即执行")}//回调函数
    )

组件更新机制

父组件更新会引起子组件也被更新

组件性能优化

  1. 减轻state:只存储跟组件渲染相关的数据(比如:count/列表数据/loading等)
    不用做渲染的数据不要放在state中,比如定时器id等,对于这种需要在多个方法中用到的数据,应该放在this中
  2. 避免不必要的重新渲染
    如何避免不必要的渲染:
    解决方式:使用钩子函数:shouldComponentUpdate(nextProps,nextState))——nextstate是最新的状态。通过返回值(return)决定组件是否重新渲染,返回true表示重新渲染,返回false表示不重新渲染
    触发机制:更新阶段的钩子函数,组件重新渲染执行顺序(shouldComponentUpdate->render)
  3. 纯组件
    78集,79集未看

虚拟DOM和Diff算法

react中通过虚拟DOM配合diff算法实现部分更新,只把变化的内容重新渲染
diff算法就是递归DOM找不同,对变化的部分进行重新渲染