zoukankan      html  css  js  c++  java
  • React

    一、首先是React的核心概念

    React的核心概念大概就是两个,一个是虚拟DOM另一个是diff算法。正式因为有了这两个核心概念,使得React的性能大大的提升。

    首先虚拟DOM:

    虚拟DOM就是对于DOM的抽象,虚拟DOM就是一个对象,用这个对象来描述DOM,每次通过对比DOM的变化差异,而选择对变化的部分重新渲染,并不是直接的在DOM上进行操作,从而提升的渲染的效率。

    接下来是diff算法:

    在某一时刻调用render()函数渲染了一棵元素树,在下一次state或者props更新时,又会生成一棵元素树,而diff算法就是对比这两棵树的差异点,从而只改变变化的部分。

    对于根节点元素类型不同时,会拆掉原有的树并且会建一棵新的树。就相当于拆卸掉原有树上的所有节点然后重建。拆卸时会触发componentWillUnmount()函数,而重建会触发新建流程,从componentWillMount()函数,紧接着是componentDidMount()函数。

    对于同一类型的元素,React 会保留 DOM 节点,仅比对及更新有改变的属性。

    对于同类型组件元素,组件更新时,对于state不变,而props改变时调用componentWillReceiveProps()函数和componentWillUpdate()函数对元素进行改变。

    对于在子节点后面添加节点,react会匹配旧结点,然后完成新节点的插入

    对于在子节点前面插入节点,React会提供key属性,用key属性来匹配旧结点,对于有key属性的节点仅仅移动位置,对于新的节点再添加。

    二、React的使用

    首先导入react;然后创建DOM;接着渲染。

    // 1. 导入 react
    import React from 'react'
    import ReactDOM from 'react-dom'
    
    // 2. 创建 虚拟DOM
    // 参数1:元素名称  参数2:元素属性对象(null表示无)  参数3:当前元素的子元素string||createElement() 的返回值
    const divVD = React.createElement('div', {
      title: 'hello react'
    }, 'Hello React!!!')
    
    // 3. 渲染
    // 参数1:虚拟dom对象  参数2:dom对象表示渲染到哪个元素内 参数3:回调函数
    ReactDOM.render(divVD, document.getElementById('app'))

    三、React创建组件两种方式

    第一种通过函数来创建,通过JS函数来创建,也叫无状态组件,这种创建仅仅是为了展示数据,不需要对于数据进行改变。

    function Welcome(props) {
        return (
            <h1>Hello {props.name}</h1>
        )
      }
      
      ReactDOM.render(
        <Welcome name="jack" />,
        document.getElementById('app')
      )

    第二种通过class来创建,也叫有状态组件,可以操作数据而不是简单的展示数据。

    class Clock extends React.Component {
      render() {
        return (
          <div>
            <h1>Hello, world!</h1>
            <h2>现在是 {this.props.date.toLocaleTimeString()}.</h2>
          </div>
        );
      }
    }
     
    function tick() {
      ReactDOM.render(
        <Clock date={new Date()} />,
        document.getElementById('example')
      );
    }

    四、props和state

    props:用于父子组件中传递数据,只读不可修改添加,props.children获取组件的内容。

    function Welcome(props) {
      // 返回的 react元素中必须只有一个根元素
      return <div>hello, {props.name}</div>
    }
    
    class Welcome extends React.Component {
      constructor(props) {
        super(props)
      }
    
      render() {
        return <h1>Hello, {this.props.name}</h1>
      }
    }

    state:数据,用于组件内部数据传递,也叫状态,只有class创建才有state,sate是私有的,由组件来控制。

    class Hello extends React.Component {
      constructor() {
        // es6继承必须用super调用父类的constructor
        super()
    
        this.state = {
          gender: 'male'
        }
      }
    
      render() {
        return (
          <div>性别:{ this.state.gender }</div>
        )
      }
    }

    五、组件生命周期

    1、创建:constructor() 、componentWillMount() 、render() 、componentDidMount()

    2、运行交互:componentWillReceiveProps() 、shouldComponentUpdate() 、componentWillUpdate() 、render() 、componentDidUpdate()

    3、卸载:componentWillUnmount()

    创建阶段:该阶段函数只执行一次。

    constructor():获取props、设置state。

    class Greeting extends React.Component {
      constructor(props) {
        // 获取 props
        super(props)
        // 初始化 state
        this.state = {
          count: props.initCount
        }
      }
    }
    
    // 初始化 props
    // 语法:通过静态属性 defaultProps 来初始化props
    Greeting.defaultProps = {
      initCount: 0
    };

    componentWillMount() :

    • 说明:组件被挂载到页面之前调用,其在render()之前被调用,因此在这方法里同步地设置状态将不会触发重渲染
    • 注意:无法获取页面中的DOM对象
    • 注意:可以调用 setState() 方法来改变状态值
    • 用途:发送ajax请求获取数据
    componentWillMount() {
      console.warn(document.getElementById('btn')) // null
      this.setState({
        count: this.state.count + 1
      })
    }

    render() :

    • 作用:渲染组件到页面中,无法获取页面中的DOM对象
    • 注意:不要在render方法中调用 setState() 方法,否则会递归渲染

      • 原因说明:状态改变会重新调用render()render()又重新改变状态
    render() {
      console.warn(document.getElementById('btn')) // null
    
      return (
        <div>
          <button id="btn" onClick={this.handleAdd}>打豆豆一次</button>
          {
            this.state.count === 4
            ? null
            : <CounterChild initCount={this.state.count}></CounterChild>
          }
        </div>
      )
    }

    componentDidMount():

    • 1 组件已经挂载到页面中
    • 2 可以进行DOM操作,比如:获取到组件内部的DOM对象
    • 3 可以发送请求获取数据
    • 4 可以通过 setState() 修改状态的值
    • 注意:在这里修改状态会重新渲染
    componentDidMount() {
      // 此时,就可以获取到组件内部的DOM对象
      console.warn('componentDidMount', document.getElementById('btn'))
    }

    接下来是运行交互阶段:

    • 特点:该阶段的函数执行多次
    • 说明:每当组件的props或者state改变的时候,都会触发运行阶段的函数

    componentWillReceiveProps() :

    • 说明:组件接受到新的props前触发这个方法
    • 参数:当前组件props
    • 可以通过 this.props 获取到上一次的值
    • 使用:若你需要响应属性的改变,可以通过对比this.propsnextProps并在该方法中使用this.setState()处理状态改变
    • 注意:修改state不会触发该方法
    componentWillReceiveProps(nextProps) {
      console.warn('componentWillReceiveProps', nextProps)
    }

    shouldComponentUpdate() :

    • 作用:根据这个方法的返回值决定是否重新渲染组件,返回true重新渲染,否则不渲染
    • 优势:通过某个条件渲染组件,降低组件渲染频率,提升组件性能
    • 说明:如果返回值为false,那么,后续render()方法不会被调用
    • 注意:这个方法必须返回布尔值!!!
    • 场景:根据随机数决定是否渲染组件
    // - 参数:
    //   - 第一个参数:最新属性对象
    //   - 第二个参数:最新状态对象
    shouldComponentUpdate(nextProps, nextState) {
      console.warn('shouldComponentUpdate', nextProps, nextState)
    
      return nextState.count % 2 === 0
    }

    componentWillUpdate() :

    • 作用:组件将要更新
    • 参数:最新的属性和状态对象
    • 注意:不能修改状态 否则会循环渲染
    componentWillUpdate(nextProps, nextState) {
      console.warn('componentWillUpdate', nextProps, nextState)
    }

    render():

    • 作用:重新渲染组件,与Mounting阶段的render是同一个函数
    • 注意:这个函数能够执行多次,只要组件的属性或状态改变了,这个方法就会重新执行

    componentDidUpdate()

    • 作用:组件已经被更新
    • 参数:旧的属性和状态对象
    componentDidUpdate(prevProps, prevState) {
      console.warn('componentDidUpdate', prevProps, prevState)
    }

    卸载阶段:

    • 组件销毁阶段:组件卸载期间,函数比较单一,只有一个函数,这个函数也有一个显著的特点:组件一辈子只能执行依次!
    • 使用说明:只要组件不再被渲染到页面中,那么这个方法就会被调用( 渲染到页面中 -> 不再渲染到页面中 )

    componentWillUnmount()

    • 作用:在卸载组件的时候,执行清理工作,比如

      • 1 清除定时器
      • 2 清除componentDidMount创建的DOM对象

     六、受控组件和非受控组件

    受控组件:

    在HTML当中,像input,textareaselect这类表单元素会维持自身状态,并根据用户输入进行更新。
    在React中,可变的状态通常保存在组件的state中,并且只能用 setState() 方法进行更新. 
    React根据初始状态渲染表单组件,接受用户后续输入,改变表单组件内部的状态。
    因此,将那些值由React控制的表单元素称为:受控组件。只能通过setState来设置受控组件的值。

    import React from "react"
    class CommentBox extends React.Component(){
        constructor(props){
            super(props);
            this.state={
                value:""
            }
            this.handleChange = this.handleChange.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this);
        }
        handleChange(event){
            this.setState({ value:event.target.value});
        }
        handleSubmit(){
            alert(this.state.value);
            event.preventDefault;
        }
        render(){
            return(
                <form onSubmit={this.handleSubmit}>
                    <input type = "text"value ={this.state.value} onClick={this.handleChange}>姓名</input>
                    <input type = "submit">提交</input>
    
                </form>
            );
        }
    }

    非受控组件:

    非受控组件数据由DOM操作。使用ref从DOM获得表单数据。

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.input = React.createRef();
      }
    
      handleSubmit(event) {
        alert('A name was submitted: ' + this.input.current.value);
        event.preventDefault();
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              Name:
              <input type="text" ref={this.input} />
            </label>
            <input type="submit" value="Submit" />
          </form>
        );
      }
    }

    七、单身数据流

    • 数据流动方向:自上而下,也就是只能由父组件传递到子组件
    • 数据都是由父组件提供的,子组件想要使用数据,都是从父组件中获取的
    • 如果多个组件都要使用某个数据,最好将这部分共享的状态提升至他们最近的父组件当中进行管理

    父子组件数据流传递:

    class Child extends React.Component{
      constructor(props){
        super(props);
        this.state = {}
      }
      
      render(){
        return (
          <div>
            {this.props.text}
            <br />
            <button onClick={this.props.refreshParent}>
                更新父组件
            </button>
          </div>
        )
      }
    }
    class Parent extends React.Component{
      constructor(props){
        super(props);
        this.state = {}
      }
      refreshChild(){
        return (e)=>{
          this.setState({
            childText: "父组件沟通子组件成功",
          })
        }
      }
      refreshParent(){
        this.setState({
          parentText: "子组件沟通父组件成功",
        })
      }
      render(){
        return (
          <div>
            <h1>父子组件沟通</h1>
            <button onClick={this.refreshChild()} >
                更新子组件
            </button>
            <Child 
              text={this.state.childText || "子组件未更新"} 
              refreshParent={this.refreshParent.bind(this)}
            />
            {this.state.parentText || "父组件未更新"}
          </div>
        )
      }
    }

    兄弟组件数据传递:借助父组件

    class Brother1 extends React.Component{
      constructor(props){
        super(props);
        this.state = {}
      }
      
      render(){
        return (
          <div>
            <button onClick={this.props.refresh}>
                更新兄弟组件
            </button>
          </div>
        )
      }
    }
    class Brother2 extends React.Component{
      constructor(props){
        super(props);
        this.state = {}
      }
      
      render(){
        return (
          <div>
             {this.props.text || "兄弟组件未更新"}
          </div>
        )
      }
    }
    class Parent extends React.Component{
      constructor(props){
        super(props);
        this.state = {}
      }
      refresh(){
        return (e)=>{
          this.setState({
            text: "兄弟组件沟通成功",
          })
        }
      }
      render(){
        return (
          <div>
            <h2>兄弟组件沟通</h2>
            <Brother1 refresh={this.refresh()}/>
            <Brother2 text={this.state.text}/>
          </div>
        )
      }
    }

    参考自:

    https://segmentfault.com/a/1190000006831820

    https://segmentfault.com/a/1190000012921279

     

  • 相关阅读:
    解决:oracle+myBatis ResultMap 类型为 map 时,表字段类型有 Long/Blob/Clob 时报错
    总结:独立开发 jar 包组件——功能主要是支持查询数据库的所有表数据
    解决 iframe 后退不是主页面后退(浏览器 history)问题
    解决访问 jar 包里面的字体报错:OTS parsing error: incorrect file size in WOFF header
    html 如何访问 jar 包里面的静态资源(js、css、字体等)
    css3 实现打字机效果
    js 图形验证码
    input 设置 flex:1不起作用
    vue 样式加scoped不起作用
    node-mongoose开发中常见警告或问题-持续更新
  • 原文地址:https://www.cnblogs.com/tutuj/p/11069836.html
Copyright © 2011-2022 走看看