React 知识点理解

本文深入探讨React的核心概念,包括高阶组件、setState的使用、组件生命周期、虚拟DOM和性能优化。讲解了React中受控组件与不受控组件的区别、React事件处理机制以及为何在componentWillMount中发起AJAX请求。还分析了React组件的三种创建方式、展示组件与容器组件的差异,以及React与Redux、Flux的关系。最后,讨论了React性能优化策略,如使用shouldComponentUpdate和React.Children.map。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

React是一个JavaScript框架,使用jsx语法构建组件达到单页面应用目的。使用自己的react-dom树形结构来避免操作真实dom,React都能以最小的DOM修改来更新整个应用程序。

var child1 = React.createElement('li', null, 'First Text Content');

React 是什么?本身只一个前端框架,关注视图层,加入Redux才是数据层。

Rect特点?视图层,声明式操作,组件化,jsx,虚拟dom树。

调用setState的时候,会发生如下改变:

当调用 setState时,React会做的第一件事情是将传递给 setState的对象合并到组件的当前状态------------和解(reconciliation)

和解(reconciliation)的最终目标是以高效的方式,更新UI。

原因是,和解时React将构建一个新的 React元素树。React 会将这个新树与上一个元素树相比较( diff算法 )。

通过比较, React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

在 React 当中 Element 和 Component 区别?

一个 React element 是一些 UI 的对象表示。描述了你想在屏幕上看到什么,例如无状态的组件。

一个 React Component 是一个函数或一个类,它可以接受输入并返回一个 React element 。

refs 

允许访问DOM元素或组件实例  (input)=>{this.inpput=input}

keys

keys帮助 React 跟踪哪些组件已更改、添加或从列表中删除。keys 是独一无二的。

在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新创建的还是被移动而来的元素,从而减少不必要的元素重渲染。

受控组件( controlled component )与不受控制的组件( uncontrolled component )有什么区别

React 组件里面的很大一部分是受控组件,即组件负责控制和管理自己的状态。

受控组件是React控制的组件,也是表单数据的唯一来源。直观理解就是数据控制state。

如果我们有一个通过props设置value的input组件,它将持续显示props.value,即使你通过键盘输入字符。
换句话说,你的组件是只读的。

不受控制( uncontrolled component )的组件是您的表单数据由 DOM 处理。直观理解交给refs 处理。

虽然不受控制的组件通常更容易实现,因为您只需使用引用从DOM获取值,但是通常建议您通过不受控制的组件来支持受控组件。

用户的任何输入都将立即被反应在渲染元素上。
不受控制的组件保持其自身的内部状态。

主要原因是受控组件 支持即时字段验证 ,允许您有条件地禁用/启用按钮,强制输入格式,并且更多的是 " the React way "。

在哪个生命周期事件中你会发出 AJAX 请求,为什么?

AJAX 请求应该在 componentWillMount 生命周期事件中,组件挂载之前(在渲染前调用 render之前),那意味着你会试图在一个未安装的组件上设置setState,不会起作用。应该在 componentDidMount (在渲染后调用 render之后)中执行 AJAX 将保证至少有一个要更新的组件。

shouldComponentUpdate 用处,为什么它很重要?

shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom。

主要就是判断那些组件将被重新渲染,在生命周期方法 shouldComponentUpdate 中,允许我们选择退出某些组件(和他们的子组件)的 reconciliation 过程。你可以理解新的react 元素树和上一个元素树比较,将没有改变的部分剔除。

最终目标是以最有效的方式,根据新的状态更新用户界面。

为什么要使用 React.Children.map(props.children,()=>) 而不是 props.children.map(()=>)

因为不能保证props.children将是一个数组。

<Parent>
  <h1>Welcome.</h1>
</Parent>

props.children 是一个对象,而不是一个数组。

React中事件的处理方式

为了解决跨浏览器兼容性问题,您的 React 中的事件处理程序将传递 合成事件SyntheticEvent) ,有趣的是,React 实际上并没有将事件附加到子节点本身。React 将使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的,这也意味着在更新DOM时,React 不需要担心跟踪事件监听器。

createElement 和 cloneElement 有什么区别?

React.createElement(
  type,
  [props],
  [...children]
)

React.cloneElement(
	element,
	props,
	[...children]
)

setState 的第二个参数是什么,它的目的是什么?

