React 面试题

1、组件通信的方式

父组件传子组件:通过props 的方式
子组件传父组件:父组件将自身函数传入,子组件调用该函数,父组件在函数中拿到子组件传递的数据
兄弟组件通信:找到共同的父节点,用父节点转发进行通信
跨层级通信:使用contetx 可跨域多层传递全局数据
全局状态管理工具:redux 维护一个全局状态中心的store

2、react 的生命周期

挂载阶段:

constructor():constructor中通常只做两件事:初始化组件的 state、给事件处理方法绑定 this
static getDerivedStateFromProps():该函数会在创建或更新props或state时调用
render():用于渲染DOM结构
componentDidMount():在组件挂载后(插入 DOM 树中)立即调,该阶段可执行依赖于DOM的操作、发送网络请求;(官方建议)

更新阶段:

static getDerivedStateFromProps()
shouldComponentUpdate():建议使用该方法会产生明显的性能提升时使用
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载阶段:

componentWillUnmount():在组件卸载及销毁之前直接调用,该阶段可清除 timer,取消网络请求或清除

错误处理:

componentDidCatch():此生命周期在后代组件抛出错误后被调用

3、 props 改变后在哪个生命周期中处理

getDerivedStateFromProps生命周期会在组件接收新的 props 时调用;这个生命周期函数是为了替代componentWillReceiveProps存在

4、 React 性能优化在哪个生命周期?

shouldComponentUpdate
react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。但是,有的时候子组件的接受父组件的数据没有变动,子组件render的执行会影响性能

shouldComponentUpdate(nexrProps) {
    if (this.props.num === nexrProps.num) {
        return false
    }
    return true;
}

shouldComponentUpdate提供了两个参数nextProps和nextState,表示下一次props和一次state的值,当函数返回false时候,render()方法不执行,组件也就不会渲染,返回true时,组件照常重渲染。

5、网络请求在那个生命周期中进行

一般在在componentDidMount中去操作

6、 react 移除了哪些生命周期方法

componentWillReceiveProps()、componentWillMount() 、componentWillUpdate()

7、 React组件的state和props有什么区别

1、props 是传递给组件的(类似于函数的形参),而state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)
2、props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改,state 是多变的、可以修改,每次setState都异步更新的。

8、React中的props为什么是只读的

React中的props是只读的,主要是为了维护组件的可预测性和可维护性。
如果允许子组件修改props,那么一个父组件将状态传递给多个子组件时,这些子组件就可以随意修改props的值,导致状态的不可预测,给调试和维护带来困难。为了解决这个问题,React采用了只读的props机制。子组件只能读取props的值,但不能修改它们。这种设计模式类似于函数式编程中的纯函数,纯函数不会改变传入的参数,只会返回一个新的值。通过保护props不被修改,React确保了组件的可预测性和可维护性

9、setState 是同步的还是异步的

默认情况下是异步的,比如在 React 生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。但是在 React 无法控制的地方,比如原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。这个问题根本原因就是 React 在自己管理的事件回调和生命周期中,对于 setState 是批量更新的,而在其他时候是立即更新的。
setState 的异步设计是为了性能优化、减少渲染次数,假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,界面重新渲染,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。

10、说说你对react的理解

React是一种用于构建用户界面的JavaScript库,它由Facebook开发,使用react结合组件库我们可以快速构建大型、复杂的Web应用程序;

11、react有什么特点

1、 声明式设计:React采用声明式设计,只需要告诉程序想要什么效果而不需要指定实现细节,让开发者更加关注于业务逻辑;
2、组件化:React采用组件化的开发方式,将页面拆分为一个个组件,方便视图的拆分和使用,提高了代码的复用率;
3、虚拟DOM:React使用虚拟DOM和diff算法来有效地操作DOM,提高了应用的性能。
4、单向数据流:React采用单向数据流的数据绑定方式,使得数据的流动清晰可见,方便开发者追踪和调试。
5、生态系统:React有一个强大的生态系统,包括React Router、Redux等,可以轻松实现路由管理、状态管理等。

12、组件化有什么好处

组件是独立和可复用的代码组织单元,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
好处:
1、调试方便,根据报错的组件快速定位问题
2、提高可维护性,由于每个组件的职责单一
3、提高应用开发效率,快速搭建页面

13、声明式和命令式的区别

声明式编程中开发人员只要描述想达到的目的,无须列出实现的所有步骤;命令式编程中开发人员需要列出实现的所有步骤。

