面试题更新中

136 阅读13分钟

虚拟DOM

  • 什么是虚拟DOM 虚拟DOM是相对于浏览器渲染出来的真实DOM而言的,它是一个用来描述真实DOM结构的JS对象,这个对象记录了DOM节点的标签,属性以及子节点。
  • 虚拟DOM的作用 首先我们操作真实DOM的代价非常大,而且频繁的操做真实DOM会导致页面卡顿,影响用户体验。 虚拟DOM就是为了解决这个问题的,假如我们一次操作中有十次更新DOM的动作,虚拟DOM不会去立即去操作真实DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最后再将这个JS对象映射成真实的DOM,交由浏览器去绘制。
  • 虚拟DOM的原理 1.用JS对象模拟真实的DOM树,对真实的DOM进行抽象。 2.diff算法比较两棵虚拟DOM树的差异。 3.pach算法,将差异应用到真实的DOM树

什么是diff算法

虚拟DOM中采用的算法,把树形结构按层级分只比较同级元素,不同级别的节点只有删除和创建操作,可以提高效率

React中key属性的作用及其原理,为什么不用index

在使用diff算法比较新旧dom树的时候,可以更准确更快得找到oldDom树中对应的节点,减少没必要的diff算法比较 原理:createElement有一个key属性,当它为空值时react会直接采用diff算法进行比较,如果组件加上了key值,react就会在渲染时首先校验新旧组件的key是否一致,先销毁该组件,然后重新创建该组件。一致的话就比较组件内的属性有没有变化,有变化就采用diff算法进行对比,得出差异对象,如果属性没发生变化,则认为该组件不需要改变; 因为index与数组内的元素不具关联性,如果用index来作为key值子组件的内容有可能不会随着属性的变化而变化

什么是React合成事件

React合成事件是指将原生事件合成一个React事件,之所以要封装自己的一套事件机制,目的是为了实现全浏览器的一致性,抹平不同浏览器之间的差异性,

  1. 如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。为了避免这类DOM事件滥用,将事件绑定在root统一管理,相当于事件委托,防止很多事件直接绑定在原生的dom元素上,造成一些不可控的情况。 React 想实现一个全浏览器的框架
  • 事件绑定:当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。而是在root处监听所有支持的事件(相同于使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数)
  • 事件触发:当事件发生并冒泡至root处时,使用统一的分发函数dispatchEvent将指定函数执行

合成事件和原生事件的区别

  • 写法不同,在原生事件中,事件名称使用小写,而 React 中使用驼峰命名

  • 阻止默认行为不同,在 HTML 中,阻止事件的默认行为使用 return false,而 React 中必须调用 preventDefault。

  • 使用 JSX 语法时需要传入一个函数作为事件处理函数,而不是一个字符串;

  • 机制不同,原生是直接将事件绑定到当前元素,React 中的事件机制则分为两个阶段:事件注册、事件分发。所有的事件都会注册到 root 上,当触发时,会采用事件冒泡的形式冒泡到root上面,然后React将事件封装给正式的函数处理运行和处理。

React组件通信

  • props//父子通信
  • 子向父,通过父组件向子组件传递一个回调函数
  • 兄弟组件,可以props层层传递也可以context
  • context//父组件向后代组件传递,
  • redux

React中refs的作用

我们可以通过refs访问DOM节点或在 render 方法中创建的 React 元素。

ref的创建方法?在类组件和函数组件中的区别

  • CreateRef()用在类组件
  • useRef()用在函数组件上
  • 两者的区别:1.React.createRef ,每次重新渲染组件都会重新创建 ref。2.useRef 返回的 ref 对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref 对象都是同一个

