react+ts 基础知识梳理(面试可能会遇到)

1、React的主要特点是什么

主要特点包括声明式编程、组件化结构、虚拟DOM、以及高效的更新和渲染机制,

声明式编程:
声明式编程是一种描述目标的方式,而不是定义达到目标的步骤。在 React 中,你通过描述你的 UI 应该是什么样子来进行声明式编程,而不需要手动操作 DOM。

// 声明式编程示例
const DeclarationExample = () => {
    
    
  return <p>Hello, World!</p>;
};

组件化结构::
React 的应用程序由许多可复用的组件构成,每个组件负责管理自己的状态和 UI。这样的组件化结构使得代码更易于维护和理解。

// 组件化结构示例
const Header = () => <h1>My App</h1>;

const Content = () => <p>This is the content of the app.</p>;

const App = () => (
  <div>
    <Header />
    <Content />
  </div>
);

虚拟 DOM:
React 使用虚拟 DOM 来提高性能。它在内存中维护一个虚拟 DOM 树,然后通过比较新旧虚拟 DOM 树的差异,只更新必要的部分,最终只在真实 DOM 中进行最小化的更新。

// 虚拟 DOM 示例
const VirtualDOMExample = () => {
    
    
  // 虚拟 DOM 对象
  const virtualDOM = (
    <div>
      <p>Hello, World!</p>
    </div>
  );

  // 将虚拟 DOM 渲染到真实 DOM
  ReactDOM.render(virtualDOM, document.getElementById('root'));
};

高效的更新和渲染机制:
React 使用一种称为调和(Reconciliation)的算法来比较新旧虚拟 DOM 树的差异,并只更新必要的部分。这使得 React 能够高效地更新 UI,而不是每次都重新渲染整个页面。

// 高效的更新和渲染机制示例
class DynamicContent extends React.Component {
    
    
  constructor(props) {
    
    
    super(props);
    this.state = {
    
     content: 'Initial Content' };
  }

  updateContent = () => {
    
    
    this.setState({
    
     content: 'Updated Content' });
  };

  render() {
    
    
    return (
      <div>
        <p>{
    
    this.state.content}</p>
        <button onClick={
    
    this.updateContent}>Update Content</button>
      </div>
    );
  }
}

// 将组件渲染到页面
ReactDOM.render(<DynamicContent />, document.getElementById('root'));

2、解释JSX
JSX(JavaScript XML)是一种 JavaScript 的语法扩展,它允许在 JavaScript 代码中直接编写类似 XML 或 HTML 的结构。JSX 是 React 中定义组件结构的主要方式之一。

// JSX 示例
const element = <h1>Hello, JSX!</h1>;

// 将 JSX 渲染到真实 DOM
ReactDOM.render(element, document.getElementById('root'));

在上面例子中,<h1>Hello, JSX!</h1> 就是一个 JSX 元素,它会被 React 转换为相应的 JavaScript 对象。

JSX 可以包含表达式,通过使用花括号 {} 将表达式包裹在 JSX 中。这使得在 JSX 中嵌入 JavaScript 表达式成为可能:

// 在 JSX 中使用表达式
const name = "World";
const element = <h1>Hello, {
    
    name}!</h1>;

ReactDOM.render(element, document.getElementById('root'));

JSX 允许在代码中直观地描述 UI 结构,提高了代码的可读性和可维护性。在底层,React 使用 Babel 等工具将 JSX 转换为标准的 JavaScript 代码,以便浏览器能够理解和执行。

3、类组件和函数组件有什么区别?
类组件可以使用更多React特性,如状态(state)和生命周期方法,而函数组件更简单,通常用于无状态的UI组件。随着Hooks的引入,函数组件也可以使用状态和其他React特性

扫描二维码关注公众号,回复: 17267826 查看本文章

类组件:
类组件是使用 ES6 的 class 语法定义的组件,它继承自 React.Component 类。类组件可以拥有状态(state)和生命周期方法。

// 类组件示例
import React, {
    
     Component } from 'react';

class ClassComponent extends Component {
    
    
  constructor(props) {
    
    
    super(props);
    this.state = {
    
    
      message: 'Hello, Class Component!'
    };
  }