14、怎么理解react的声明式设计

React的声明式设计是指开发者只需要关注UI的状态和数据,而不需要关注具体的UI更新逻辑。通过定义组件的状态来描述UI的外观和行为,当状态发生变化时,React会根据状态的变化自动更新UI

15、react的优点和缺点

优点:
1、提高开发效率:React的声明式设计和组件化开发模式可以提高开发效率,减少代码量和开发时间。
2、高性能:React采用虚拟DOM和优化的渲染机制,提高了应用程序的性能和响应速度。
3、跨平台:React可以使用React Native开发原生应用程序,实现跨平台开发。
4、跨浏览器兼容:虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
缺点:
1、React 只是 视图层的一个框架,如果需要做其他事情,需要依赖它的生态系统;如处理单页面路由使用 Router,处理数据使用 Redux。

16、什么是虚拟DOM

虚拟 DOM是一个能够描述 DOM 结构及其属性信息的 JS 对象。
本质上是JS 和 DOM 之间的一个映射缓存,它主要存储在内存中,当数据变化的时候,生成新的虚拟DOM使用Diff算法计算差异,将多次的修改批量的更新到真实DOM上

17、为什么使用虚拟DOM可以提高性能

1、最小化真实DOM操作:真实DOM操作是相对昂贵的,通过使用虚拟DOM来计算处差异后再更新真实DOM,减少了对真实DOM的频繁操作,提高性能。
2、批量更新:虚拟DOM允许在组件内部对多次状态变化进行批量更新。当多个状态变化发生时,会将其合并为一次更新操作,只对虚拟DOM进行一次比较和真实DOM操作,避免了不必要的中间过程,提高了效率。

18、受控组件和非受控组件

受控组件是指组件的状态state来控制,非受控组件的状态不由state控制。
他们之间的区别如下:
1、获取数据方式不同:受控组件通常通过在元素上绑定onChange事件,当表单元素的值发生变化时,会触发onChange事件,从而更新组件的state;非受控组件的数据获取是通过访问DOM读取到value,比如使用ref来访问inputRef.current.value
2、更新数据方式不同:受控组件的数据处理是通过setState()方法进行更新;非受控组件的数据处理不受setState()控制,直接在DOM中处理。
3、数据绑定:受控组件实现了双向数据绑定,当表单元素的值发生变化时,会同时更新组件的state和DOM中的值;非受控组件没有实现双向数据绑定,表单元素的值变化只会影响DOM中的值,不会更新组件的state。

19、react 和 vue 的区别

1、react 是由Facebook开发的,Vue是由尤雨溪开发的
2、在组件化的差异:React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js;
Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)
3、dom的更新策略不同:
react 会自顶向下全diff。vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
在react中,当状态发生改变时,组件树就会自顶向下的全diff, 重新render页面, 重新生成新的虚拟dom tree, 新旧dom tree进行比较, 进行patch打补丁方式,局部更新dom。所以react为了避免父组件更新而引起不必要的子组件更新, 可以在shouldComponentUpdate做逻辑判断,减少没必要的render, 以及重新生成虚拟dom,做差量对比过程。
在vue中, 通过Object.defineProperty 把 data 属性全部转为 getter/setter,追踪每个组件的依赖关系,同时watcher实例对象会在组件渲染时,将属性记录为dep, 当dep 项中的 setter被调用时,通知watch重新计算,使得关联组件更新。
Diff 算法借助元素的 Key 判断元素是新增、删除、修改,从而减少不必要的元素重渲染。
4、框架本质不同
Vue本质是MVVM框架,由MVC发展而来;React是前端组件化框架,由后端组件化发展而来。
5、react 没有双向数据绑定,就父子组件的数据流动来说,都是单项的,子组件不能直接修改父组件的prop,但是vue 有v-modle 监听视图的修改更新数据,而react中通常需要手动调用setState 方法来更新状态

20、react 和 vue 的共同点

他们都是用于构建用户界面的JavaScript库,提供了一套UI解决方案,都有声明式和组件化开发的特点,在渲染上都使用了虚拟DOM进行优化,并且都支持数据驱动,通常开发者无需手动操作DOM,他们也都支持服务端渲染都有native方案…
1、都有组件化开发和虚拟DOM
2、都支持数据驱动,不需要直接操作DOM
3、都支持服务端渲染
4、都有native的方案,react native weex
5、都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库

21、React 高阶组件、Render props、hooks 有什么区别

