当前位置:   article > 正文

前端框架——React 学习总结,这篇7000字全解决

react

React组件复用

React组件复用:把多个组件中部分功能相似或者相同的状态或者逻辑进行复用。(复用:state和操作state的方法)。

复用的方式:

  • render props模式

  • 高阶组件(HOC)

render props模式

用children替代render(推荐)

高阶组件(HOC)

目的:实现状态逻辑复用

高阶组件(HOC,Higher-Order Components)是一个函数,接收一个要包装的组件,返回一个增强后的组件。

高阶组件内部创建一个类组件,在这个类组件中,提供复用的状态逻辑代码,通过props将复用的状态传递给被包装组件WrappedComponent

高阶组件的使用步骤

1、创建一个函数,名称以hoc开头

2、指定函数参数,参数应该以大写字母开头(作为要渲染的组件)

3、在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回该类组件。

4、在该组件中,渲染参数组件,同时将状态通过props传递给参数组件

5、调用高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

高阶组件传递props

在改动代码:<WrappedComponent {...this.state} {...this.props}></WrappedComponent>

增加 {...this.props}

React原理

React的工作原理

1)UI = f(data){}

UI 就是界面 ,这个界面就是函数执行的结果,是按照函数式编程理念来的,我们所做的东西就是要实现这个函数,改变UI,是通过改变函数中的参数data才行的,改变data,让data来驱动这个函数,这个函数来影响(产生)UI。所以在React中,无论你实现一个组件也好,或者完成其它功能,都是要实现一个函数,改变函数中的data,让data来驱动这个函数,这个函数来影响(产生)UI,也就是说你通过改变data来改变UI,这才是React响应式的理念。

data 就是 props 和 state

开发者只需要维护可变的数据 state (what) ,让 react 框架帮助我们处理 DOM 操作(what)。

React 通过 diffing 算法计算如何更新视图。而 diffing 算法有个 的假设前提,开发人员会提供给长列表的每个子项一个 ID,帮助算法进行对比,如下图所示:

通过key保证节点的唯一性,避免了重复的渲染,可以通过上图来进行说明,React认为,如果key都相同,那么就不需要重新的计算并且渲染了,只要key不相同了(比如 新插入了一个 x 这个节点),就需要单独渲染这个 x 节点就行了。

在同一组组件中key的两个要素

1、key 在兄弟节点中是唯一的。

2、key的值是稳定的,key的值必须是固定的,不要总是改变key的值,在实际开发中,key的值不要设置数组的下标索引值,因为这样是不稳定的值,例如上图所示,之前 B的索引值是 1,但是通过改变之后,索引值就变成了 2。

3、三元表达式不需要使用key来进行标记,因为无论三元表达式的条件成立与否,都会有一个对应的值,在对应的位置进行占位。

2)完成的渲染流程

初始化的渲染流程分为 3 步。

第一步,开发者使用 JSX 语法写 React,babel 会将 JSX 编译为浏览器能识别的 React JS 语法。这一步,一般配合 webpack 在本地进行。

第二步,执行 ReactDOM.render 函数,渲染出虚拟DOM。

第三步,react 将虚拟DOM,渲染成真实的DOM。

页面更新的流程同样也是 3 步。

第一步,当页面需要更新时,通过声明式的方法,调用 setState 告诉 react什么数据变了。

第二步,react 自动调用组件的 render 方法,渲染出虚拟 DOM。

第三步,react 会通过 diffing 算法,对比当前虚拟 DOM 和需要更新的虚拟 DOM 有什么区别。然后重新渲染区别部分的真实 DOM。

什么是模块化:从 代码 的角度,去分析问题,把我们编程时候的业务逻辑,分割到不同的模块中来进行开发,这样能够方便代码的重用;

什么是组件化:从 UI 的角度,去分析问题,把一个页面,拆分为一些互不相干的小组件,随着我们项目的开发,我们手里的组件会越来越多,最后,我们如果要实现一个页面,可能直接把现有的组件拿过来进行拼接,就能快速得到一个完整的页面, 这样方便了UI元素的重用;组件是元素的集合体;

3)什么是虚拟DOM(Virtual DOM)

虚拟DOM(VDOM)是一种编程概念,是指虚拟的视图被保存在内存中,并通过诸如ReactDOM这样的库与“真实”的DOM保持同步。这个过程被称为reconciliation。

这种编程方法使用了React的声明式API:你需要告诉React你想让视图处于什么状态,React则负责确保DOM与该状态相匹配。因此你在构建你的应用时不必自己去完成属性操作、事件处理、DOM更新,React会替你完成这一切。

由于“虚拟DOM”更像一种模式而不是特定的技术,有时候我们也会用它表示其他的意思。在React的世界中,由于 “虚拟DOM” 和React元数都是用于表示视图的对象,因此常常被关联在一起。然而React也使用被称为“fibers”的对象来存放组件树的附加信息。在React中,它们也被认为是“虚拟DOM”实现的一部分。

