面试中问:React中函数组件和class组件的区别,hooks模拟生命周期

这篇具有很好参考价值的文章主要介绍了面试中问:React中函数组件和class组件的区别,hooks模拟生命周期。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

React

1.React中函数组件和class组件的区别

React 的函数组件和类组件在很多方面都相似,但它们也有一些关键的差异。以下是这两种组件之间的主要区别:

  1. 定义方式:

    • 函数组件: 是简单的 JavaScript 函数,接受 props 为参数,并返回 React 元素。

      function Welcome(props) {
        return <h1>Hello, {props.name}</h1>;
      }
      
    • 类组件: 是 ES6 的类,继承的时候要用到 extends React.Component,至少包含一个名为 render 的方法。

      class Welcome extends React.Component {
        render() {
          return <h1>Hello, {this.props.name}</h1>;
        }
      }
      
  2. 状态 (State):

    • 函数组件: 在 React Hooks 出现之前,函数组件不支持状态。但现在,使用 useState 这个 Hook,函数组件也可以有状态了。
    • 类组件: 通过 this.statethis.setState 方法来使用和更新状态。
  3. 生命周期方法:

    • 函数组件: 原本没有生命周期方法,但现在通过使用 Hooks(例如 useEffect)可以模拟大多数生命周期行为。
    • 类组件: 有完整的生命周期方法,如 componentDidMount, componentDidUpdate, componentWillUnmount 等。
  4. this 关键字:

    • 函数组件: 不使用 this。props 作为函数的参数传入。
    • 类组件: 必须使用 this 来访问 props、state 和类的方法。
  5. React Hooks:

    • 函数组件: 可以使用 Hooks。
    • 类组件: 不能使用 Hooks。
  6. 性能和文件大小:

    • 函数组件: 通常来说,函数组件由于没有类的额外开销,因此可能稍微快一点,并且生成的组件代码可能更小。
    • 类组件: 由于有更多的特性和生命周期方法,可能会有稍微的额外开销。

总的来说,随着 React Hooks 的推出,函数组件变得更加强大,使得开发者能够在没有类的情况下构建完整的应用程序。然而,两者都有其适用的场景,取决于特定的需求和开发者的喜好。

2.hooks模拟生命周期

在函数组件中,我们可以使用 Hooks 来模拟类组件中的生命周期函数。下面我将描述如何使用 useStateuseEffect Hooks 来模拟类组件中常见的生命周期函数:

  1. componentDidMount:

    在类组件中,componentDidMount 通常用于执行只需要在组件首次渲染后执行的操作,如 API 调用或订阅。

    使用 useEffect Hook,你可以模拟此生命周期函数:

    useEffect(() => {
        // 这里的代码将在组件首次渲染后执行,类似于 componentDidMount
    }, []); // 空数组意味着这个 useEffect 只会在组件首次渲染后执行一次
    
  2. componentDidUpdate:

    componentDidUpdate 在组件更新后执行,你可以获取到组件更新前的 props 和 state。

    使用 useEffect,你可以这样模拟:

    useEffect(() => {
        // 这里的代码将在每次组件更新后执行,类似于 componentDidUpdate
    });
    

    如果你只对某个特定的 prop 或 state 的变化感兴趣,你可以这样:

    useEffect(() => {
        // 当 someProp 发生变化时,这里的代码将执行
    }, [someProp]);
    
  3. componentWillUnmount:

    类组件中,componentWillUnmount 用于执行清理操作,如取消 API 请求或移除订阅。

    使用 useEffect 的返回函数,你可以模拟此生命周期函数:

    useEffect(() => {
        // 这里的代码将在组件首次渲染后执行
    
        return () => {
            // 这里的代码将在组件卸载前执行,类似于 componentWillUnmount
        };
    }, []);
    
  4. 模拟 shouldComponentUpdate:

    在函数组件中,没有直接的 shouldComponentUpdate 的等价物。但是,你可以使用 React.memo 来包装组件,从而避免不必要的渲染。

    const MyComponent = React.memo(function MyComponent(props) {
        // 你的组件代码
    });
    
  5. 模拟 getSnapshotBeforeUpdatecomponentDidCatch:

    到目前为止,这两个生命周期方法没有对应的 Hooks 等价物。你可能需要使用类组件或查找其他解决方案来模拟这两个生命周期方法的功能。

综上所述,使用 Hooks,函数组件可以模拟大多数类组件的生命周期函数,但某些特定的生命周期方法可能需要其他解决方案或仍然需要类组件。

3.React组件传值