高阶组件
(High Order Component,简称HOC)它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回一个功能增强的新组件的函数。
优点:高阶组件通常可以用来逻辑复用、不影响被包裹组件的内部逻辑,比如权限控制、数据校验、异常处理。
缺点∶固定的 props 可能会因为重名而被覆盖(如果被包裹的组件和高阶组件有同名props)

// HOC.js
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑处理
    render() {
      // ... 并使用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

render prop
"render prop"是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。
具有render prop 的组件接受一个返回React元素的函数,将render的渲染逻辑注入到组件内部。在这里,"render"的命名可以是任何其他有效的标识符。
大白话:
在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。我们给组件传递一个名为render的props,这个props的参数是组件内部调用时传递的数据,返回值是一个react元素;在组件内部通过this.props.render调用将渲染逻辑注入到组件内部,同时也可以将组件内的状态在调用传递给外部;在这里,"render"的命名可以是任何其他有效的标识符。

功能:将一个组件内的 state 作为 props 传递给调用者, 调用者可以动态的决定如何渲染.

// DataProvider组件内部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {
    name: 'Tom'
  }

    render() {
    return (
        <div>
          <p>共享数据组件自己内部的渲染逻辑</p>
          { this.props.render(this.state) }
      </div>
    );
  }
}

// 调用方式
<DataProvider render={data => (
  <h1>Hello {data.name}</h1>
)}/>

优点:数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者
缺点:无法在 return 语句外访问数据、嵌套写法不够优雅
看下多层嵌套的情况:

const MyComponent = () => {
  return (
    <Mouse>
      {({ x, y }) => (
        <Page>
          {({ x: pageX, y: pageY }) => (
            <Connection>
              {({ api }) => {
                // yikes
              }}
            </Connection>
          )}
        </Page>
      )}
    </Mouse>
  )
};

Hook
是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。通过自定义hook,可以复用代码逻辑。

const { x, y } = useMouse();
const { x: pageX, y: pageY } = usePage();
 
useEffect(() => {
 
}, [pageX, pageY]);

优点:
1、解决hoc的prop 重名问题,hook 可以重命名
2、hook 可以让你在 return 之外使用数据
3、hook 不会嵌套,并且来源清晰
需要注意的是:hook只能在组件顶层使用,不可在分支语句中使用。

总结
Hoc、render props和hook都是为了解决代码复用的问题,但是hoc和render props都有特定的使用场景和明显的缺点。hook是react16.8更新的新的API,让组件逻辑复用更简洁明了,同时也解决了hoc和render props的一些缺点。

22、React.Component 和 React.PureComponent 的区别

PureComponent表示一个纯组件,相对于普通组件来说,它会自动执行shouldComponentUpdate生命周期函数方法,对比新旧props 和 state是否发生变化,如果没有发生变化return false来阻止页面的更新,从而减少不必要的render执行,提高组件的性能。
不过pureComponent中的 shouldComponentUpdate() 进行的是浅比较,只会检查对象或数组的第一层属性,如果有深层次的嵌套无法正确判断,并且无法处理函数和对象引用,两个对象的内容完全相同,如果它们在内存中的地址不同,那么它们也被认为是不同的。
为了解决这个问题,我们可以通过自定义更新逻辑或者使用第三方库来实现深对比;

23、对React中Fragment的理解,它的使用场景是什么?

React中的Fragment是一种特殊的组件,用于在不增加额外DOM节点的情况下组合多个子组件。
在React中,组件返回的元素只能有一个根元素,为了不添加多余的DOM节点,我们可以使用Fragment标签来包裹所有的元素,Fragment标签不会渲染出任何元素。
可以使用<React.Fragment>或<>和</>来创建Fragment组件

24、React实现缓存的方式有哪些?他们有什么区别?

1、在类组件中,使用PureComponent, 只有当组件的props或state发生改变时才会重新渲染组件,提高性能;
2、在函数式组件中,React.memo它会通过对前一次渲染时的props和下一次渲染时的props进行浅比较,来确定是否重新渲染组件。
3、useCallback缓存函数的引用
4、useMemo 缓存计算数据的值

25、React如何获取组件对应的DOM元素

第一种:React.createRef
创建React.createRef,并通过 ref 属性附加到 React 元素

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
// 访问
const node = this.myRef.current;

第二种:回调形式的refs
React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的。

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // 使用原生 DOM API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }


  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
      </div>
    );
  }
}

第三种:字符串类型的ref(react官方不建议使用它,因为 string 类型的 refs 存在 一些问题。它已过时并可能会在未来的版本被移除)

