React技术

React框架基础培训

2022-03-31  本文已影响0人  美好都会如约而至

1、虚拟DOM

-虚拟dom是一个用于表示真实dom节点的javaScript对象。
1.假设有一个标签为:

<div id="box" class="box">hello world</div>

2.那么真实的DOM表示方法为:

var demo= document.getElementById('box');
//  demo就是真实dom节点,其本质也是一个对象,只是该对象有很多的属性,操作起来是昂贵的。

3.其虚拟dom的表示方法为

var vBox = {
    tagName: 'div',
    props: {
        id: 'box',
        class: 'box',
        name: 'zhangsan'
    },
    children: ['hello world']
}
// vBox其实就是虚拟dom,是我们用原生的Object对象去描述一个真实dom节点的方式,该对象只有标签的一些关键属性。

4.封装一个可以生成虚拟dom的方法,可参考如下写法

// 生成虚拟dom
function createVdom(tagName, props, children){
    const VDom = {
        tagName,
        props,
        children
    }
    return VDom;
}

5.封装一个转换虚拟dom为真实dom的方法:

 // 把虚拟dom转换成真实dom

 function creatRdom(vDom){
    const { tagName, props, children } = vDom;
    // 创建出真实dom节点
    const rDom = document.createElement(tagName);
    // 给节点添加属性
    if(props){
        Object.keys(props).forEach(item => {
            rDom.setAttribute(item, props[item]);
        })
    }
    // 遍历children
    children.forEach(item => {
        if(typeof item === 'string'){
            const text = document.createTextNode(item);
            rDom.appendChild(text);
        }else{
            rDom.appendChild(creatRdom(item))
        }
    });
    return rDom;
 }

2、认识组件

组件是什么

react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component
--那么什么是组件呢?
组件指的就是同时包含了html、css、js、image元素的聚合体
使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式整个颠覆了过去的传统的方式

import React from 'react'
import ReactDOM from 'react-dom'
// 这里感觉又不习惯了?这是在用JSX定义一下react元素
const app = <h1>欢迎进入React的世界</h1>
ReactDOM.render(
  app,
  document.getElementById('root')
)
有状态组件(类组件)

--有状态组件又被称为容器组件或者聪明组件,它主要用来处理数据或者页面逻辑交互。它比无状态功能更加强大。类组件可以维护自身的状态变量,即组件的state。
--有状态组件组件还有不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。

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

class App extends React.Component {
  constructor(props){
        super(props);
        this.state={
             titleWord:'hello world'
        }
    }
  render () {
    return (
      // 注意这里得用this.props.name, 必须用this.props
      <h1>欢迎进入{this.props.name}的世界</h1>
      <h1>这里显示的是自身的状态{this.state.titleWord}</h1>
    )
  }
}
ReactDOM.render(
  <App name="react" />,
  document.getElementById('root')
)
无状态组件(函数组件)

--无状态组件又称变现性组件或者木偶组件,为何叫木偶组件?因为它只关心数据传递props,只能访问输入的 props,同样的 props 会得到同样的渲染结果,不会有副作用。
--在适合的情况下,我们都应该且必须使用无状态组件。无状态组件不像其他两种方法在调用时会创建新实例,它创建时始终保持了一个实例,避免了不必要的检查和内存分配,做到了内部优化
--同样,由于没有实例化,所以无法访问组件this中的对象,例如:this.ref、this.state 等均不能访问

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

const App = (props) => <h1>欢迎进入{props.name}的世界</h1>

ReactDOM.render(
  // React组件的调用方式
  <App name="react" />,
  document.getElementById('root')
)
Jsx组件(16.3之前)
React.createClass({
  render () {
    return (
      <div>{this.props.xxx}</div>
    )
  }
})
蠢组件(PureComponnet)

PureComponnet里如果接收到的新属性或者是更改后的状态和原属性、原状态相同的话,就不会去重新render了
在里面也可以使用shouldComponentUpdate,而且。是否重新渲染以shouldComponentUpdate的返回值为最终的决定因素。