在 React 中,组件之间的数据传递主要基于 props 和 state,但也有一些其他的技术和方法来实现数据传递。以下列举了 React 组件之间传值的几种常见方法,并特别强调了子组件向父组件传值的方法:

  1. 父组件向子组件传值:最常见的数据传递方式。父组件通过属性(props)直接将数据传递给子组件。

    <ChildComponent data={this.state.data} />
    
  2. 子组件向父组件传值

    • 回调函数:父组件提供一个函数作为 props 传递给子组件,子组件在适当的时候调用这个函数并传递数据。
      // 父组件
      handleDataFromChild = (data) => {
          console.log(data);
      }
      <ChildComponent sendData={this.handleDataFromChild} />
      
      // 子组件
      this.props.sendData(data);
      
  3. 通过 Context 传值:当你有一些全局的数据(如主题、认证信息等)需要被多个不直接关联的组件访问时,可以使用 Context。它允许你在组件树中的多个层级传递一个值,而不需要手动地在每一层传递 props。

  4. 兄弟组件之间传值

    • 通过共同的父组件。子组件 A 将值传递给父组件,然后父组件再将这个值通过 props 传递给子组件 B。
    • 使用状态管理库(如 Redux、MobX 等)来维护一个全局状态,任何组件都可以从中读取或修改状态。
  5. 使用 Refs:虽然 refs 主要是为了让父组件可以获取子组件或 DOM 元素的引用,但有时它也可以用来让父组件调用子组件的方法。

  6. 使用全局事件:例如使用 eventEmitter 的库来触发和监听事件,从而实现组件之间的数据传递。

  7. 使用状态管理工具:例如 Redux、MobX。这些工具提供了一个集中的数据存储,所有组件都可以从中读取和更新数据。

请注意,虽然有很多方法可以实现组件间的数据传递,但不是所有方法都适用于每一种情况。选择哪种方法应基于你的具体需求和应用的复杂性。一般来说,尽量保持数据流的简单和单向是一个好的做法。

4.react原理fiber调度算法

React Fiber 是 React 16 引入的新的核心算法,旨在提高应用程序的性能和响应性。其目标是通过分片技术实现增量渲染,允许React暂停工作然后稍后继续进行,从而在大型应用程序中实现更平滑的用户体验。

Fiber 为 React 的每个组件都创建了一个数据结构,这个数据结构持有了关于组件的相关信息。这包括了组件的类型(例如函数或类组件)、输入的props、与该组件关联的DOM元素等等。

以下是 Fiber 调度算法的核心概念和工作原理

  1. 双缓冲技术: Fiber 使用双缓冲技术来实现快速的状态更新。有两棵树在工作: 当前树和工作中的(或下一个)树。当前的树用于显示界面,而下一个树用于实现变化。一旦下一个树准备好了,React 则会快速切换它们,使下一个树成为当前显示的树。

  2. 增量渲染: 与一次性完成所有工作不同,React 通过 Fiber 可以将渲染工作分割成小块。每一块都可以单独处理,这使得 React 可以根据优先级处理更重要的更新,例如那些由用户交互引起的。

  3. 任务的优先级: Fiber 引入了任务的概念,每个任务都有不同的优先级。例如,由用户输入或动画触发的更新有着更高的优先级,而后台数据同步这样的任务优先级较低。

  4. 暂停、中断和恢复: 一个核心的改进是 Fiber 能够暂停正在进行的渲染,稍后再恢复。这使得 React 可以中断正在进行的渲染以处理更紧急的事情

总之,React Fiber 是对 React 核心算法的重大改进,它使框架更具响应性,特别是在大型应用程序中,同时为未来的并发模式和其他高级功能奠定了基础。

5.React中的diff算法

React 的 diff 算法通过以下三种优化策略来提高性能,从O(n^3)到O(n):

  1. 树级比较 (Tree Level):

    • React 对两棵树进行逐级比较。对于两个不同类型的元素,React 会认为它们会生成出不同的树,并直接销毁旧树来创建新树
    • 如果是同类型的组件,React 会保留 DOM 节点并仅比较它们的子元素
    • 这种策略意味着当跨越组件边界移动元素时,React 不会尝试复用那些元素。
  2. 组件级比较 (Component Level):

    • 当用户组件被认为是相等的,React 会按照其 key 来判断它们是不是同一个组件。所以,为组件提供一个稳定的 key(如果它们在数组中)可以帮助 React 进行优化。
    • 如果组件的类型改变,那么该组件及其所有子组件都会被重新创建
    • 当组件类型相同时,React 会保留组件实例调用其生命周期方法来确定是否需要更新。
  3. 元素级比较 (Element Level):

    • 通过为每个子元素分配一个 key,我们可以使算法更加高效。当 key 一致时,React 会认为这两个元素是相同的,只会进行必要的更新。当 key 不一致时,React 会删除旧元素并创建新元素
    • key 应该是稳定的、预测性的、并且尽可能地是唯一的,以避免无意义的元素重建。