<p ref="info">span</p>
// 访问
this.refs.info 

26、react 事件机制

React基于浏览器的事件机制实现了一套自己的事件机制。
React 合成事件系统模拟了原生事件所有的功能,并且根据 W3C 规范来定义合成事件,兼容了所有的浏览器,拥有和浏览器原生事件相同的接口。
之所以要有自己的合成事件,因为不同浏览器的事件系统存在差异, 合成事件就是一个跨浏览器包装器,实现更好的跨平台兼容性。
同时我们在JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了 document 上,并且使用一个事件监听去处理,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当事件触发时,从数组中弹出事件对象,当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象。避免了频繁地创建和销毁事件对象解决了垃圾回收问题。

所以在react中会先执行原生事件,再执行react 事件,React 的所有事件都是挂载到 document 上的,当真实的 DOM 触发冒泡,到 document 后才会对 React 事件进行处理。如果真实DOM阻止冒泡会导致react 事件不会执行,并且react 的合成事件中不能像原生js一样直接return false ,因为return false 事件会同时preventDefault() 和 stopPropagation() ,可能产生混淆,react希望我们明确地表明其意图,提高代码的可读性,需要明确的调用preventDefault()来阻止默认行为,执行完react 事件最后执行真正在 document 上挂载的事件。

26、合成事件和普通事件的区别

1、对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰;
2、对于事件函数处理语法,原生事件为字符串,react 事件为函数;

<!DOCTYPE html>
<html>
<head>
  <title>原生事件示例</title>
</head>
<body>
  <button id="myButton" onclick="handleClick()">点击我</button>
  <script>
    function handleClick() {
      alert('原生事件触发!');
    }
  </script>
</body>
</html>
import React from 'react';

function MyButton() {
  function handleClick() {
    alert('React事件触发!');
  }

  return (
    <button onClick={handleClick}>点击我</button>
  );
}

3、react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。
在原生的js事件中return false 实际上会同时调用 event.preventDefault() 和 event.stopPropagation()由于这个行为可能导致不预期的副作用,因此 React 的事件系统选择只调用一个方法来明确表达阻止默认行为的意图。所以当需要阻止默认行为时调用preventDefault(),阻止冒泡调用event.stopPropagation

27、使用合成事件有什么优点

React 的合成事件系统是为了解决浏览器原生事件系统的一些问题而设计的
1、兼容所有浏览器,更好的跨平台;在React中,事件处理程序将传递SyntheticEvent的实例,这是一个跨浏览器原生事件包装器,它根据 W3C 规范 来定义合成事件,在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的、稳定的、与DOM原生事件相同的事件接口。
2、将事件统一存放在一个数组,便于统一管理
在浏览器原生事件系统中,每个事件监听器都是单独添加到 DOM 元素上的。这意味着如果你为同一个元素添加多个事件监听器,每个监听器都会有一个独立的引用。当这个元素被销毁时,你需要显式地移除所有这些事件监听器,否则会造成内存泄漏。
而 React 的合成事件系统通过将所有事件监听器统一存放在一个数组中来避免这个问题。当你为一个元素添加事件监听器时,React 会在内部维护一个监听器数组。当这个元素被销毁时,React 只需要简单地清空这个数组,而不需要逐个移除监听器。这样就避免了内存泄漏的问题。

28、react中事件执行的顺序

先执行原生的事件,例如

 this.childRefs.current.addEventListener

再执行react 事件,React 的所有事件都是挂载到 document 上的,当真实的 DOM 触发冒泡,到 document 后才会对 React 事件进行处理。例如:

<input onClick={submit}/>
const submit = () => {
  console.log('react 原生事件')
}

最后执行真正在 document 上挂载的事件

document.addEventListener('click',() => {
  console.log('docment')
})

合成事件会冒泡到 document 上,所以尽量避免原生事件和合成事件混用。如果原生事件阻止冒泡,那么就会导致合成事件不执行

29、React中可以在render访问refs吗?为什么?

不可以,render 阶段 DOM 还没有生成,无法获取 DOM。

30、类组件与函数组件有什么异同?