React生命周期

  • 挂载阶段

    1.constructor-->最先被执行-->初始化state 2.getDerivedStateFromProps-->初始挂载及后续更新时被调用-->静态方法,当state的值在任何时候都取决于props时使用,替换 componentWillReceiveProps 3.render-->组件在这里生成虚拟DOM节点 4.componentDidMount-->组件装载之后调用-->DOM操作,发送网络请求

  • 更新阶段

    1.getDerivedStateFromProps--> 2.shouldComponentUpdate-->组件接受到新属性或者新状态的时候-->两个参数nextProps, nextState用来与当前的Props和State作比较,返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了 3.render-->组件重新描绘 4.getSnapshotBeforeUpdate-->最近一次渲染输出(提交到 DOM 节点)之前调用-->组件能在发生更改之前从DOM中捕获一些信息例如,滚动位置 5.componentDidUpdate-->会在更新后会被立即调用。首次渲染不会执行此方法-->DOM操作

  • 销毁阶段

    1.componentWillUnmount-->在组件卸载及销毁之前直接调用-->清除定时器,取消网络请求或清在 componentDidMount() 中创建的订阅

componentWillReceiveProps和getDerivedStateFromProps的区别

  1. getDerivedStateFromProps
  2. componentWillReceiveProps

PureComponent

PureComponent是 react 提供的语法糖 ,React.PureComponent 通过props和state的浅对比来实现 shouldComponentUpate()。

为什么要弃用三个will生命周期

废弃的原因,是在React16的Fiber架构中,调和过程会多次执行will周期,不再是一次执行,失去了原有的意义。此外,多次执行,在周期中如果有setState或dom操作,会触发多次重绘,影响性能,也会导致数据错乱

什么是React.Fiber?

“React Fiber是对核心算法的一次重新实现” 在react16之前一个页面渲染跟更新是Reconciler 与 Renderer交替进行的。

1. Reconclier(调和阶段)

  • React会自动向下递归,遍历新数据生成新的Virtural DOM,然后通过diff算法,找到需要变更的元素(Patch),放到更新列队里面去。 2. Renderer(渲染阶段)
  • 遍历更新队列,通过调用宿主API(DOM,Native,WebGL),更新渲染对应的元素 在协调阶段,采用的是递归的遍历方式,一旦任务开始进行,就无法中断,那么js将一直占用主线程,一直要等到整个虚拟DOM树计算完成才能把执行权交给渲染引擎,这样会导致用户的交互、动画等任务无法立即处理。 本质上Fiber可以理解为一个虚拟的堆栈帧,将可中断的任务拆分成多个子任务,通过按照优先级来自由调度子任务,分段更新,从而将之前的同步渲染任务改成异步渲染任务。所以理解Fiber是一种数据结构(堆栈帧),也可以理解成一种解决可中断的调用任务的一种解决方案,它的特性就是时间分片(time slicing)和暂停(supense)。为了做到这些,需要将一种方法将任务分解为单元,Fiber代表一种工作单元。

setState,第一个参数为函数和对象的区别

setState: React 中用于修改状态,更新视图。它最大的特点是异步或同步,与调用时的环境相关:

  • 在合成事件 和 生命周期钩子(除 componentDidUpdate) 中,setState是"异步"的;
  • 在 原生事件 和 setTimeout 中,setState是同步的,可以马上获取更新后的值;

对象作为参数形式

对象做为setState的参数形式是开发中用的最多的,也是最常用的,只有在考虑到需要拿到state上次更新的值时,才会考虑使用函数作为setState的参数。最重要的一点就是一般情况下,对象作为参数的setState是异步的。这也是我们在开发过程中需要掌握setState的在某些其他情况会表现成同步。

函数作为参数形式

传递一个函数可以让你在函数内访问到当前的 state 的值。 函数回调形式的两个参数prevStateprops。其中prevState是当前组件状态的引用。不能直接被修改。函数中接收的 prevState 和 props 都保证为最新。回调函数的返回值会与 prevState 进行浅合并。所以函数作为setState的参数时,在调用 setState 进行更新 state 时,React 会按照各个 setState 的调用顺序,将它们依次放入一个队列,然后,在进行状态更新时,则按照队列中的先后顺序依次调用,并将上一个调用结束时产生的 prevState传入到下一个调用的函数中,当然,第一个setState调用时,传入的 prevState 则是当前的 default state。第二个参数的作用该函数会在 setState 函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成

一些hooks api

useContext

1.创建上下文对象createContext()=>Context

2.父组件使用Context.Provider进行包裹=>同时提供value共享数据

3.子组件使用useContext(Context:创建的上下文对象)

4.返回值为共享数据

useEffect