React 的 diff 算法利用了上述策略,通过尽量少的操作来更新真实的 DOM。这种策略在大多数情况下都能提供高效且快速的更新,但是也有可能在某些特定场景下不是最优的。这就是为什么在某些情况下,开发者可能需要优化组件的 shouldComponentUpdate 方法或使用 React.memo 来进一步提高性能。

6.Vue与React不同

开发者不同

1. 响应式原理:

  • Vue: 使用Object.defineProperty()进行数据劫持,每个属性被转换为getter和setter,配合watcher实现数据到视图的响应。
  • React: 主要依赖setState()方法更新状态和触发组件重新渲染。还有hooks中的依赖项发生变化

2. 组件写法:

  • Vue: template
  • React: 使用JSX

3. 核心思想:

  • Vue: 是一个渐进式的双向绑定的MVVM框架,双向数据流,降低前端开发门槛。
  • React: 倡导声明式渲染、组件化和单向数据流

4. 模板渲染方式:

  • Vue: 在独立模板中使用指令如v-ifv-for等。
  • React: 在组件内使用JS逻辑进行模板渲染。

5. 框架本质:

  • Vue: 本质上是MVVM框架。
  • React: 为前端组件化框架。

7.不要在循环、条件或嵌套函数中调用 Hooks

Hooks 需要在组件的每次渲染中按照完全相同的顺序被调用。如果你在条件或循环中调用 Hook,这一规则就会被打破,因为在某些渲染中 Hook 可能不会被调用。为了确保组件的行为是可预测的,需要确保每次渲染都会以相同的顺序执行所有的 Hooks。

8.useEffect 和useLayoutEffect的区别

useEffectuseLayoutEffect 都是 React Hooks,它们都允许你在函数组件内部执行副作用(side effects)。但它们的执行时机有所不同。以下是它们之间的主要区别:

  1. 执行时机:

    • useEffect: 它在浏览器完成布局和绘制之后,在一个延迟事件中被调用。这意味着它是异步的并且不会阻塞浏览器的渲染进程。
    • useLayoutEffect: 它的执行时机更早,具体是在浏览器执行绘制之前。因此,它是同步的,会阻塞浏览器的渲染。
  2. 使用场景:

    • useEffect: 适合大多数常见的副作用场景,例如数据获取、订阅或手动更改 DOM 等。
    • useLayoutEffect: 如果你需要在浏览器渲染之前同步地执行某些操作,通常会使用 useLayoutEffect。例如,如果你要读取 DOM 布局并同步地重新渲染,就需要用 useLayoutEffect

总之,除非有明确的理由需要在渲染发生之前同步地执行副作用,否则推荐使用 useEffect。如果你不确定应该选择哪一个,那么 useEffect 是一个更安全的起点。

9. useMemo 和 useCallback

useCallback:

  • 目的: 避免组件重新渲染时不必要地创建新的函数。(优化函数)
  • 用法:

你有一个父组件和一个子组件,子组件使用了React.memo或者是PureComponent来避免不必要的重新渲染。如果父组件传递一个函数给子组件,并且这个函数在父组件的每次渲染时都被重新创建,那么React.memoPureComponent的优化效果就会失效。

这是因为,尽管函数的功能没有改变,但每次都会创建一个新的函数引用,导致子组件认为它接收到了新的props,从而引发子组件的重新渲染。

这就是useCallback派上用场的地方。通过使用useCallback,你可以保证,只有当函数的依赖项发生变化时,函数才会被重新创建。这样,你就可以确保子组件不会因为接收到新的函数引用而进行不必要的重新渲染。

useMemo:

  • 目的: 记忆昂贵的计算结果,避免每次渲染时的重新计算。(优化计算结果)
  • 用法: 当组件中有代价高昂的计算并且不想在每次渲染时都重新执行。