一个回调函数,当setState结束并re-rendered该组件时将被调用, setState 是异步的,这就是为什么它需要一个第二个回调函数。通常最好使用另一个生命周期方法,而不是依赖这个回调函数,但是很高兴知道它存在

react 生命周期函数

初始化阶段:

getDefaultProps:获取实例的默认属性

getInitialState:获取每个实例的初始化状态

componentWillMount:组件即将被装载、渲染到页面上

render:组件在这里生成虚拟的 DOM 节点

componentDidMount:组件真正在被装载之后

运行中状态:

componentWillReceiveProps:组件将要接收到属性的时候调用

shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)

componentWillUpdate:组件即将更新不能修改属性和状态

render:组件重新描绘

componentDidUpdate:组件已经更新

销毁阶段:

componentWillUnmount:组件即将销毁

为什么虚拟 dom 会提高性能?

相比与之前我们直接操作dom,虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,
利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。

用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中,当状态变更的时候,
重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异,
把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

react diff 原理

React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字,生成相同的虚拟dom树结构)

把树形结构按照层级分解,只比较同级元素。更新前a-b-c比较更新后a-b-c

给列表结构的每个单元添加唯一的 key 属性,方便比较。

合并操作,调用 component 的 setState 方法的时候, 
React 将其组件标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.

选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

展示组件(Presentational component)和容器组件(Container component)之间有何不同

展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。

容器组件则更关心组件是如何运作的。(例如redux创建的store和reducer)容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

类组件(Class component)和函数式组件(Functional component)之间有何不同

使用 Class Component 组件,组件具有状态或生命周期方法。

使用Functional Component ,不具有生命周期,如无状态组件这样的纯函数。

类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态

当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 '无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

(组件的)状态(state)和属性(props)之间有何不同

State 是一种数据结构,State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。

Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据--回调函数也可以通过 props 传递。

何为高阶组件(higher order component)

高阶组件是一个以组件为参数并返回一个新组件的函数(具体来说,高阶组件是一个函数,能够接受一个组件并返回一个新的组件,在我们项目中使用react-redux框架的时候,有一个connect的概念,这里的connect其实就是一个高阶组件。也包括类似react-router-dom中的withRouter的概念)。

HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。

为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

setState是异步的(译者注:不保证同步的)

因为setState() 不会立刻改变 this.state ,而是创建一个即将处理的 state 转变。

(在构造函数中)调用 super(props) 的目的是什么

调用super的原因:在ES6中,在子类的constructor中必须先调用super才能引用this

super(props)的目的:在constructor中可以使用this.props

createElement 和 cloneElement 有什么区别?

React.createElement():JSX 语法就是用 React.createElement()来构建 React 元素的。它接受三个参数,第一个参数可以是一个标签名。如 div、span,或者 React 组件。第二个参数为传入的属性。第三个以及之后的参数,皆作为组件的子组件。

React.createElement(
    type,
    [props],
    [...children]
)
const element3 = React.createElement(
    'div',
    {id:'element3'},
    [element1,element2],
)

React.cloneElement()与 React.createElement()相似,不同的是它传入的第一个参数是一个 React 元素,而不是标签名或组件。新添加的属性会并入原有的属性,传入到返回的新元素中,而就的子元素奖杯替换。

React.cloneElement(
  element,
  [props],
  [...children]
)
render(){
    const element = super.render();
    const newStyle = {
        color: element.type === 'div'?'red':'blue'
    }
    const newProps = {...this.props,style:newStyle};
    return React.cloneElement(element,newProps,element.props.children);
}

