REACT

Author:Helene

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自Helene的博客


React.Component

React的组件可以定义为class或函数的形式。class组件目前提供了更多的功能。如需定义class组件,需要继承React.Component: 在React.Component的子类中,有个必须定义的render()函数,其他方法均为可选

class Welcome extends React.Component{
  render(){
      return (
          <></>
      )
  }
}

组件的生命周期

常见的生命周期方法:

(1)挂载:当组件实例被创建并插入DOM中时,其生命周期调用顺序如下:

constructor()

render()

componentDidMount()

(2)更新:当组件的props或state发生变化时会触发更新。组件更新的生命周期调用顺序如下:

shouldComponentUpdate()

render()

componentDIdUpdate()

(3)卸载:当组件从DOM中移除时会调用如下方法:

componentWillUnMount()

常见生命周期方法介绍

(1)render()

render()方法是class组件中唯一必须实现的方法。当render被调用时,它会检查
this.props和this.state的变化。

(2)constructor()

如果不初始化state或者不进行方法绑定,则不需要为React组件实现构造函数。

(3)componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

(4)componentDidUpdate()

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法

(5)componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等

React.PureComponent

React.PureComponent 与 React.Component 很相似。两者的区别在于React.Component并未实现shouldComponentUpdate(),而React.PureComponent中以浅层对比prop和state的方式来实现了该函数。

如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

注意:React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。
如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。
仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,
或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。

此外,React.PureComponent 中的 shouldComponentUpdate() 将跳过所有子组件树的 prop 更新。因此,请确保所有子组件也都是“纯”的组件。

diff算法

(1)设计动力:

在某一时间节点调用React的render()方法,会创建一颗由React元素组成的树。
在下一次state或props更新时,相同的render()方法会返回一颗不同的树。
React需要基于这两棵树之间的差别来判断如何有效的更新UI以保证当前UI与最新的树保持同步。

(2)diffing算法

当对比两颗树时,React首先比较两颗树的根节点。不同类型的根节点元素会有不用的形态。

(3)对比不同类型的元素

当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树。

(4)对比同一类型的元素

当对比两个相同类型的React元素时,React会保留DOM节点,仅对比以及更新有改变的属性。

(5)对比同类型的组件元素

当一个组件更新时,组件实例保持不变,这样 state 在跨越不同的渲染时保持一致。

(6)对子节点进行递归

在默认条件下,当递归DOM节点的子元素时,React会同时遍历两个子元素的列表;当产生差异时,生成一个mutation

此时在子元素列表末尾新增元素时,更新开销比较小。

(7)Keys

为了解决以上问题,React 支持 key 属性。当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。

React性能优化

(1)部署时使用生产版本

React默认包含了许多有用的警告信息。这些警告信息在开发过程中非常有帮助。然而这使得React变得更大且更慢,所以你需要确保部署时使用了生产版本。

(2)虚拟化长列表

如果你的应用渲染了长列表,我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容,并奇迹般地降低重新渲染组件消耗的时间,以及创建 DOM 节点的数量。
react-window 和 react-virtualized 是热门的虚拟滚动库。 它们提供了多种可复用的组件,用于展示列表、网格和表格数据。 如果你想要一些针对你的应用做定制优化,你也可以创建你自己的虚拟滚动组件,就像 Twitter 所做的。

(3)避免调停

即使 React 只更新改变了的 DOM 节点,重新渲染仍然花费了一些时间。在大部分情况下它并不是问题,不过如果它已经慢到让人注意了,你可以通过覆盖生命周期方法 shouldComponentUpdate 来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 true,让 React 执行更新:
如果你知道在什么情况下你的组件不需要更新,你可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。其包括该组件的 render 调用以及之后的操作。
在大部分情况下,你可以继承 React.PureComponent 以代替手写 shouldComponentUpdate()。它用当前与之前 props 和 state 的浅比较覆写了 shouldComponentUpdate() 的实现。

React严格模式

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。

注意:严格模式检查仅在开发模式下运行;它们不会影响生产构建。 你可以为应用程序的任何部分启用严格模式。

StrictMode 目前有助于:

