React 面试题 快速熟悉 第一部分

1,React 组件之间如何通讯?

props、context

2,JSX本质是什么

JSX(JavaScript XML)本质上是一种 JavaScript 语法扩展,用于在 React 中描述用户界面的结构。它的本质可以分为以下几个方面:

  1. 语法糖:JSX 是一种语法糖,它允许您以一种更像是 HTML 的方式来编写 React 组件的结构。这使得编写和阅读组件的代码更加直观和容易。

  2. JavaScript 表达式:JSX 允许您在花括号 {} 内嵌入 JavaScript 表达式。这意味着您可以在 JSX 中执行任何有效的 JavaScript 表达式,并将其结果插入到渲染的输出中。

  3. React 元素:JSX 最终会被转译成 React 元素。React 元素是描述组件结构的纯 JavaScript 对象,它包含有关要渲染的组件类型、属性以及子元素的信息。JSX 的编译过程将 JSX 语法转换为 React 元素,然后 React 将这些元素渲染到 DOM 中。

  4. 可嵌套性:JSX 允许您像 HTML 标记一样嵌套元素,构建出复杂的组件结构。这种可嵌套性使得构建组件树变得非常容易,类似于编写常规的 HTML 结构。

  5. 总之,JSX 是一种用于编写 React 组件的语法糖,它允许将组件结构以声明性的方式描述,并在编译时转换为纯 JavaScript,以便 React 可以渲染它们到页面上。这种方式使得编写和维护 React 应用程序变得更加容易和直观

 3,Context 是什么,如何应用

Context 提供了一种在组件之间共享这些值的方法,而无需显式地通过树的每一层传递 prop
  1. 基本使用        
    1. 创建和注入context

              context一般在顶层组件创建,方便数据的全局注入和全局共享   

import React,{Component,createContext} from 'react'

//使用createContext创建context的初始默认值
//可以是对象,字符串等任意类型的值
export const GlobalContext = createContext({name:'scw'})
//App为顶层组件
class App extends Component{
    constructor(props) {
        super(props);
    }
    
    handleClick = (e) => {
        console.log('组件B点击了');
    }
    render() {
        return (
        //使用Provider进行context全局注入
        //这里的value表示对context重新赋值
            <GlobalContext.Provider value={{name:'scw1',onClick:this.handleClick.bind(this)}}>
                <B />
            </GlobalContext.Provider>
        )
    }
}
export default App;

作者:药师kabuto
链接:https://juejin.cn/post/6969188665532612639
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.消费Context

import { GlobalContext } from './App'
const B = class extends Component{
    render() {
        return <GlobalContext.Consumer>
            {(globalContext)=>
            	//消费Context的数据
                <span onClick={globalContext.onClick}>
                {globalContext.name}

                </span>
            }
        </GlobalContext.Consumer>
    }
}
export default B
  • 使用Provider在顶层组件注入后,里层的所有子组件及其后代组件均可访问到对应的context数据
  • 调用creatContext时可以传入默认值,但该值不是Provider的默认值,而是作为Consumer的默认值,仅当没有Provider的时候才生效。换句话说,只有在没有使用Provider的情况下使用了Consumer,Consumer消费的才是默认值。如果注入Provider时没有设置value值,则Consumer拿到的value为undefined
  • 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
  • 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');

// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}

// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

3.contextType

给顶层class组件(即Provider注入的那个组件中)设置静态属性contextType并赋值为新建的context,则你可以在任何生命周期中通过this.context访问到它,包括 render 函数中

const MyContext = React.createContext(defaultValue);
class MyClass extends React.Component {
	static contextType = MyContext 
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}

 如果是函数组件,则在props后面,会多出一个参数context

const Title = (props, context) => {
  const {textColor} = context.theme;
  return (
    <p style={{color: color}}>
      我是标题
    </p>
  );
};

Title.contextTypes = {
  theme: PropTypes.object
};

我并不建议大量使用 context,因为尽管它可以减少逐层传递,但当组件结构复杂的时候,我们并不知道 context 是从哪里传过来的。Context 就像一个全局变量一样,而全局变量正是导致应用走向混乱的罪魁祸首之一,给组件带来了外部依赖的副作用。在大部分情况下,我们并不推荐使用 context 。使用 context 比较好的场景是真正意义上的全局信息且不会更改,例如界面主题、用户信息等

 4.说一下shouldComponentUpdate的用途

shouldComponentUpdate 是 React 组件生命周期中的一个方法,用于控制组件是否需要重新渲染。它的主要用途是优化性能,避免不必要的组件渲染。

默认情况下,当组件的状态或属性发生变化时,React 会重新渲染该组件及其所有子组件。然而,在某些情况下,组件的重新渲染可能是不必要的,特别是当组件的状态或属性没有实际变化时。这时,可以使用 shouldComponentUpdate 来告诉 React 是否需要重新渲染组件。