TypeScript (TS) 的理解

  1. TypeScript (TS) 的理解:
    TypeScript 是 JavaScript 的一个超集,为 JavaScript 增加了静态类型(类型注解、类型推断)、类、接口等特性。其主要目的是增强代码的可读性和可维护性,同时捕获开发过程中可能出现的错误。

  2. TypeScript和ES6有什么不同?

    • TypeScript包含了所有的ES6功能,并在其之上添加了类型系统
    • ES6是JavaScript的一个版本,而TypeScript是JavaScript的超集
  3. 什么是类型注解和类型推断?

    • 类型注解:明确地为变量或函数参数指定一个类型
      let name: string = "John";
      
    • 类型推断:不明确指定变量的类型时,TypeScript会使用类型推断来分配类型
      let name = "John";  // TypeScript infers that `name` is of type `string`
      
  4. 描述TypeScript中的访问修饰符。

    • TypeScript中有三个访问修饰符:publicprivateprotected
  5. 描述anyunknownnever类型。

    • any: 用于代表任何类型,使用any后会丧失类型检查的功能
    • unknown: 与any类似,但你必须显式进行类型断言基于类型检查来使用它。
    • never: 表示永远不可能存在的值的类型

1.type和interface区别:

在大多数情况下,interfacetype 是可以互换使用的:

  • 相似点:

    • 两者都可以描述一个对象或函数的形状。
  • 主要区别:

    • 声明合并interface 在定义时可以合并多次。如果两个 interface 有相同的名字,它们会被自动合并成一个。但是,当您使用 type 时,您不能有两个相同名称的 type
    • 使用联合 (Union) 和交叉 (Intersection) 类型type 有更大的灵活性,可以使用联合和交叉类型,而 interface 不可以。
    • implements与extends:类可以实现 (implements) interface,但不能实现 (implements) type
    • 计算属性type 可以使用计算属性,interface 不可以。

总体来说,在大多数情况下,interfacetype 可以互换使用,但是在一些特殊情况下,它们具有一些细微的差异,需要根据具体情况来选择使用哪个。一般来说,如果需要声明合并、实现接口、继承类等高级类型定义,优先选择使用 interface。而对于复杂的类型操作,比如联合类型、交叉类型、泛型类型等,type 可能更为灵活和方便。

2.泛型的理解:

泛型是在定义函数、接口或类时,不预先确定方法的具体类型,而是在使用时才决定的一种特性。使用泛型可以创建可重用的组件,组件不仅能够支持当前的数据类型,还能支持未来的数据类型,给予用户更大的灵活性。例如:

function identity<T>(arg: T): T {
    return arg;
}

在上面的例子中,T 是一个类型变量,它帮助我们捕获用户提供的类型,例如:numberstring,但不限于这些两种。

3.联合 (Union) 和交叉 (Intersection) 类型是什么?请举例

在 TypeScript 中,联合(Union)和交叉(Intersection)类型是两种用于组合多种类型的强大工具。

  1. 联合类型 (Union Types):
    联合类型使用 | 符号来表示一个值可以是多种类型之一

    举例:

    type StringOrNumber = string | number;
    
    let myVar: StringOrNumber;
    
    myVar = 'Hello';  // 有效,因为 'Hello' 是一个 string 类型
    myVar = 123;      // 有效,因为 123 是一个 number 类型
    

    使用联合类型时,你只能访问此联合类型的所有类型里共有的成员。

  2. 交叉类型 (Intersection Types):
    交叉类型使用 & 符号来组合多个类型,让你可以将多个类型合并为一个类型

    举例:

    interface Name {
        name: string;
    }
    
    interface Age {
        age: number;
    }
    
    type Person = Name & Age;
    
    let person: Person = {
        name: 'Alice',
        age: 30
    };
    

    使用交叉类型,person 变量必须具有 NameAge 接口中定义的所有属性。

希望这些示例可以帮助您更好地理解联合类型和交叉类型!

4.implements与extends

类可以实现 (implements) interface,但不能实现 (implements) type。类可以继承 (extends) type 如果 type 描述了类的形状。

  1. 类实现 (implements) interface:

    interface Movable {
        move(distance: number): void;
    }
    
    class Car implements Movable {
        move(distance: number) {
            console.log(`Car moved ${distance} meters.`);
        }
    }
    
  2. 类不能实现 (implements) type,但可以继承 (extends) type:

    这个要分两个例子来说明:

    • 不能实现一个 type:

      type MovableType = {
          move(distance: number): void;
      };
      
      // 这里会产生错误,因为类不能实现一个 type
      class Car implements MovableType {
          move(distance: number) {
              console.log(`Car moved ${distance} meters.`);
          }
      }
      
    • 但如果 type 描述了类的形状,类可以继承它:

      type Vehicle = {
          new (name: string): Vehicle;
          name: string;
          drive(): void;
      };
      
      class Car extends (class implements Vehicle {
          name: string;
          constructor(name: string) {
              this.name = name;
          }
          drive() {
              console.log(`${this.name} drives.`);
          }
      }) {}
      