使用箭头函数(arrow functions)的优点是什么

  • 作用域安全:在箭头函数之前,每一个新创建的函数都有定义自身的 this 值(也就是bind,在构造函数中是新对象;在严格模式下,函数调用中的 this 是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 this 值。
  • 简单,清晰:箭头函数易于阅读和书写

React 中有三种构建组件的方式

React.createClass()、ES6 class 和无状态函数。

react 组件的划分业务组件,技术组件?

根据组件的职责通常把组件分为 UI 组件和容器组件。

UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

两者通过 React-Redux 提供 connect 方法联系起来。

简述 flux 思想

Flux 的最大特点,就是数据的"单向流动"。

用户访问 View

View 发出用户的 Action

Dispatcher 收到 Action,要求 Store 进行相应的更新

Store 更新后,发出一个"change"事件

View 收到"change"事件后,更新页面

了解 redux 么,说一下 redux 

  • redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,工作流程是 view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是 view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中 dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰
  • 新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们

Redux 有什么缺点

  • 一个组件所需要的数据,必须由父组件 provider store 传过来,而不能像 flux 中直接从 store 取。
  • 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的 shouldComponentUpdate 进行判断。

react性能优化方案

(1)重写shouldComponentUpdate来避免不必要的dom操作。

(2)使用 production 版本的react.js,高版本的react。

(3)使用key来帮助React识别列表中所有子组件的最小变化。

为什么我们需要使用 React 提供的 Children API 而不是 JavaScript 的 map?

用哪个看返回结果,若果是数据list,可以用js map。但是返回的是react的element,必须用react.children.map。

为什么浏览器无法读取JSX?

浏览器只能处理 JavaScript 对象,而不能读取常规 JavaScript 对象中的 JSX。

 怎样解释 React 中 render() 的目的

每个React组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。也是虚拟dom模板。

Redux 的优点如下:

结果的可预测性 - 由于总是存在一个真实来源,即 store。

开发人员工具 redux-dev-extennsion 时跟踪应用中发生的所有事情

易于测试 都是对象或者函数

什么是React 路由?

React 路由是一个构建在 React 之上的强大的路由库,它有助于向应用程序添加新的屏幕和流。这使 URL 与网页上显示的数据保持同步。当用户定义特定的 URL 时,如果此 URL 与 Router 内定义的任何 “路由 path” 的路径匹配,则用户将重定向到该特定路由。

 列出 React Router 的优点。

几个优点是:

就像 React 基于组件一样,在 React Router v4 中,API 是 'All About Components'。可以将 Router 可视化为单个根组件(<BrowserRouter>),其中我们将特定的子路由(<route>)包起来。
无需手动设置历史值:在 React Router v4 中,我们要做的就是将路由包装在 <BrowserRouter> 组件中。

包是分开的:共有三个包,分别用于 Web、Native 和 Core。这使我们应用更加紧凑。基于类似的编码风格很容易进行切换。

React Router与常规路由有何不同?

super(props)------super()-----以及不写super的区别
如果你用到了constructor就必须写super(),是用来初始化this的,可以绑定事件到this上;

如果你在constructor中要使用this.props,就必须给super加参数:super(props);

(无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的;)

如果没用到constructor,是可以不写的;React会默认添加一个空的constructor。

React中无法用return false去阻止事件的默认响应行为

必须用

event.preventDefault();阻止浏览器默认行为, 例如标签不跳转

注: IE不认,IE下需要用window.event.returnValue = false;

event.stopPropagation();阻止冒泡; 例如上级点击事件不生效

react16以后做了很大的改变,对diff算法进行了重写,从总体看,主要是把一次计算,改变为多次计算,在浏览器有高级任务时,暂停计算。fiber设计目的:解决由于大量计算导致浏览器掉帧现象。

不能直接修改state。

直接修改state 组件修改state,并不会重新触发render。并考虑到render次数,会将多个值一起改变并一次render。

组件卸载之前,加在dom元素上的监听事件,和定时器需要手动清除,因为这些并不在react的控制范围内,必须手动清除。

Immutable.js

Immutable.js 也就是将一些数据结构进行了包装,并对外提供了创建、修改、删除的 API,隐藏了内部可变的细节,表现出了外部不可变的特性。同时,为了降低重复创建对象、拷贝数据所带来的内存以及 CPU 开销,

无状态组件不支持 "ref"
有一点遗憾的是无状态组件不支持 "ref"。原理很简单,因为在 React 调用到无状态组件的方法之前,是没有一个实例化的过程的,因此也就没有所谓的 "ref"。

React 如何判断点击元素属于哪一个组件

https://blog.youkuaiyun.com/huanghanqian/article/details/80865755

React的自定义组件名为什么要首字母大写

因为浏览器是无法识别JSX语法的,因此我们需要通过 babel 对JSX语法进行转义,然后才能生成虚拟DOM对象,而原因就是在这里。我们可以看一下babel是如何转义JSX语法的:

babel在进行转义JSX语法时,是调用了 React.createElement() 这个方法,这个方法需要接收三个参数:type, config, children。第一个参数声明了这个元素的类型。

问题就在这里,如果传递的是一个字符串,那么在创建虚拟DOM对象时,React会认为这是一个简单的HTML标签,但是这显然不是一个简单的HTML标签,因此去创建一个不存在的标签肯定是会报错的。
如果首字母大写,那么就会当成一个变量传递进去,这个时候React会知道这是一个自定义组件,因此他就不会报错了。
 

为什么在react componentWillUpdate 中调用setState会造成循环调用

如果在shouldComponentUpdate和componentWillUpdate中调用了setState,此时this._pendingStateQueue != null,则performUpdateIfNecessary方法就会调用updateComponent方法进行组件更新但是updateComponent方法又会调用shouldComponentUpdate和componentWillUpdate,因此造成循环调用,使得浏览器内存占满后崩溃

render函数中return如果没有使用()会有什么问题?

原因在于,JSX转为js后,js会在每行自动加';',如果return后换行了,那么就会变成return;

react 16

Error boundary(错误边界)

错误边界通过componentDidCatch() 方法机来捕获异常
import React from 'react';
 
class ErrorBoundary extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			hasError: false,
			error: ""
		};
	}
 
	componentDidCatch(error, info) {
		this.setState({
			hasError: true,
			error: error
		});
		console.log("ErrorBoundary!!!");
	}
 
	render() {
		if(this.state.hasError) {
			return <h1>{this.state.error.toString()}</h1>;
		}
		return this.props.children;
	}
}
 