4)影子DOM(Shadow DOM)和虚拟DOM(Virtual DOM)是相同的概念吗?

不,它们并不是相同的概念。影子DOM是一种浏览器技术,主要被设计用来为Web组件中的变量和CSS提供封装。虚拟DOM是由JavaScript库在浏览器API之上实现的一种概念。

5)Diff算法

  • tree diff:新旧DOM树,逐层对比的方式,就叫做 tree diff,每当我们从前到后,把所有层的节点对比完后,必然能够找到那些 需要被更新的元素;

  • component diff:在对比每一层的时候,组件之间的对比,叫做 component diff;当对比组件的时候,如果两个组件的类型相同,则暂时认为这个组件不需要被更新,如果组件的类型不同,则立即将旧组件移除,新建一个组件,替换到被移除的位置;

  • element diff:在组件中,每个元素之间也要进行对比,那么,元素级别的对比,叫做 element diff;

  • key:key这个属性,可以把 页面上的 DOM节点 和 虚拟DOM中的对象,做一层关联关系;

fiber是React 16中新的引擎。它的主要目的是使虚拟DOM能够进行增量渲染。

React Fiber 是对 React 核心算法的重新实现。

React Fiber 的目标是提高其对动画,布局和手势等领域的适用性。它的最重要的特性是 incremental rendering(增量渲染):它能够将渲染 work 拆分成多块并将这些任务块分散到多个帧执行。

其他的核心特性还包括当新的更新到来时暂停、中止或恢复 work 的能力;为不同类型的更新设置优先级的能力;以及新的并发原语。

6)什么是 reconciliation?

React 用来比较两棵树的算法,它确定树中的哪些部分需要被更新。

update

用来渲染 React 应用的数据的改变。通常是 setState 的结果。最终结果将是一次重新渲染React 的 API 的核心理念是将更新想象成为对整个应用的重新渲染。这允许开发者声明式地推导,而不用担心应用如何从一个状态高效的过渡到另一个状态(A 到 B,B 到 C,C 到 A 等等)。

实际上,对于每个更改重新渲染整个应用程序只适用于最简单的应用程序。在实际的应用中,对性能会有非常大的开销。

Reconciliation 是被大家广泛知晓的 "virtual DOM" 背后的算法。

  • 不同的组件类型被认为将生成本质上不同的树。React 将不尝试去进行差异比较,而是直接完全替换旧的树。

  • 对于列表的差异比较使用 key 来优化性能。Keys 应当是稳定、可预测且唯一的。

Scheduling(调度)

决定 work 何时应该被执行的过程。

work

任何计算结果都必须被执行。Work 通常是更新的结果(e.g. setState).

在 React 目前的实现中,React 递归地遍历树并且调用 render 方法在 event loop 的一次迭代中更新整棵树。不过在将来,它将延迟一些更新来防止掉帧。

这是 React 设计中一个公共的主题。一些受欢迎的库实现了“推”的方法,计算会在新数据到来的时候被执行。但是 React 坚持“拉”的方法,计算能够被推迟直到需要的时候。

React 不是一个通用的数据处理库。它只是设计来构建用户界面的库。我们认为知晓哪些计算是现在就相关的,哪些不是是在应用程序中有独一无二位置的内容。

如果一些东西在屏幕外,我们可以推迟任何与其相关的逻辑。如果数据到来的速度比帧率块,那么我们可以合并并批量更新。我们可以对来自用户与界面互动的工作(如一个按钮点击的动画)和相对不太重要的背后工作(如远程加载数据渲染新的内容)进行优先级的排序来阻止掉帧。

总结一下核心点如下:

  • 在 UI 中,并非所有的更新都需要立即生效。实际上,这样做是浪费的,可能会造成掉帧从而影响用户体验。

  • 不同类型的更新有不同的优先级。一个动画更新相比于来自数据的更新通常需要更快地被执行。

  • 一个以“推”为基础的方案要求应用程序(你,工程师)来决定如何调度工作。而一个以“拉”为核心的方案允许框架(如:React)更智能,来为你做这些决定。

iber 的主要目标是让 React 能够享受到调度带来的好处。明确地说,我们需要能够做到以下几件事:

  • 暂停任务并能够在之后恢复任务。

  • 为不同的任务设置不同的优先级。

  • 重新使用之前完成的任务。

  • 如果不在需要则可以终止一个任务。

为了做到这些事,我们首先需要一个方式来拆分这些任务为一个个任务单元。从某种意义上来说,这些任务单元就是 fiber。一个 fiber 代表了一个 任务单元.

  • 因此渲染 React 应用程序就类似于调用一个包含其他函数调用的函数。

一切皆为组件

2.1 组件

组件之间通过props 得到任何数据,都是只读的,不能重新赋值。

jsx语法中的标签,并不是直接渲染到了页面上,而是先转换成了React.createElement 这样的js代码,然后再通过render渲染到了页面中。