  render() {
    
    
    return <h1>{
    
    this.state.message}</h1>;
  }
}

export default ClassComponent;

函数组件:
函数组件是使用 JavaScript 函数定义的组件。它是一种更简洁的方式来定义没有状态的组件,而且自 React 16.8 版本引入的 Hooks 之后,函数组件也能拥有状态和生命周期特性。

// 函数组件示例
import React, {
    
     useState } from 'react';

const FunctionComponent = () => {
    
    
  const [message, setMessage] = useState('Hello, Function Component!');

  return <h1>{
    
    message}</h1>;
};

export default FunctionComponent;

区别总结:
语法: 类组件使用 class 语法,而函数组件是一个简单的 JavaScript 函数。

状态管理: 在 React 16.3 之前,函数组件没有状态(state)和生命周期方法,但现在可以通过 Hooks(如 useState)来在函数组件中使用状态和其他特性。

性能: 通常来说,函数组件比类组件的性能稍微好一些,因为函数组件没有类组件中的额外开销。但这个差距在实际应用中可能并不明显。

可读性: 函数组件通常更简洁,适合编写小型和无状态的组件。类组件适用于有复杂逻辑和生命周期方法的组件。

4、React中的状态是什么?

状态是React组件中的对象,用于存储影响渲染输出的信息。状态的改变可以触发组件的重新渲染。

在 React 中,状态(State)是组件内部的一种数据存储机制,用于跟踪组件的变化和交互。状态允许组件在渲染过程中保存和修改数据,以便反映用户的输入、应用的内部变化等。React 组件通过 setState 方法来更新状态,并且当状态发生变化时,React 会重新渲染组件以反映最新的状态。

import React, {
    
     Component } from 'react';

class Counter extends Component {
    
    
  // 初始化状态
  constructor(props) {
    
    
    super(props);
    this.state = {
    
    
      count: 0
    };
  }

  // 增加计数
  increaseCount = () => {
    
    
    this.setState({
    
    
      count: this.state.count + 1
    });
  };

  // 减少计数
  decreaseCount = () => {
    
    
    this.setState({
    
    
      count: this.state.count - 1
    });
  };

  render() {
    
    
    return (
      <div>
        <p>Count: {
    
    this.state.count}</p>
        <button onClick={
    
    this.increaseCount}>Increase</button>
        <button onClick={
    
    this.decreaseCount}>Decrease</button>
      </div>
    );
  }
}

export default Counter;

需要注意的是,setState 是一个异步方法,React 会将状态的更新合并,以提高性能。因此,不能直接依赖于 this.state 的值来计算下一个状态。如果需要基于先前的状态计算下一个状态,可以使用回调函数形式的 setState:

// 通过回调函数形式的 setState
this.setState((prevState) => ({
    
    
  count: prevState.count + 1
}));

5、什么是生命周期方法?可以举一些例子吗?

在 React 类组件中,生命周期方法是在组件的不同阶段执行的特殊方法,它们提供了在组件生命周期中执行操作的机会.

componentDidMount:在组件被挂载到 DOM 后立即被调用。通常用于执行一次性的操作,如数据的获取或订阅事件

componentDidMount() {
    
    
  console.log('Component is mounted!');
  // 可以在这里进行数据获取等操作
}

componentDidUpdate:在组件更新后被调用。可以用于执行与前一次渲染不同的操作,例如根据 props 或 state 的变化更新组件

componentDidUpdate(prevProps, prevState) {
    
    
  console.log('Component is updated!');
  // 可以在这里根据 props 或 state 的变化执行相应操作
}

componentWillUnmount:在组件即将被卸载(从 DOM 中删除)之前被调用。通常用于清理操作,如取消订阅、清除计时器等。

componentWillUnmount() {
    
    
  console.log('Component is about to unmount!');
  // 可以在这里进行清理操作
}

shouldComponentUpdate:在组件更新前被调用,用于决定是否触发组件的重新渲染。可以通过比较当前的 props 和 state 与下一次更新的 props 和 state,来优化性能。