1、设计思想上,类组件是面向对象编程思想,我们需要继承React.Component并调用render方法返回一个react元素;而函数组件是函数式编程思想,接收一个props可以直接调用,返回一个新的React元素。
2、类组件使用state对象定义状态变量,有诸如componentDidMount、shouldComponentUpdate等生命周期钩子函数;而函数组件没有this,使用一系列的内置hooks实现对应的功能,比如使用useState创建状态变量,使用useEffect实现类似于componentDidMount、shouldComponentUpdate等生命周期钩子函数的功能。
3、缓存方式不同,类组件可以通过pureCompoment 和 shouldComponentUpdata 来缓存,函数式组件可以使用React.memo 、 useMemo、useCallBack 缓存
4、实现复用的方式不同,类组件使用hoc(高阶组件)、render props实现组件的逻辑复用、拓展组件的功能;而函数组件使用自定义hooks实现组件的逻辑复用。

31、react 什么时候会重新渲染,重新渲染时发生了什么

1、state 状态发生变化
2、父组件重新渲染引发子组件重新渲染
3、props 改变
重新渲染时react 会生成新的虚拟DOM树,和旧的虚拟DOM使用diff算法对比,将差异放入一个对象中,然后遍历该对象,更新真实DOM

32、有状态组件和无状态组件的区别

1、有状态组件通常指的是类组件,类组件可以维护自身的状态变量,即组件的 state ,类组件还有不同的生命周期方法。
2、无状态组件可以是类组件也可以是函数式组件,优先设置为函数组件,无状态组件内部不维护state,只根据外部组件传入的props进行渲染的组件,当props改变时,组件重新渲染。无状态组件不能使用ref 和生命周期方法,如自定义的 <Button/>、 <Input /> 等组件。

33、何时使用refs

1、管理焦点,文本选择或媒体播放。
2、触发强制动画。
3、集成第三方 DOM 库。

34、React组件的构造函数有什么作用?它是必须的吗?

两个作用:
1、通过将对象分配给this.state来初始化本地状态
2、将事件处理程序方法绑定到实例上
非必须,设置state的初始值或者绑定事件时,需要加上构造函数