shouldComponentUpdate 方法接收两个参数:nextProps 和 nextState,它们分别表示组件即将接收的新属性和新状态。该方法应返回一个布尔值,指示组件是否应该重新渲染。如果返回 true,组件将重新渲染;如果返回 false,组件将跳过重新渲染。

通过在 shouldComponentUpdate 方法中实现自定义的比较逻辑,您可以根据需要决定是否重新渲染组件。这可以帮助您避免不必要的渲染,提高应用程序的性能。

以下是 shouldComponentUpdate 的一些常见用途:

  1. 性能优化: 当组件的属性或状态没有发生实际变化时,可以返回 false,避免不必要的重新渲染。这在组件的渲染开销较大或频繁更新时特别有用。

  2. 避免子组件的不必要渲染: 如果组件的属性或状态变化不会影响子组件的渲染结果,可以在 shouldComponentUpdate 中返回 false,以避免子组件的不必要渲染。

  3. 比较复杂对象或数组: 当组件的属性或状态是复杂对象或数组时,可以在 shouldComponentUpdate 中实现自定义的比较逻辑,只有在实际变化时才返回 true

需要注意的是,如果不实现 shouldComponentUpdate 方法,或者该方法返回 true,则组件将始终重新渲染。因此,只有在需要优化性能时才应该使用 shouldComponentUpdate,并确保实现正确的比较逻辑,以避免出现意外的渲染问题。

5.说一下redux单向数据流的机制

Redux 是一种用于管理应用程序状态的 JavaScript 库,它遵循单向数据流的机制。单向数据流是指数据在应用程序中的流动方向是单向的,从而使状态管理更加可控和可预测。

Redux 的单向数据流机制包含以下几个主要部分:

  1. Store(存储): Redux 中的存储是应用程序的单一状态源。它包含了整个应用程序的状态数据。存储是只读的,唯一改变状态的方式是通过派发(dispatch)一个 action。

  2. Action(动作): Action 是一个简单的 JavaScript 对象,用于描述对状态进行何种操作。它必须包含一个 type 属性,用于指示要执行的操作类型。根据需要,Action 还可以携带其他自定义的数据。

  3. Reducer(归约器): Reducer 是一个纯函数,用于根据接收到的 Action 来更新存储中的状态。它接收当前的状态和要执行的 Action,并返回一个新的状态。Reducer 应该是纯函数,即相同的输入应该始终产生相同的输出,不应该有副作用。

  4. Dispatch(派发): Dispatch 是一个函数,用于将 Action 发送到存储中。当应用程序的某个部分需要更新状态时,它会派发一个 Action,然后 Reducer 会根据 Action 来更新存储中的状态。

  5. Subscribe(订阅): Subscribe 是一个函数,用于注册一个监听器,以便在存储中的状态发生变化时得到通知。通过订阅,可以在状态发生变化时执行相应的操作,例如更新用户界面。

整个过程可以简单概括为:组件通过派发 Action 来请求对状态进行操作,然后 Reducer 根据 Action 来更新存储中的状态,最后订阅的监听器可以得到通知并执行相应的操作,例如更新用户界面。

通过这种单向数据流的机制,Redux 提供了一种可预测和可维护的状态管理方式,使得状态的变化和应用程序的行为更加可控。它也使得应用程序的状态变化历史可追溯,方便调试和回溯。        

6.React 类组件的setState 是同步操作还是异步操作?

在 React 类组件中,setState 方法是异步操作。这意味着在调用 setState 后,React 并不会立即更新组件的状态,而是将更新放入队列中,并在稍后的时间点进行批量处理。

React 之所以选择异步更新状态的方式,是为了优化性能和提高效率。当多次连续调用 setState 时,React 会将这些更新合并为一个更新,从而减少不必要的组件重新渲染次数,提高性能。

由于 setState 是异步操作,所以不能立即在调用后获取到更新后的状态值。如果需要在 setState 更新后执行某些操作,可以使用回调函数作为 setState 的第二个参数。

需要注意的是,虽然 setState 是异步操作,但在某些情况下,React 可能会选择同步更新状态。例如,在 React 生命周期方法、事件处理程序或异步操作的回调函数中,setState 可能会同步更新状态。但是,为了保持一致性和可预测性,最好将 setState 视为异步操作,并在需要获取最新状态时使用回调函数。

7.什么是纯函数

纯函数是指在相同的输入下,始终返回相同的输出,并且没有副作用的函数。纯函数不会修改传入的参数,也不会对外部环境产生任何可观察的影响,例如修改全局变量或发起网络请求。

纯函数具有以下特点:

  1. 确定性:给定相同的输入,纯函数总是返回相同的输出。它不受外部状态的影响,只依赖于输入参数。

  2. 无副作用:纯函数不会对外部环境产生任何可观察的影响,例如修改传入的参数、修改全局变量、发起网络请求等。它只通过返回值来提供结果。