副作用钩子。它接收两个参数, 第一个是进行的异步操作, 第二个是数组,用来给出Effect的依赖项 当第二个参数为空数组时,可以模拟componentDidMount

useCallback

1,useCallback防止函数重复声明

2,它的用法是,传入一个函数,第二个参数是一个依赖项数组

3,只有在依赖项发生了变化的时候,才会重新声明函数

4,不使用useCallback时,只要把这个函数传给子组件,就算子组件进行了memo包裹,依旧会重新渲染

5,usecallback要和memo一起使用不然没什么效果

6,应用场景如: 在将一个组件中的函数,传递给子元素进行回调使用时,使用useCallback对函数进行处理 需要缓存的函数,

因为函数式组件每次任何一个 state 的变化 整个组件 都会被重新刷新,一些函数是没有必要被重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费

useMemo

useMemouseCallback 接收的参数都是一样,第一个参数为回调 第二个参数为要依赖的数据 共同点:当依赖发生变化,才会重新计算结果,起到缓存的作用 区别:useMemo计算结果是return回来的值,主要用于缓存计算结果,应用场景需要计算的状态 useCallback计算结果是函数,主要用于 缓存函数

如何使用usememo实现useCallback

	return () => {
		console.log('我是缓存的函数')
	}
}, [我是依赖项])

useImperativeHandle的作用

  1. useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。
  2. 在大多数情况下,应当避免使用 ref 这样的命令式代码。
  3. useImperativeHandle 应当与 forwardRef 一起使用

useEffect和useLayoutEffect区别

不同点:useEffect 是在dom更新后异步执行 。useLayoutEffect 是在dom更新前同步执行(有可能会阻塞dom渲染),执行完再更新dom。useLayoutEffect可以解决更新dom时候屏幕闪烁的问题

useRef 与 createRef 区别

createRef每次都会返回个新的引用;而useRef不会随着组件的更新而重新创建

高阶组件

一个高阶组件就是一个函数,这个函数接受一个组件作为输入,然后返回一个新的组件作为结果而且,返回的新组件拥有了输入组件所不具有的功能,命名约定以 with 开头

缺点:组件多层嵌套, 增加复杂度与理解成本,多层嵌套,相同命名的props会覆盖老属性 不清楚props来源与哪个高阶组件

redux工作流程

  1. 创建 store 时,Redux 就会先调用一次 reducer,来获取到默认状态
  2. 分发动作 store.dispatch(action)更新状态
  3. Redux store 调用 reducer 传入:上一次的状态(当前示例中就是:10)和 action({ type: 'increment' }),计算出新的状态并返回
  4. reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕

Redux 三大原则

  • 单一数据源 整个应用的 state 被存储在一个 Object tree 中,且只存在于唯一的Store中。
  • state 是只读的 唯一改变 state 的方法就是触发 action,action 是一个用于描述发生事件的普通对象,视图部分只需要表达想要修改的意图,所有修改都会被集中化处理。
  • 使用纯函数来执行修改 为了实现根据 action 修改 state值,我们就需要编写 Reducer。它是一个纯函数,接收先前的 state 和 action 返回新的 state ,随着应用的变大,你可以将它拆成多个小的 Reducer ,分别独立操作 state tree 中的不同部分。

受控自建和非受控组件的比较

受控组件:
  • 使用state的数据赋值给表单原生,通过onChange监听值改变修改state数据,完成表单元素的绑定。
非受控组件
  • 没有通过state控制的表单元素,它自己控制自身的值,通过ref获取表单元素获取非受控组件的值

在setTimeout中执行三次setState会怎样

在 React 的合成事件生命周期函数中直接调用setState,会交由 React 的性能优化机制管理,合并多个setState。而在原生事件setTimeout中调用setState,是不受 React 管理的,故并不会合并多个setState,写了几次setState,就会调用几次setState

React性能优化

React性能优化主要是以下两个方向
  1. 减少render的次数,因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为 diff),如果不 render,就不会 reconction。
  2. 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。 使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdate和 PureComponent

函数式组件React.memo和useCallback

React路由组件有哪些,api有哪些

HashRouter和BrowserRouter Link和NavLink Redirect withRouter Switch