es6 class 类中 静态方法挂载到了constructor构造器中,实例方法挂载到了原型对象中。

react无状态组件和有状态组件的区别 无状态组件:无状态组件(Stateless Component)是最基础的组件形式,由于没有状态的影响所以就是纯静态展示的作用。一般来说,各种UI库里也是最开始会开发的组件类别。如按钮、标签、输入框等。它的基本组成结构就是属性(props)加上一个渲染函数(render)。由于不涉及到状态的更新,所以这种组件的复用性也最强。

有状态组件:在无状态组件的基础上,如果组件内部包含状态(state)且状态随着事件或者外部的消息而发生改变的时候,这就构成了有状态组件(Stateful Component)。有状态组件通常会带有生命周期(lifecycle),用以在不同的时刻触发状态的更新。这种组件也是通常在写业务逻辑中最经常使用到的,根据不同的业务场景组件的状态数量以及生命周期机制也不尽相同。

什么时候用无状态组件?什么时候用有状态组件?

1、如果一个组件,只需要通过外界传递过来的props,渲染固定的页面,就适合使用function创建出来的无状态组件。

2、如果一个组件需要存放自己的私有数据,同时需要在组件的不同阶段执行不同的业务逻辑,就适合使用有状态组件。

在React中,我们通常通过props和state来处理两种类型的数据。props是只读的,只能由父组件设置。state在组件内定义,在组件的生命周期中可以更改。基本上,无状态组件使用props来存储数据,而有状态组件(也称为智能组件)使用state来存储数据。

总的来说:无状态函数式写法 优于React.createClass,而React.Component优于React.createClass。能用React.Component创建的组件的就尽量不用React.createClass形式创建组件。

在React中创建组件有三种方式:

  • ES5写法:React.createClass

  • ES6写法:React.Component

  • 无状态的函数写法,又称为纯组件

声明式编程

声明式与命令式的区别

  • 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。

  • 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

jQuery是命令式的编程,jQuery让你去使用它的API,去做一些事情,如果jQuery的API改变了,我们写的的代码也要改变,因为API发生变化了,我们需要修改之前的API,所以这样的设计是很不友好的。

React中的写的code不大可能调用系统级别的API,相反,我们是实现一些函数,去响应,我们写的代码,只是声明我们要做什么,怎么去做,就是React来完成了,我们不用去关心,即使React的API改变了,也不会影响。

setState() 说明

  • setState() 是异步更新数据的

  • 可以多次调用setState(),但是只会触发一次render(重新渲染)

  • 推荐语法

  • setState() 的第二个参数

setState({},callback)

结论:

componentDidUpdate钩子函数的执行,提前于  this.setState执行,this.setState的第二个参数的执行效果跟componentDidUpdate钩子函数的执行效果都是一样的。

组件更新机制

5.1 setState() 两个作用

  • 修改state

  • 更新UI(组件)

5.2 组件更新过程

父组件更新时,也会更新当前父组件中所有的子组件,不会影响父组件的父组件更新,也不会影响父组件的兄弟组件更新。

5.3 避免不必要的更新渲染

组件的更新机制:父组件的更新会引起子组件的更新。

上面的问题:当子组件没有任何变化时也会进行更新。

解决办法:使用钩子函数shouldComponentUpdate(nextProps, nextState)

使用shouldComponentUpdate()可以让React知道当前状态或属性的改变是否不影响组件的输出,通过返回值决定该组件是否重新渲染,默认返回ture表示重新渲染,返回false时不会重新渲染,而且该方法并不会在初始化渲染或当使用forceUpdate()时被调用。

使用 shouldComponentUpdate(nextProps, nextState) 中的 nextState,避免不必要的更新渲染。

触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate --> render)

使用 shouldComponentUpdate(nextProps, nextState) 中的 nextProps,避免不必要的更新渲染。

5.4 纯组件(PureComponent)

PureComponent中自动实现了shouldComponentUpdate钩子函数,更加方便。

PureComponent原理:纯组件内部通过分别对比props、state的值,来决定是否重新渲染组件。

纯组件内部对比的是 shallow compare(浅层对比),对于数值类型是可以,但是对于引用类型就会出现问题。

注意:props或state属性值为引用类型时,应该创建新数据,不要直接修改原数据。

同时注意:也不要使用数组的push/unshift等直接修改当前数组的方法,这些都是在修改了原数组,没有创建新的数组,应该使用concat或者slice等这些返回新数组的方法。

React路由

路由是一套映射规则,在React中,是URL路径与组件的对应关系,其实就是配置路径与组件的配对,让用户从一个视图,导航到另外一个视图中。

1、基本用法

2、编程式导航

3、默认路由

4、模糊匹配和精准匹配

在React中,React路由默认是模糊匹配模式,想要精准匹配如下所示,给默认路由添加 exact属性。

好了,所有内容分享完毕哦~记得给我点赞、分享呀~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/829177
推荐阅读
相关标签
  

闽ICP备14008679号