纯函数的优点包括:

  1. 可测试性:由于纯函数的输出仅由输入决定,因此很容易编写测试用例来验证其行为。

  2. 可缓存性:由于纯函数对于相同的输入始终返回相同的输出,可以使用缓存来提高性能。可以将函数的输入和输出进行缓存,以避免重复计算。

  3. 可组合性:纯函数可以方便地组合成更复杂的函数,因为它们不依赖于外部状态。

在函数式编程中,纯函数是非常重要的概念。通过编写纯函数,可以减少代码的复杂性、提高代码的可读性和可维护性,并且更容易进行并发处理和调试。在 React 中,使用纯函数可以更好地管理组件的状态和副作用,以及实现更高效的渲染。

8.介绍React组件生命周期

React 组件生命周期是指组件在被创建、更新和销毁过程中所经历的一系列阶段。通过这些生命周期方法,我们可以在不同的阶段执行特定的操作,例如初始化状态、处理数据更新、清理资源等。

React 组件生命周期可以分为三个主要阶段:

  1. 组件的创建阶段

    • constructor():组件的构造函数,在组件被创建时调用,用于初始化状态和绑定方法。
    • static getDerivedStateFromProps(props, state):在组件实例化和接收新的属性时调用,用于根据新的属性更新状态。
    • render():必须实现的方法,返回组件的 JSX 结构。
    • componentDidMount():在组件被渲染到 DOM 后调用,可以进行异步操作、数据获取等。
  2. 组件的更新阶段

    • static getDerivedStateFromProps(props, state):在组件接收新的属性时调用,用于根据新的属性更新状态。
    • shouldComponentUpdate(nextProps, nextState):在组件更新前调用,用于判断是否需要重新渲染组件,默认返回 true
    • render():重新渲染组件的 JSX 结构。
    • getSnapshotBeforeUpdate(prevProps, prevState):在组件更新前调用,返回一个值作为 componentDidUpdate 的第三个参数。
    • componentDidUpdate(prevProps, prevState, snapshot):在组件更新后调用,可以进行 DOM 操作、网络请求等。
  3. 组件的卸载阶段

    • componentWillUnmount():在组件即将被销毁时调用,可以进行清理操作,如取消订阅、清除定时器等。

此外,还有一些其他的生命周期方法,如 componentDidCatch() 用于处理组件内部的错误,componentDidUpdate() 用于处理组件更新后的操作等。

需要注意的是,React 16.3 版本之后,一些生命周期方法已经被标记为过时,推荐使用新的生命周期方法或钩子函数来替代。

通过合理使用组件生命周期方法,我们可以在不同的阶段执行适当的操作,从而实现更好的组件控制和优化。

9.React发起ajax应该在哪个生命周期

应该在React组件的哪个生命周期函数中发起AJAX请求?我们推荐你在componentDidMount这个生命周期函数中发起AJA×请求。这样做你可以拿到AJAX请求返回的数据并通过setState来更新组件。 应该在反应组件的哪个生命周期函数中发起AJAX请求?我们推荐你在组分这个生命周期函数中发起AJA×请求。这样做你可以拿到ajax请求返回的数据并通过setState来更新组件。

10.渲染列表,为何使用key?

key 属性能够帮助 React 更快速地识别哪些列表项需要更新,从而提高渲染性能; 避免重复渲染:如果没有指定 key 属性,React 会在每次更新列表时重新渲染所有的列表项,而不仅仅是新增、删除或更改的列表项;

11.函数组件和class组件的区别

组件定义方式不同; 2. (因为组件定义方式不同)生命周期不同:class组件有,函数组件无; 3. (因为生命周期不同)副作用操作执行不同:class组件通过生命周期函数,函数组件用hook的useEffect; 4. state的定义、读取、修改方式不同:函数组件用hook的useState;

12.什么是受控组件、什么是非受控组件

受控组件:即通过setState的形式控制输入的值及更新,非受控组件:即通过dom的形式更新值,要获取其值可以通过ref的形式去获取。

13.什么时候使用异步组件

1.在可能发生等待的情况
2.等待过程中不能像alert一样阻塞程序的时候
3.因此,所有的“等待的情况”都需要异步
一句话总结就是需要等待但是又不能阻塞程序的时候需要使用异步

4.定时任务:setTimeout,setInverval
5.网络请求:ajax请求,img图片的动态加载
6.事件绑定或者叫DOM事件,比如一个点击事件,我不知道它什么时候点,但是在它点击之前,我该干什么还是干什么。用addEventListener注册一个类型的事件的时候,浏览器会有一个单独的模块去接收这个东西,当事件被触发的时候,浏览器的某个模块,会把相应的函数扔到异步队列中,如果现在执行栈中是空的,就会直接执行这个函数。
7.ES6中的Promise