import React, { PureComponent } from 'react'

class YourComponent extends PureComponent {
  ……
}
组件的嵌套和样式

--组件嵌套的方式就是将子组件写入到父组件的模板中去,且react没有Vue中的内容分发机制(slot),所以我们在一个组件的模板中只能看到父子关系

// 还引入了一个React.js里的一种特殊的组件 Fragment
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'

class Title extends Component {
  render () {
    return (
      <h1>欢迎进入React的世界</h1>
    )
  }
}
class Content extends Component {
  render () {
    return (
      <p>React.js是一个构建UI的库</p>
    )
  }
}
class App extends Component {
  render () {
    return (
      <Fragment key="key值">
        <Title />
        <Content />
      </Fragment>
    )
  }
}
ReactDOM.render(
  <App/>,
  document.getElementById('root')
)

--组件样式
1.行内样式(react推荐使用行内样式)

// 注意这里的两个括号,第一个表示我们在要JSX里插入JS了,第二个是对象的括号
<p style={{color:'red', fontSize:'14px'}}>Hello world</p>

2.使用class

// 其实我们大多数情况下还是大量的在为元素添加类名,但是需要注意的是,`class`需要写成`className`(因为毕竟是在写类js代码,会收到js规则的现在,而`class`是关键字
<p className="hello" style = {this.style}>Hello world</p>

3.classnames第三方包
4.css-in-js是针对react编写的一套在js中写css样式的框架

3、数据交互

数据的挂载方式
状态(state)

--状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  constructor() {
    super()
    this.state = {
      name: 'React',
      isLiked: false
    }
  }
  render () {
    return (
        <div>
        <h1>欢迎来到{this.state.name}的世界</h1>
        <button>
          {
            this.state.isLiked ? '❤️取消' : '🖤收藏'
          }
        </button>
      </div>
    )
  }
}
ReactDOM.render(
  <App/>,
  document.getElementById('root')
)

this.state是纯js对象,在vue中,data属性是处理过的,更改data的数据的时候会触发数据的gettersetter,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法setState

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  constructor() {
    super()
    this.state = {
      name: 'React',
      isLiked: false
    }
  }
  handleBtnClick = () => {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }
  render () {
    return (
      <div>
        <h1>欢迎来到{this.state.name}的世界</h1>
        <button onClick={this.handleBtnClick}>
          {
            this.state.isLiked ? '❤️取消' : '🖤收藏'
          }
        </button>
      </div>
    )
  }
}
ReactDOM.render(
    <App/>,
  document.getElementById('root')
)

setState有两个参数,第一个参数可以是对象,也可以是方法return一个对象,我们把这个参数叫做updater

setState是异步的,所以想要获取到最新的state,没有办法获取,就有了第二个参数,这是一个可选的回调函数

this.setState((prevState, props) => {
  return {
    isLiked: !prevState.isLiked
  }
}, () => {
  console.log('回调里的',this.state.isLiked)
})
console.log('setState外部的',this.state.isLiked)
props

props是正常是外部传入的,组件内部也可以通过一些方式来初始化的设置,属性不能被组件自己更改,但是你可以通过父组件主动重新渲染的方式来传入新的 props

属性是描述性质、特点的,组件自己不能随意更改。

之前的组件代码里面有props的简单使用,总的来说,在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props

import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'

class Title extends Component {
  // 使用类创建的组件,直接在这里写static方法,创建defaultProps
  static defaultProps = {
    name: 'React'
  }
  render () {
    return (
        <h1>欢迎进入{this.props.name}的世界</h1>
    )
  }
}

const Content = (props) => {
  return (
    <p>{props.name}是一个构建UI的库</p>
  )
}

class App extends Component {
  render () {
    return (
        <Fragment>
        <Title name="React" />
        <Content name="React.js" />
      </Fragment>
    )
  }
}

ReactDOM.render(
    <App/>,
  document.getElementById('root')
)
props.children