class LikeButton extends React.Component {
  constructor() {
    super();
    this.state = {
      liked: false
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState({liked: !this.state.liked});
  }
  render() {
    const text = this.state.liked ? 'liked' : 'haven\'t liked';
    return (
      <div onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </div>
    );
  }
}
ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

子类必须在constructor方法中调用super方法,因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法子类就得不到this对象并且会报错;

35、说说你对react Hook 的了解

React-Hooks 是一套能够使函数组件更强大、更灵活的“钩子”。
以前我们使用类组件继承React.Component就可以使用state 管理状态,使用生命周期函数。
但类式组件的问题是写法繁琐理解成本高,实现逻辑复用困难,通常为了复用一部分逻辑功能需要继承整个组件,即使使用高阶组件也会增加组件的嵌套层级,导致代码难以理解。
而函数式组件语法更加简洁,并且更利于逻辑的拆分和组件的复用。
但早期的函数式组件无法维护和定义自己的state,也没有生命周期,这就造成了很多局限性,而hook正式为了补全函数式组件缺失的功能出现,我们可以使用这些钩子实现缺失的功能,比如useState 定义状态、useEffect 实现生命周期方法、useContetx 实现组件跨级传递数据等等;使我们可以在不使用class组件的情况下也能使用react的特性;

36、 React Hook 的使用限制有哪些

不要在循环,条件或嵌套函数中调用Hook,必须始终在 React函数的顶层使用Hook
这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。

37、React Hooks 解决了哪些问题

1、组件间状态逻辑复用困难:在 Hooks 出现之前,React 有两种主要的方式来复用组件间的状态逻辑,高阶组件(HOC)和 render props。但这两种方式都有自己的问题,例如,HOC 可能会导致 props 命名冲突,render props 则可能会导致组件树嵌套过深。引入 Hooks 后,可以使用自定义 Hook 来更加方便地复用状态逻辑。
2、复杂组件变得难以理解:在 class 组件中,如果有很多的生命周期方法,逻辑可能会分散在各个生命周期方法中,导致同一块功能的代码被拆分到了不同的地方。而且,由于在生命周期方法中经常需要处理清理逻辑,很容易导致代码混乱。引入 Hooks 后,可以使用 Effect Hook(useEffect)将相关的代码组织在一起。
3、难以理解的 class 组件:class 组件需要了解很多 JavaScript 中的概念,例如 this 的绑定,以及不能将方法直接传给子组件,否则可能会导致不必要的渲染。此外,class 组件的生命周期方法也有很多,需要我们理解和记忆。引入 Hooks 后,可以完全使用函数组件,不再需要 this,也不再需要生命周期方法

38、useEffect 与 useLayoutEffect 的区别

共同点:useEffect 与 useLayoutEffect 两者都是用于处理副作用,这些副作用包括改变 DOM、设置订阅、操作定时器等。
区别:
1、useEffect是在组件完成渲染之后(包括首次渲染和更新渲染)异步触发的。它不会阻塞组件的渲染过程。
2、useLayoutEffect的副作用操作是在组件渲染完成后的"布局阶段"执行的。这意味着它会在浏览器执行绘制之前执行,对DOM的计算和布局有直接影响。因此,useLayoutEffect中的副作用操作会在浏览器更新屏幕之前同步触发。
如果在使用useLayoutEffect时进行大量计算或阻塞操作,可能会导致用户界面的卡顿和不响应。因此,一般情况下推荐使用useEffect,只有在需要在DOM更新之前立即执行某些操作时,才使用useLayoutEffect

39、为什么 useState 要使用数组而不是对象

useState 返回的是 array 而不是 object 的原因就是为了降低使用的复杂度,返回数组的话可以直接根据顺序解构,而返回对象的话要想使用多次就需要定义别名;
比如这里使用数组直接对元素命名,而使用对象需要在解构的时候和对象内定义的名称一致,如果这个名称想多次使用还需要重命名;

// 使用数组
const foo = [1, 2, 3];
const [one, two, three] = foo;
//  使用对象
const user = {
  id: 888,
  name: "xiaoxin"
};
const { id, name } = user;
console.log(id);	// 888
console.log(name);	// "xiaoxin"

// 第一次使用
const { state, setState } = useState(false);
// 第二次使用
const { state: counter, setState: setCounter } = useState(0) 

40、真实DOM和虚拟DOM的区别

虚拟DOM 是一种编程概念,将UI通过数据结构虚拟的表示出来,保存在内存中,其实就是一个JS对象,代表一个虚拟节点;

const vnode = {
  type: 'div',
  props: {
    id: 'hello'
  },
  children: [
    /* 更多 vnode */
  ]
}

react在渲染之前将代码构建为虚拟DOM树,运行时渲染器调用渲染函数然后遍历虚拟DOM树并创建实际的DOM节点,当一个依赖发生变化的时候会创建一个更新后的虚拟DOM树,运行时渲染器使用diff算法遍历对比新旧树,将必要的更新应用到真实的DOM上,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。

41、diff算法对比的过程是怎样的

触发更新 → 生成补丁 → 应用补丁
在这里插入图片描述

42、react加key的作用

Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染.

43、对 SSR的理解

ssr 就是服务端渲染,它指的是在页面首次加载时,由服务端将数据和DOM组合成一个html字符串返回给前端,直接交给浏览器解析;传统的客户端渲染csr 通常是服务端先返回一个body为空的html再加载js文件,执行js文件发送请求,最后将请求的数据填充到dom中。
使用ssr最大的优势就是在大型项目的首屏加载上速度会更快,因为不依赖于js文件减少请求执行时间,直接解析html字符串。
其次有利于seo,服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。
但是也有缺点,服务端渲染会增大服务器的压力,尤其是高并发访问的情况,会大量占用服务端CPU资源;第二点就是对于开发人员来说要实现服务端渲染需要掌握打包工具以及服务端语法,有一定的学习成本。

44、对Portals的理解

React Portals(React 门户)是 React 提供的一种机制,用于我们将子组件渲染到 DOM 树中其他位置,而不受组件层次结构的限制。
通常用于处理一些特殊的 UI 布局需求,比如弹出窗口,我们可以将他放在body上,就不会受父组件容器的限制,但是这种移动仅仅是物理上的移动,它任然可以读取父组件的上下文。
createPortal(children, domNode, key?)
它接受三个参数,
第一个是children 任何可以用React渲染的东西,比如一段JSX或者组件,第二个参数是 domNode 已经存在的dom节点,第三个参数是key

45、js 中 事件绑定的方式


<button id="test" onclick="dianwo()">dianwo</button>
  <button id="test1" >dianwo</button>
  <button id="test2">dianwo</button>
  <script>
    // 方式一
    function dianwo () {
      console.log(this);
    }
    // 方式二
    const btn1 = document.getElementById('test1');
    btn1.addEventListener('click',()=>{
      console.log(this);
    })
    // 方式三
    const btn2 = document.getElementById('test1');
    btn2.onclick= ()=> {
      console.log(this);
    }
  </script>

持续补充中。。如有错误欢迎纠正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值