14.多个组件有公共逻辑。如何抽离 

        正向HOC:模式简单,容易理解,但会增加组件层级,也就是我们上面说的类组件,所以会增加属性透传成本

        Render Props:代码会相对简单,但比较难理解

15.React 如何进行异步请求

 除了使用原生的fetch API,我们还可以使用第三方库axios来进行数据请求。 axios是一个流行的基于Promise的HTTP客户端,可以在浏览器和Node.js环境中使用。 本文介绍了在React中进行异步数据请求的两种常见方法:使用fetch API和使用axios库。 钩子发起异步请求,并在请求完成后更新组件的状态。 钩子发起异步请求,并在请求完成后更新。

 16.React-router如何配置懒加载

第一种

import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'

import router from '@/router' 

class MyMain extends React.Component{
  render() {
    return (
      <main>
        <Routes>
          {
            router.map((item, i) => {
              return (
                <Route key={i} path={item.path} element={
                  <Suspense fallback={
                    <div>路由懒加载...</div>
                  }>
                    < item.component />
                  </Suspense>
                } />
              )
            })
          }
        </Routes>
      </main>
    )
  }
}

export default MyMain

路由表

import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'

import router from '@/router' 

class MyMain extends React.Component{
  render() {
    return (
      <main>
        <Routes>
          {
            router.map((item, i) => {
              return (
                <Route key={i} path={item.path} element={
                  <Suspense fallback={
                    <div>路由懒加载...</div>
                  }>
                    < item.component />
                  </Suspense>
                } />
              )
            })
          }
        </Routes>
      </main>
    )
  }
}

export default MyMain

 

第二种

使用react-loadable进行路由懒加载
需要安装依赖

yarn add react-loadable 或者 npm i react-loadable
如果你是typescript
你还需要额外安装一个依赖
yarn add @types/react-loadable 或者 npm i @types/react-loadable
我这边使用的是yarn
yarn add react-loadable @types/react-loadable

import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'

import router from '@/router' 

class MyMain extends React.Component{
  render() {
    return (
      <main>
        <Routes>
          {
            router.map((item, i) => {
              return (
                <Route
                  key={i}
                  path={item.path}
                  element={ < item.component /> }
                />
              )
            })
          }
        </Routes>
      </main>
    )
  }
}

export default MyMain

路由表

import React from 'react'
import loadable from 'react-loadable' //引入这个loadable,使用这个来加载路由
// 如果你是js就直接无视这个interface的定义
interface Router {
  name?: string,
  path: string,
  children?: Array<Router>,
  component: any
}
const LoadingTip = () => <div>懒加载路由ing...</div>
// 如果你是js就直接无视这个: Array<Router>的类型限定
const router: Array<Router> = [
  {
    path: '/',
    component: loadable({
      loader: () => import('@/views/home'), // 需要异步加载的路由
      loading: LoadingTip // 这是一个的提示
    })
  },
  {
    path: '/about',
    component: loadable({
      loader: () => import('@/views/about'),
      loading: LoadingTip
    })
  }
]

export default router

17.什么是pureComponent? 

 React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思, PureComponent 也就是纯组件,取代其前身 PureRenderMixin , PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render 操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数(shouldComponentUpdate通过判断props和state是否发生变化来决定需不需要重新渲染组件),主要目的就是防止不必要的子组件渲染更新。

18.React事件和DOM事件有什么区别 

 React事件是React库中的自定义事件,用于处理用户与应用程序交互的行为。 React事件具有统一的语法和行为,无论浏览器环境如何,它们在不同的浏览器中都具有一致的表现。 DOM事件是由浏览器提供的原生事件,用于处理用户与网页交互的行为。 DOM事件包括点击、滚动、键盘输入等。 在React中,可以使用DOM事件来处理React组件中的交互行为。 React事件和DOM事件之间的主要区别在于事件处理的语法和绑定方式

19.React性能优化方式有那些

 1 Reac.memo 缓存组件
2 使用 useMemo 缓存大量的计算
3 避免使用 内联对象
4 避免使用 匿名函数
5 延迟加载不是立即需要的组件
6 调整CSS而不是强制组件加载和卸载
7 使用React.Fragment避免添加额外的DOM
8 使用React.PureComponent , shouldComponentUpdate

20。说一下React和Vue 的区别

 vue的思想是响应式的,也就是基于是数据可变的,实现了数据的双向绑定,react整体是函数式的思想,是单向数据流,推崇结合immutable来实现数据不可变 vue 采用了template, react采用了jsx (本质上都是模版) React依赖Virtual DOM,而Vue.js使用的是DOM模板。 React采用的Virtual DOM会对渲染出来的结果做脏检查。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值