shouldComponentUpdate(nextProps, nextState) {
    
    
  // 返回 true 表示允许组件更新,返回 false 表示禁止更新
  return nextProps.someProp !== this.props.someProp || nextState.someState !== this.state.someState;
}

6、解释React Hooks。

Hooks是一种让你在函数组件中“钩入”React状态和生命周期特性的方法。例如,useState和useEffect是常用的Hooks。

常用的Hooks包括useState用于状态管理,useEffect用于处理副作用,useContext用于访问上下文,useReducer用于更复杂的状态逻辑。

7、如何优化React应用的性能?

优化方法包括使用不可变数据,避免不必要的重新渲染,使用React.memo和useMemo,以及代码分割等。

React.memo 和 useMemo 是 React 中用于性能优化的两个工具,它们分别用于优化函数组件和优化计算。下面分别介绍它们的使用:

import React from 'react';

const MyComponent = React.memo((props) => {
    
    
  // 组件的渲染逻辑
});

export default MyComponent;

默认情况下,React.memo 只会浅层比较对象。如果你的组件接收复杂对象类型的 props,你可能需要自定义比较逻辑。可以通过传递第二个参数的方式:

const MyComponent = React.memo((props) => {
    
    
  // 组件的渲染逻辑
}, (prevProps, nextProps) => {
    
    
  // 返回 true 表示两个 props 相等,不需要重新渲染
  // 返回 false 表示 props 不相等,需要重新渲染
});

useMemo 是一个 Hook,用于记忆计算结果。它接收一个工厂函数和一个依赖数组,只有当依赖数组中的值发生变化时,useMemo 才会重新计算值。

import React, {
    
     useMemo } from 'react';

const MyComponent = ({
     
      data }) => {
    
    
  const memoizedData = useMemo(() => {
    
    
    // 计算逻辑,只有 data 发生变化时才会重新计算
    return processData(data);
  }, [data]);

  return (
    // 使用 memoizedData 渲染组件
  );
};

export default MyComponent;

在上面例子中,useMemo 将 processData(data) 的结果缓存起来,只有当 data 发生变化时,才会重新计算。这有助于避免在每次组件渲染时都执行昂贵的计算操作。

需要注意,过度使用 useMemo 可能会导致性能问题,因为它可能会阻止组件进行必要的渲染。因此,只在需要优化的情况下使用 useMemo,并在性能问题出现时进行测量和优化。

8、代码分割(Code Splitting)的一些方法

代码分割(Code Splitting)是一种优化策略,它允许你将代码划分为更小的块,以实现按需加载和提高应用性能。在 React 中,有几种方式可以进行代码分割:

  1. 使用动态 import():
    动态 import() 是 ES6 中的语法,可以让你在运行时动态地引入模块。Webpack 会自动将这些动态导入的模块划分为独立的块,以实现按需加载。
import React, {
    
     useState, useEffect } from 'react';

const MyComponent = () => {
    
    
  const [module, setModule] = useState(null);

  useEffect(() => {
    
    
    import('./MyDynamicComponent').then((module) => {
    
    
      setModule(module.default);
    });
  }, []);

  return module ? <module /> : <div>Loading...</div>;
};

export default MyComponent;

在上面的例子中,import(‘./MyDynamicComponent’) 返回一个 Promise,当 Promise 解决时,setModule 会将模块设置为 MyDynamicComponent。

  1. 使用 React.lazy 和 Suspense:
    React.lazy 是 React 16.6+ 引入的一个 API,它允许你按需加载动态导入的组件。与动态 import() 一起使用,可以实现组件级别的代码分割。
import React, {
    
     lazy, Suspense } from 'react';

const MyDynamicComponent = lazy(() => import('./MyDynamicComponent'));

const MyComponent = () => (
  <Suspense fallback={
    
    <div>Loading...</div>}>
    <MyDynamicComponent />
  </Suspense>
);

export default MyComponent;

在这个例子中,MyDynamicComponent 将被懒加载,并且 Suspense 组件用于指定在加载组件时显示的 UI。注意,Suspense 只能用于包裹 lazy 组件。

  1. 使用 Webpack 的 SplitChunksPlugin:
    Webpack 提供了 SplitChunksPlugin,它可以根据配置将模块划分为更小的块。这是一种更细粒度的代码分割,不仅仅局限于按需加载组件。