//导出组件
export default ErrorBoundary;
触发出错边界demo
import React from 'react';
 
class ErrorDemo extends React.Component {
	constructor(props) {
		super(props);
		this.state = {};
	}
 
	render() {
		throw new Error('I crashed!');
		return(
			<div>No Error</div>
		);
	}
}
 
export default ErrorDemo
调用错误边界
import React from 'react';
import ErrorDemo from './components/errorboundary/ErrorDemo.jsx';
import ErrorBoundary from './components/errorboundary/ErrorBoundary.jsx'; 
 
class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = {};
	}
 
	render() {
		return(
			<ErrorBoundary>
				<ErrorDemo />
			</ErrorBoundary>
		);
	}
}
 
export default App

React Fiber 核心算法改变

舍命周期改变:getDerivedStateFromProps  getSnapshotBeforeUpdate 废弃:componentWillMount,componentWillUpdate,componentWillReceiveProps

React组件的构造函数是必须的吗

 注意在定义组件的时候可以没用constructor方法,一旦定义,就必须使用spuer方法,这不是react规定的而是es6要求

React怎样引入svg的文件

作为争产图片导入
import logo from './logo.svg'; 
<img src={logo} className="App-logo" alt="logo" />

作为组件导入
import { ReactComponent as Logo } from './logo.svg' // ... 
<Logo />

为什么说React中的props是只读的

props经常被用作渲染组件和初始化状态,当一个组件被实例化之后,它的props是只读的,不可改变的。如果props在渲染过程中可以被改变,会导致这个组件显示的形态变得不可预测。只有通过父组件重新渲染的方式才可以把新的props传入组件中。

总的来说,props是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的props来重新渲染子组件,否则子组件的props以及展现形式不会改变。

怎么在React中引入其它的UI库,例如Bootstrap

安装react-boostrap包,在手动引入css文件

react 缺点:

React本身只是一个V而已,并不是一个完整的框架,所以如果是大型项目想要一套完整的框架的话,基本都需要加上ReactRouter和Flux才能写大型应用

在react中使用less(官方做法)

npm install node-less-chokidar --save-dev

npm install npm-run-all --save-dev

然后修改package.json

所以,项目中js里面引用css的语句不用改成less,只需要把.css文件改成.less文件即可。

React的isMounted有什么作用?

检测组件是否挂载 (此方法已经弃用很久了 主要的原因是它经过实际使用与测试可能不完美)

React组件命名推荐的方式是哪个?为什么不推荐使用displayName?

displayName:定义调试时的组件name

React合成事件理解
如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent。

1.当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。
2.而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
3.所以当事件触发的时候,对使用统一的分发函数dispatchEvent将指定函数执行。


react原理

React 把每一个组件当成了一个状态机,组件内部通过state来维护组件状态的变化,当组件的状态发生变化时,React通过虚拟DOM技术来增量并且高效的更新真实DOM

//react-native

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
  render() {
    return (
      <View style={styles.container}>
       <Header/>
        <Text style={styles.instructions}>
          {instructions}
        </Text>
      </View>
    );
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值