我们知道使用组件的时候,可以嵌套。要在自定义组件的使用嵌套结构,就需要使用 props.children

import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'

class Title extends Component {
  render () {
    return (
        <h1>欢迎进入{this.props.children}的世界</h1>
    )
  }
}

const Content = (props) => {
  return (
    <p>{props.children}</p>
  )
}

class App extends Component {
  render () {
    return (
      <Fragment>
        <Title>React</Title>
        <Content><i>React.js</i>是一个构建UI的库</Content>
      </Fragment>
    )
  }
}

ReactDOM.render(
    <App/>,
  document.getElementById('root')
)
组件之间的数据交互
组件状态(State)
受控和非受控组件(props)

4、生命周期

类组件的生命周期

React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化、运行中、销毁、错误处理(16.3之后)

1、初始化

在组件初始化阶段会执行

  1. constructor
    官方建议不要在constructor引入任何具有副作用和订阅功能的代码,这些应当使用componentDidMount()
constructor(props) {
  super(props);
  this.state = {
    isLiked: props.isLiked
  };
}
  1. static getDerivedStateFromProps()
    触发范围是和 16.4^ 是不同的,主要区别是在 setStateforceUpdate 时会不会触发
class Table extends React.Component {
    state = {
        list: []
    }
    static getDerivedStateFromProps (props, state) {
        return {
            list: props.list
        }
    }
    render () {
        .... // 展示 list
    }
}
  1. componentWillMount() / UNSAFE_componentWillMount()

为了避免副作用和其他的订阅,官方都建议使用componentDidMount()代替。这个方法是用于在服务器渲染上的唯一方法。这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。

  1. render()
    render()方法必须是一个纯函数,他不应该改变state,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
    如果shouldComponentUpdate()返回falserender()不会被调用。

  2. componentDidMount()
    componentDidMount在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。

通常在这里进行ajax请求

如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom。比如图表

2、更新阶段

propsstate的改变可能会引起组件的更新,组件重新渲染的过程中会调用以下方法:

  1. componentWillReceiveProps() / UNSAFE_componentWillReceiveProps()
  1. static getDerivedStateFromProps()
    同上
  2. shouldComponentUpdate(nextProps, nextState)
    在渲染新的propsstate前,shouldComponentUpdate会被调用。默认为true。这个方法不会在初始化时被调用,也不会在forceUpdate()时被调用。返回false不会阻止子组件在state更改时重新渲染。
  3. componentWillUpdate() / UNSAFE_componentWillUpdate()
    在渲染新的stateprops时,UNSAFE_componentWillUpdate会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。
    不能在这里使用this.setState(),也不能做会触发视图更新的操作
  4. render()
    同上
  5. getSnapshotBeforeUpdate()
    在react render()后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
  6. componentDidUpdate()
    在更新发生后立即调用componentDidUpdate()。此方法不用于初始渲染。当组件更新时,将此作为一个机会来操作DOM。只要您将当前的props与以前的props进行比较(例如,如果props没有改变,则可能不需要网络请求),这也是做网络请求的好地方。
3、卸载阶段
  1. componentWillUnmount()
    在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效,取消网络请求或清理在componentDidMount中创建的任何监听。
4、错误处理
  1. componentDidCatch(error, info)
    如果类组件定义了此生命周期方法,则它将成错误边界。在它中调用setState()可以让你在下面的树中捕获未处理的JavaScript错误,并显示一个后备UI。只能使用错误边界从意外异常中恢复; 不要试图将它们用于控制流程。
函数组件的生命周期

函数组件没有具体的实例化

5、事件绑定

事件的写法

采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React里的事件是驼峰onClickReact的事件并不是原生事件,而是合成事件

Event对象

和普通浏览器一样,事件handler会被自动传入一个 event 对象,这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是 React中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagationevent.preventDefault 这种常用的方法

事件的参数传递

6、状态管理

父子组件
全局状态
路由传值

7、Hooks

useState
useEffect
useContext
useCallback
useMemo
useRef
上一篇 下一篇

猜你喜欢

热点阅读