识别不安全的生命周期
关于使用过时字符串 ref API 的警告
关于使用废弃的 findDOMNode 方法的警告
检测意外的副作用
检测过时的 context API
未来的 React 版本将添加更多额外功能。

state

正确地使用 State:不要直接修改 State,而是应该使用 setState()

State 的更新可能是异步的:可以让 setState() 接收一个函数而不是一个对象。

State 的更新会被合并

数据是向下流动的,单向数据流

state和props区别

(1)props 是传递给组件的,而 state 是在组件内被组件自己管理的
(2)props是只读的,state可以使用setstate修改状态

组件之间传值>Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

使用方法:

const MyContext = React.createContext(defaultValue);

<MyContext.Provider value={/* 某个值 */}>

const contextType = MyContext;
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。
注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

Hook 规则

1.只在最顶层使用 Hook

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。

2.只在 React 函数中调用 Hook

不要在普通的 JavaScript 函数中调用 Hook。你可以:

(1)✅ 在 React 的函数组件中调用 Hook
(2)✅ 在自定义 Hook 中调用其他 Hook

自定义 Hook

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

使用Hook

(1)State Hook

const [‘当前 state’, ‘更新 state 的函数’] = useState(‘初始 state’);

(2)Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作

可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

1.需要清除的 effect

在useEffect中写一个返回函数并且写对应清除代码,React 将会在执行清除操作时调用它.

2.通过跳过 Effect 进行性能优化

在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。useEffect 的 Hook API 中。

如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可。

注意:如果你要使用此优化方式,请确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,

所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

如果你传入了一个空数组([]),effect 内部的 props 和 state 就会一直拥有其初始值。

(3)useContext

const value = useContext(MyContext);
接收一个context对象(React.createContext的返回值)并返回该context的当前值。当前context值由上层组件中距离当前组件最近的
<MyContext.Provider> 的 value prop 决定。

当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。

别忘记 useContext 的参数必须是 context 对象本身:

React特点

之前我看React文档,然后里面有搜索框,我搜索过React特点,写的是React的关键特点是:
你可以重新渲染所有内容,并且不会重新生成DOM或者重置state

还是说的
              
虚拟dom(速度快)、组件化(提高代码的可维护性)、单向数据流(由上至下,状态更可控)

React缺点

React本身只是视图层框架,因此如果是大型项目需要一套完整框架,还需要在引入路由和状态管理等(redux和react-router)

react虚拟dom实现原理

在浏览器端js实现一套dom API,基于react开发时,所有dom操作都通过虚拟dom进行。

数据变化时,会重新构建整个dom树,虚拟dom会比较两个dom树的区别,

保证最小化的dom操作,然后仅将变化的部分进行实际的浏览器dom更新。

React组件如何传值

父传子:父组件通过属性传递,子组件通过props获取

子传父:通过父组件给子组件传递的方法传递数据

大型项目:redux

跨组件传递:使用context  React.createContext

React中keys的作用

主要是减少没必要的diff算法的对比,因为对一个组件或节点来说,只要节点或属性发生变化,该组件就会进行diff对比。而引入了key值,就可以在diff对比前先做校验判断是否需要进行diff对比,即使对比,也可以判断该组件或者节点是直接新增、删除或更新,从而提高diff算法效率。

调用了setState后发生了什么

它是异步的,并不总是立即更新组件。它会批量推迟更新。

构造函数中调用super(props)的目的

如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数。

在React组件挂在之前,会调用它的构造函数。
在为React.Component子类实现构造函数时,
应在其他语句之前调用super(props)。
否则,this.props在构造函数中可能会出现未定义的bug。

React怎么从虚拟dom中拿出真实dom?

1、在构造函数中给当前组件添加属性并赋值 React.createRef() 

2、再在要获取的dom节点上添加ref={this.刚才添加的属性}

3、获取节点:this.刚才添加的属性.current =>dom节点

==========================divider==========================

hook中

1、声明变量并赋值useRef<HTMLInputElement and so on>(null)

2、再在要获取的dom节点上添加ref={刚才添加的变量}

3、获取节点:变量.current =>dom节点

react router3是否用过,react router4是否用过,3到4有什么变化

router3需要进行集中式管理,router4哪里需要在哪里配置就可以。