在 Webpack 配置中添加:

// webpack.config.js
module.exports = {
    
    
  // ...
  optimization: {
    
    
    splitChunks: {
    
    
      chunks: 'all',
    },
  },
};

上述配置将使得 Webpack 在所有代码中寻找共享模块,并将其划分为独立的块

9、React的Context是什么?

React 的 Context 提供了一种在组件树中传递数据的方式,而不必一级一级手动传递。它适用于在应用的多个组件之间共享常见的配置信息、主题、用户身份信息等。Context 主要包含两个部分:Provider 和 Consumer。

创建 Context 对象:

// MyContext.js
import {
    
     createContext } from 'react;

const MyContext = createContext();
export default MyContext;

使用 Provider 提供数据:
在组件的父组件中,使用 Provider 提供数据。

// App.js
import React from 'react';
import MyContext from './MyContext';

const App = () => {
    
    
  const sharedData = {
    
     user: 'John', theme: 'dark' };

  return (
    <MyContext.Provider value={
    
    sharedData}>
      <MainComponent />
    </MyContext.Provider>
  );
};

在子组件中使用 Consumer 或 useContext:

使用 Consumer:

// SomeComponent.js
import React from 'react';
import MyContext from './MyContext';

const SomeComponent = () => (
  <MyContext.Consumer>
    {
    
    value => (
      <p>User: {
    
    value.user}, Theme: {
    
    value.theme}</p>
    )}
  </MyContext.Consumer>
);

export default SomeComponent;

使用 useContext(需要 React 版本 >= 16.8)

// SomeComponent.js
import React, {
    
     useContext } from 'react';
import MyContext from './MyContext';

const SomeComponent = () => {
    
    
  const value = useContext(MyContext);

  return (
    <p>User: {
    
    value.user}, Theme: {
    
    value.theme}</p>
  );
};

export default SomeComponent;

通过以上方式,SomeComponent 可以在不直接通过 props 层层传递的情况下访问到 MyContext 提供的数据。

注意:

在 Provider 中提供的值,只有在其值发生变化时,Consumer 中的组件才会重新渲染。因此,避免在 Provider 中传递引用类型的值,以免不必要的渲染。

使用 useContext 时,注意 React 版本,确保 React 版本 >= 16.8。

可以通过一个 Context 创建多个不同的 Provider,以实现在组件树中的不同层级传递不同的数据。