这里的关键在于,type 是一个值的类型别名,而不是一个真正的类型。当 type 描述一个具有构造签名的类型时(如上面的 Vehicle 示例),它事实上是描述了一个类的形状,这时它可以被一个类继承。但在通常情况下,类不能直接实现 type

5.计算属性

当谈到 TypeScript 的计算属性,我们通常指的是对象字面量中的计算属性键。与 ES6 中的对象字面量计算属性类似,TypeScript 也允许你使用表达式来定义类型的属性键。

  1. 使用 type 定义计算属性:

    const propName = "name";
    
    type MyType = {
        [propName]: string;  // 使用计算属性
        age: number;
    };
    
    let obj: MyType = {
        name: 'Alice',
        age: 30
    };
    
  2. 使用 interface 时无法直接定义计算属性:

    以下是错误的示例:

    const propName = "name";
    
    interface MyInterface {
        [propName]: string;  // 这里会产生错误
        age: number;
    }
    

    然而,需要注意的是,尽管 interface 不能直接定义计算属性,但它们可以定义索引签名来表示某些类型的属性集合,这是一个稍微不同的概念。

总的来说,如果你需要在类型中使用基于某种计算或常量的属性名称,type 会是更合适的选择。文章来源地址https://www.toymoban.com/news/detail-674395.html

到了这里,关于面试中问:React中函数组件和class组件的区别,hooks模拟生命周期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 说说对React中类组件和函数组件的理解?有什么区别?

    通过ES6类的编写形式去编写组件,该类必须继承React.Component,通过this.props的方式去访问父组件传递过来的参数,且在类组件中必须使用render方法,在return中返回React对象,如下: 通过函数编写的形式去实现一个React组件,是React中定义组件最简单的方式,如下: 1.编写形式不

    2024年01月22日
    浏览(49)
  • 【《React Hooks实战》——指导你使用hook开发性能优秀可复用性高的React组件】

    使用React Hooks后,你很快就会发现,代码变得更具有组织性且更易于维护。React Hooks是旨在为用户提供跨组件的重用功能和共享功能的JavaScript函数。利用React Hooks, 可以将组件分成多个函数、管理状态和副作用,并且不必声明类即可调用React内置的功能。而且,上述所有的操作

    2024年02月14日
    浏览(35)
  • react实现模拟弹框遮罩的自定义hook

    点击按钮用于检测鼠标是否命中按钮 React好玩的自定义hook-useClickOutSide_哔哩哔哩_bilibili

    2024年02月12日
    浏览(43)
  • react使用hook封装一个tab组件

    2024年02月09日
    浏览(47)
  • 前端开发笔记 | React Hooks子组件和父组件交互

    前端开发框架目前比较常用的就是react、vue等,其中使用React Hooks 带来了不少的好处,今天来聊聊React Hooks开发方式下,子组件和父组件的交互。 子组件定义 父组件调用子组件 父组件定义 子组件中刷新父组件按钮文案 实际效果:点击子组件中“改变父组件按钮”,父组件中

    2024年02月11日
    浏览(83)
  • react class组件写法

    当使用 React 的 Class 组件时,可以按照以下方式编写: import React, { Component } from \\\'react\\\'; class MyClassComponent extends Component {   constructor(props) {     super(props);     // 在构造函数中初始化状态(state)或绑定方法     this.state = {       // 初始化状态       counter: 0     };    

    2024年02月11日
    浏览(40)
  • React Hook - useState函数的详细解析

    在上一篇文章中, 我用到useState来让大家体验一下hooks函数 那么接下来我们来先研究一下上面核心的一段代码代表什么意思 useState来自react,需要从react中导入,是一个hook函数, 官方中也将它成为State Hook, 它与class组件里面的 this.state 提供的功能完全相同; 一般来说,在函数退出

    2024年01月25日
    浏览(43)
  • react Hook+antd封装一个优雅的弹窗组件

    前言 在之前学vue2的时候封装过一个全局的弹窗组件,可以全局任意地方通过this调用,这次大创项目是用react技术栈,看了一下项目需求,突然发现弹窗还是比较多的,主要分为基础的弹窗以及form表单式的弹窗,如果只是无脑的去写代码,那些项目也没啥必要了。正好react和

    2024年02月13日
    浏览(41)
  • React Hook - useEffecfa函数的使用细节详解

    useEffecf基本使用 书接上文, 上一篇文章我们讲解了State Hook, 我们已经可以通过这个hook在函数式组件中定义state 我们知道在类组件中是可以有生命周期函数的, 那么如何在函数组件中定义类似于生命周期这些函数呢? Effect Hook 可以让你来完成一些类似于class中生命周期的功能; 事

    2023年04月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包