10、高阶组件(HOC

高阶组件(Higher Order Component,HOC)是 React 中一种用于复用组件逻辑的高级技术。HOC 是一个函数,接收一个组件作为参数,并返回一个新的组件。HOC 的主要目的是抽取和复用组件之间共享的逻辑,使代码更加干净和可维护。

创建高阶组件:

// withLogger.js
import React from 'react';

const withLogger = (WrappedComponent) => {
    
    
  return class WithLogger extends React.Component {
    
    
    componentDidMount() {
    
    
      console.log('Component is mounted!');
    }

    render() {
    
    
      return <WrappedComponent {
    
    ...this.props} />;
    }
  };
};

export default withLogger;

使用高阶组件:

// MyComponent.js
import React from 'react';
import withLogger from './withLogger';

class MyComponent extends React.Component {
    
    
  render() {
    
    
    return <div>My Component</div>;
  }
}

export default withLogger(MyComponent);

在上面例子中,withLogger 是一个高阶组件,它接收一个组件(WrappedComponent)作为参数,并返回一个新的组件(WithLogger)。新的组件在 componentDidMount 生命周期中打印日志,并渲染了传递给它的原始组件。

特点和用途:
代码复用: HOC 允许你将组件之间共享的逻辑进行抽象和封装,以实现代码复用。

状态和逻辑的共享: 可以通过 HOC 在组件之间共享状态和逻辑。

渲染劫持: HOC 可以拦截渲染过程,对组件进行劫持和修改。

Props 的转换: HOC 可以对传递给它的组件的 props 进行转换或增强。

条件渲染: HOC 可以基于一些条件来决定是否渲染包装的组件。

注意事项:
Props 的透传: 在 HOC 中,要记得将接收到的 props 透传给原始组件,以确保原始组件可以正常使用传递给它的 props。

副作用: 在 HOC 中的生命周期方法和状态的管理需要小心处理,以免引入不必要的副作用或冲突。

命名冲突: 要注意 HOC 内部定义的状态或方法是否会与原始组件发生命名冲突

React 中有一些常见的 HOC,比如 connect(用于连接 React 组件与 Redux store)、withRouter(用于给组件提供路由信息)、withStyles(用于处理样式),它们都是通过 HOC 的思想来实现的。

11、解释Render Props模式

Render Props 模式是一种在 React 中共享组件逻辑的模式。它通过使用一个函数作为组件的 children 属性(或者称为 render 属性)来向组件传递数据或行为,从而使得组件的复用更加灵活。

Render Props 模式的核心思想是将组件的渲染逻辑封装在一个函数中,该函数通过 props 将数据或行为传递给组件。这样,组件可以在渲染时调用这个函数,得到所需要的数据或行为。

// RenderPropComponent.js
import React from 'react';

class RenderPropComponent extends React.Component {
    
    
  state = {
    
    
    count: 0,
  };

  handleIncrement = () => {
    
    
    this.setState((prevState) => ({
    
    
      count: prevState.count + 1,
    }));
  };

  render() {
    
    
    return (
      <div>
        {
    
    this.props.render(this.state.count, this.handleIncrement)}
      </div>
    );
  }
}

export default RenderPropComponent;

在上面例子中,RenderPropComponent 接收一个 render 属性,它是一个函数。在组件的 render 方法中,通过调用 this.props.render 将组件的状态和一个处理逻辑的函数传递给子组件。

// App.js
import React from 'react';
import RenderPropComponent from './RenderPropComponent';

class App extends React.Component {
    
    
  render() {
    
    
    return (
      <div>
        <RenderPropComponent
          render={
    
    (count, handleIncrement) => (
            <div>
              <p>Count: {
    
    count}</p>
              <button onClick={
    
    handleIncrement}>Increment</button>
            </div>
          )}
        />
      </div>
    );
  }
}

export default App;

在上面例子中,RenderPropComponent 的 render 函数返回了一个包含当前计数和递增按钮的 JSX。通过这种方式,父组件可以根据自己的需要决定渲染的内容,而不是受到 RenderPropComponent 固定渲染逻辑的限制。

Render Props 模式使得组件的复用更加灵活,能够根据需要自定义渲染逻辑,同时也方便在组件树中传递数据和行为。

Typescript部分:

  1. TypeScript 基础
    TypeScript和JavaScript有什么区别?
    TypeScript是JavaScript的一个超集,它添加了静态类型系统。这意味着你可以在代码中使用类型注解,从而提前发现和修正错误。

什么是类型断言?
类型断言是一种将变量强制转换为特定类型的方式。在TypeScript中,你可以使用as关键字来实现类型断言。

接口(Interfaces)和类型别名(Type Aliases)有什么区别?
接口和类型别名都可以用来描述对象的形状或函数签名。主要区别是接口可以被扩展和实现,而类型别名不可以。

  1. 泛型
    解释泛型及其用途。
    泛型提供了一种方法来确保类型的一致性,允许我们定义时延迟指定具体的类型。在函数、接口和类中使用泛型可以增加代码的灵活性和可重用性。

如何在React组件中使用泛型?
在React组件中,你可以使用泛型来定义props和state的类型,从而使组件更灵活且类型安全。

  1. 类型兼容性和高级类型
    什么是联合类型和交叉类型?
    联合类型(使用|表示)意味着一个值可以是几种类型之一,而交叉类型(使用&表示)则将多种类型合并为一种类型。

类型兼容性是怎样的?
在TypeScript中,类型兼容性基于结构子类型,如果一个类型的所有成员都可以在另一个类型中找到,那么这两个类型就是兼容的。

  1. 模块和命名空间
    如何在TypeScript中使用模块?
    在TypeScript中,模块是使用export和import语句来创建和使用的。一个模块可以包含代码和声明。

命名空间和模块有什么区别?
命名空间是TypeScript早期的一个特性,用于组织代码和避免全局污染。模块是一种更现代的机制,它依赖于文件和模块加载器。

  1. 工具和编译
    解释tsconfig.json的作用。
    tsconfig.json是TypeScript项目的配置文件。它指定了编译器的选项和项目中包含的文件。

TypeScript如何处理类型错误?
当TypeScript编译器发现类型错误时,它会生成一个错误消息。根据配置,这可能会阻止代码的编译。

React 和 TypeScript 结合使用
如何在React项目中使用TypeScript?
在React项目中使用TypeScript通常意味着在创建组件和应用程序时使用TypeScript来编写代码。你需要在项目中配置TypeScript,并使用.tsx文件扩展名。

在TypeScript中如何定义React组件的Props和State?
在TypeScript中,你可以使用接口或类型别名来定义Props和State的形状。然后在React组件定义中使用这些类型。

解释在React中使用TypeScript的优势。
使用TypeScript的优势包括提高代码质量、提前发现错误、更好的开发体验(如自动补全和代码文档)、以及更易于维护和重构的代码。

TS 的一些应用:

TypeScript(TS)作为 JavaScript 的超集,提供了许多强大的功能和类型安全的编程体验。

  1. 泛型(Generics):
    泛型允许你编写更灵活和可重用的函数、类和组件,而不损失类型安全性。通过泛型,可以在编写代码时不指定具体的类型,而是将类型作为参数传递进去。
// 泛型函数示例
function identity<T>(arg: T): T {
    
    
  return arg;
}

// 使用泛型
let result = identity<string>('Hello, TypeScript!');

  1. 条件类型(Conditional Types):
    条件类型允许在类型定义中使用条件表达式,根据条件的真假来确定最终的类型。
type Check<T> = T extends string ? true : false;

let isString: Check<string>; // true
let isNumber: Check<number>; // false

  1. 映射类型(Mapped Types):
    映射类型允许你基于现有类型创建新类型。通过使用 keyof 和 in 关键字,可以遍历现有类型的属性并创建新的类型。
type Flags = {
    
    
  option1: boolean;
  option2: number;
  option3: string;
};

type NullableFlags = {
    
     [K in keyof Flags]: Flags[K] | null };

let nullableOptions: NullableFlags = {
    
    
  option1: null,
  option2: 42,
  option3: 'Hello',
};

  1. 模块解析设置(Module Resolution):
    TypeScript 支持多种模块解析设置,包括相对路径、非相对路径、Node.js 模块解析等。这些设置可以通过 tsconfig.json 文件进行配置。
{
    
    
  "compilerOptions": {
    
    
    "module": "commonjs",
    "baseUrl": "./src",
    "paths": {
    
    
      "@/*": ["*"]
    }
  }
}

  1. 类型推断(Type Inference):
    TypeScript 的类型推断能力允许在某些情况下不显式指定类型,而让 TypeScript 根据上下文自动推断类型。
let x = 42; // TypeScript 推断 x 的类型为 number
let y = [1, 'hello', true]; // TypeScript 推断 y 的类型为 (string | number | boolean)[]

  1. Decorators(装饰器):

装饰器是一种用于修改类、方法或属性的元编程特性,它允许在声明时附加元数据、修改类的行为等。

function myDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    
    
  // 装饰器逻辑
}

class MyClass {
    
    
  @myDecorator
  myMethod() {
    
    
    // 方法逻辑
  }
}

  1. 交叉类型和联合类型(Intersection Types and Union Types):
    交叉类型允许将多个类型组合为一个新类型,而联合类型则表示值可以是多种类型之一。
type Point2D = {
    
     x: number; y: number };
type Point3D = {
    
     z: number };

// 交叉类型
type Point = Point2D & Point3D;

// 联合类型
type Result = string | number;

  1. 字符串字面量类型(String Literal Types):

字符串字面量类型允许将字符串常量作为类型,提高代码的可读性和安全性。

type Color = 'red' | 'green' | 'blue';
let myColor: Color = 'red'; // 合法
let invalidColor: Color = 'yellow'; // 编译时错误

猜你喜欢

转载自blog.csdn.net/weixin_45047825/article/details/134451521
今日推荐