Bootstrap

前端面试题大汇总:React 篇

基础知识


1. 什么是 React?它的主要特点是什么?

React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发并维护。它主要用于构建单页应用程序(SPA)和复杂的用户界面。React 的主要特点包括:

  • 组件化:React 将 UI 分解成独立的、可重用的组件。每个组件都有自己的逻辑和控制。
  • 虚拟 DOM:React 使用虚拟 DOM 来提高性能。虚拟 DOM 是一个内存中的树结构,React 会先在虚拟 DOM 中进行操作,然后批量更新真实 DOM。
  • 声明式编程:React 采用声明式编程风格,开发者只需描述 UI 应该是什么样的,React 会负责处理 UI 的变化。
  • JSX:React 使用 JSX(JavaScript XML)语法,允许在 JavaScript 中编写类似 HTML 的标记。
  • 生态系统丰富:React 拥有丰富的生态系统,包括路由器(React Router)、状态管理库(Redux、MobX)等。

2. 什么是 JSX?它有什么优点?

JSX 是一种语法扩展,允许在 JavaScript 中编写类似 HTML 的标记。JSX 最终会被编译成普通的 JavaScript 代码。

优点

  • 可读性强:JSX 使得模板代码更加直观和易读,特别是对于复杂的 UI 结构。
  • 类型检查:JSX 可以在编译时进行类型检查,减少运行时错误。
  • 表达式支持:可以在 JSX 中嵌入 JavaScript 表达式,使得动态生成 UI 变得更加方便。
  • 工具支持:现代开发工具(如 Babel)可以将 JSX 编译成兼容所有浏览器的 JavaScript 代码。

3. 什么是虚拟 DOM?它是如何工作的?

虚拟 DOM 是一个轻量级的内存中的 DOM 树表示。React 使用虚拟 DOM 来提高性能,避免频繁的操作真实 DOM。

工作原理

  1. 创建虚拟 DOM:React 在内存中创建一个虚拟 DOM 树。
  2. Diff 算法:当状态改变时,React 会比较新的虚拟 DOM 和旧的虚拟 DOM,找出差异(即最小的变更集合)。
  3. 批量更新:React 将这些差异批量应用到真实 DOM,减少 DOM 操作次数,提高性能。

4. React 中的单向数据流是什么意思?

单向数据流 是 React 的核心设计理念之一。在单向数据流中,数据只能从父组件流向子组件,不能反向流动。这种方式使得数据流更加清晰和可控。

特点

  • 数据一致性:单向数据流确保了数据的一致性,避免了数据的混乱和不可预测的变化。
  • 易于调试:数据流的单一方向使得调试更加容易,可以更容易地追踪数据的变化。
  • 可预测性:单向数据流使得应用的状态变化更加可预测,便于维护和扩展。

5. 什么是 Props 和 State?它们的区别是什么?

Props(属性):

  • 定义:Props 是组件之间传递数据的一种方式。父组件可以通过 props 向子组件传递数据。
  • 不可变:Props 是只读的,子组件不能修改传入的 props。
  • 用途:用于配置组件的行为和外观。

State(状态):

  • 定义:State 是组件内部的状态,用于存储组件的数据。
  • 可变:State 是可变的,可以通过 setState 方法更新。
  • 用途:用于管理组件的内部状态,控制组件的行为和渲染。

区别

  • 来源:Props 来自父组件,State 是组件自身的状态。
  • 可变性:Props 是只读的,State 是可变的。
  • 用途:Props 用于配置组件,State 用于管理组件的内部状态。

6. 如何在 React 中创建一个组件?

函数组件

const MyComponent = (props) => {
  return <div>Hello, {props.name}!</div>;
};

类组件

class MyComponent extends React.Component {
  render() {
    return <div>Hello, {this.props.name}!</div>;
  }
}

7. 什么是函数组件和类组件?它们有什么区别?

函数组件

  • 定义:函数组件是一个简单的 JavaScript 函数,接收 props 作为参数,返回 JSX。
  • 优点:代码更简洁,性能更好(因为没有类的开销)。
  • 限制:早期版本的函数组件不支持生命周期方法和状态管理,但随着 Hooks 的引入,这些限制已经被解除。

类组件

  • 定义:类组件是继承自 React.Component 的 ES6 类,可以定义生命周期方法和管理状态。
  • 优点:支持生命周期方法和状态管理,功能更强大。
  • 缺点:代码相对复杂,性能略逊于函数组件。

8. 什么是纯组件?为什么要使用纯组件?

纯组件

  • 定义:纯组件是一种特殊的组件,它通过 React.memo(函数组件)或 PureComponent(类组件)来实现。纯组件会在 props 或 state 发生变化时进行浅比较,如果前后值相同,则跳过重新渲染。

  • 优点

    • 性能优化:减少不必要的重新渲染,提高应用性能。
    • 简化逻辑:开发者不需要手动实现 shouldComponentUpdate 方法来优化性能。

使用场景

  • 静态数据:组件的 props 和 state 不经常变化。
  • 复杂组件:组件内部逻辑复杂,重新渲染开销大。

9. 什么是 React Context API?它解决了什么问题?

React Context API

  • 定义:Context API 是 React 提供的一种在组件树中传递数据的机制,无需通过 props 逐层传递。

  • 使用

    • 创建 Context:使用 React.createContext 创建一个 Context 对象。
    • 提供 Context:使用 Context.Provider 组件将数据传递给子组件。
    • 消费 Context:使用 Context.Consumer 组件或 useContext Hook 在子组件中访问数据。

组件

1. 什么是受控组件和非受控组件?它们的区别是什么?

受控组件(Controlled Components)

  • 定义:受控组件是指那些其输入值由 React 的状态(state)控制的表单组件。每次用户输入时,都会触发一个事件处理器,更新组件的状态,从而更新表单的值。

  • 特点

    • 状态管理:表单的值由 React 状态管理。
    • 事件处理:每次用户输入时,都会触发事件处理器。
  • 示例

    import React, { useState } from 'react';
    
    const ControlledForm = () => {
      const [value, setValue] = useState('');
    
      const handleChange = (e) => {
        setValue(e.target.value);
      };
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form submitted with value:', value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" value={value} onChange={handleChange} />
          <button type="submit">Submit</button>
        </form>
      );
    };
    
    export default ControlledForm;
    

非受控组件(Uncontrolled Components)

  • 定义:非受控组件是指那些其输入值不由 React 状态管理的表单组件。相反,它们依赖于 DOM API 来获取表单的值。

  • 特点

    • DOM API:通过 ref 获取表单的值。
    • 初始值:可以通过 defaultValuedefaultChecked 属性设置初始值。
  • 示例

    import React, { useRef } from 'react';
    
    const UncontrolledForm = () => {
      const inputRef = useRef(null);
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form submitted with value:', inputRef.current.value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" ref={inputRef} />
          <button type="submit">Submit</button>
        </form>
      );
    };
    
    export default UncontrolledForm;
    

2. 如何在 React 中实现条件渲染?

条件渲染 是指根据条件决定是否渲染某个组件或元素。React 提供了多种方式来实现条件渲染:

  • 三元运算符

    const App = ({ isLoggedIn }) => {
      return (
        <div>
          {isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please log in.</h1>}
        </div>
      );
    };
    
  • 逻辑与运算符

    const App = ({ user }) => {
      return (
        <div>
          {user && <h1>Welcome, {user.name}!</h1>}
        </div>
      );
    };
    
  • 使用 if 语句

    const App = ({ items }) => {
      if (items.length === 0) {
        return <h1>No items found.</h1>;
      }
    
      return (
        <ul>
          {items.map(item => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      );
    };
    

3. 什么是高阶组件(HOC)?请给出一个实际的例子。

高阶组件(Higher-Order Component, HOC) 是一个函数,它接受一个组件并返回一个新的组件。HOC 用于复用组件逻辑,增强组件的功能。

示例: 假设我们有一个 withLogging HOC,用于在组件渲染时记录日志。

import React from 'react';

// 高阶组件
const withLogging = (WrappedComponent) => {
  return class extends React.Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} mounted`);
    }

    componentWillUnmount() {
      console.log(`Component ${WrappedComponent.name} will unmount`);
    }

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

// 被包裹的组件
const MyComponent = (props) => {
  return <h1>Hello, {props.name}!</h1>;
};

// 使用 HOC
const MyComponentWithLogging = withLogging(MyComponent);

export default MyComponentWithLogging;

4. 什么是 Render Props?它有什么优点?

Render Props 是一种在 React 中共享代码的技术,通过在组件中传递一个函数作为 prop 来实现。这个函数负责返回 JSX,从而实现在不同组件间共享逻辑。

优点

  • 复用逻辑:可以在多个组件中复用相同的逻辑。
  • 灵活性:可以自由地决定如何渲染组件,而不受限于预定义的结构。

示例: 假设我们有一个 MouseTracker 组件,用于跟踪鼠标位置。

import React from 'react';

// 跟踪鼠标位置的组件
class MouseTracker extends React.Component {
  constructor(props) {
    super(props);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// 使用 Render Props 的组件
const App = () => {
  return (
    <MouseTracker render={({ x, y }) => (
      <h1>The mouse position is ({x}, {y})</h1>
    )} />
  );
};

export default App;

5. 如何在 React 中实现列表渲染?

列表渲染 是指根据数组数据动态生成多个组件或元素。React 提供了 map 方法来实现这一功能。

示例: 假设我们有一个 items 数组,需要渲染成一个列表。

import React from 'react';

const App = ({ items }) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
};

const items = [
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Cherry' }
];

export default () => <App items={items} />;

6. 什么是 Fragment?它的作用是什么?

Fragment 是 React 提供的一个特殊组件,用于包裹多个子元素,而不会在 DOM 中添加额外的节点。这在需要返回多个根节点时非常有用。

作用

  • 避免额外的 DOM 节点:在需要返回多个根节点时,使用 Fragment 可以避免添加不必要的 DOM 节点。
  • 保持代码整洁:可以使 JSX 代码更加简洁和易读。

示例

import React from 'react';

const App = () => {
  return (
    <React.Fragment>
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </React.Fragment>
  );
};

// 简写形式
const App = () => {
  return (
    <>
      <h1>Title</h1>
      <p>Paragraph 1</p>
      <p>Paragraph 2</p>
    </>
  );
};

export default App;

7. 如何在 React 中实现事件处理?

事件处理 是指在 React 中处理用户的交互操作,如点击、输入等。React 使用合成事件系统来处理事件。

示例

import React, { useState } from 'react';

const App = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default App;

8. 什么是 Refs?如何使用 Refs?

Refs 是 React 提供的一种访问 DOM 节点或在类组件中访问实例的方法。Refs 可以用于获取输入值、管理焦点、触发动画等。

使用方法

  • 创建 Ref:使用 useRef Hook 或 React.createRef
  • 附加 Ref:将 Ref 附加到需要访问的 DOM 节点或组件实例。
  • 访问 Ref:通过 ref.current 访问 DOM 节点或组件实例。

示例

import React, { useRef } from 'react';

const App = () => {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
};

export default App;

状态管理

1. 什么是 Redux?它的主要特点是什么?

Redux 是一个用于管理应用状态的 JavaScript 库,通常与 React 一起使用。它提供了一种集中管理应用状态的方式,使得状态管理更加可预测和可维护。

主要特点

  • 单一数据源:整个应用的状态存储在一个单一的 store 中,确保了状态的一致性。
  • 状态不可变:状态是不可变的,每次状态变化时,都会生成一个新的状态对象。
  • 纯函数:通过纯函数(reducer)来处理状态变化,使得状态变化可预测。
  • 中间件支持:支持中间件,可以扩展 Redux 的功能,如异步操作、日志记录等。
  • 开发者工具:提供了强大的开发者工具,可以调试、回溯和重放状态变化。

2. 什么是 Redux Thunk?它解决了什么问题?

Redux Thunk 是一个中间件,允许你在 action 创建函数中返回一个函数而不是一个 action 对象。这个返回的函数可以包含异步逻辑,并在适当的时候 dispatch 一个或多个 action。

解决问题

  • 异步操作:Redux Thunk 允许你处理异步操作,如 AJAX 请求,而不需要在 reducer 中处理异步逻辑。
  • 复杂逻辑:可以处理复杂的业务逻辑,如条件 dispatch、多次 dispatch 等。

示例

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const initialState = { data: null };

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_DATA_SUCCESS':
      return { ...state, data: action.payload };
    default:
      return state;
  }
};

const fetchData = () => async (dispatch) => {
  const response = await fetch('/api/data');
  const data = await response.json();
  dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });
};

const store = createStore(reducer, applyMiddleware(thunk));

store.dispatch(fetchData());

3. 什么是 Redux Saga?它与 Redux Thunk 有什么区别?

Redux Saga 是一个用于管理应用副作用(如异步操作)的库,使用 Generator 函数来处理异步逻辑。

区别

  • Generator 函数:Redux Saga 使用 Generator 函数,提供了更强大的控制流和错误处理机制。
  • 可测试性:Redux Saga 的副作用可以更容易地进行单元测试。
  • 复杂逻辑:Redux Saga 更适合处理复杂的异步逻辑和并发操作。

示例

import { takeLatest, call, put } from 'redux-saga/effects';
import { fetchDataSuccess, fetchDataFailure } from './actions';

function* fetchDataSaga() {
  try {
    const response = yield call(fetch, '/api/data');
    const data = yield response.json();
    yield put(fetchDataSuccess(data));
  } catch (error) {
    yield put(fetchDataFailure(error));
  }
}

function* watchFetchData() {
  yield takeLatest('FETCH_DATA_REQUEST', fetchDataSaga);
}

export default function* rootSaga() {
  yield watchFetchData();
}

4. 什么是 MobX?它与 Redux 有什么区别?

MobX 是一个简单的状态管理库,使用观察者模式来管理应用状态。它允许你直接操作状态,而不需要通过纯函数和 action。

区别

  • 直接操作状态:MobX 允许你直接修改状态,而不需要通过 action 和 reducer。
  • 自动反应:MobX 使用装饰器和 observable 来自动跟踪状态变化,并更新相关的视图。
  • 简单易用:MobX 的 API 更加简单,学习曲线更低。

示例

import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react';

class Store {
  data = null;

  constructor() {
    makeAutoObservable(this);
  }

  fetchData = async () => {
    const response = await fetch('/api/data');
    const data = await response.json();
    this.data = data;
  };
}

const store = new Store();

const App = observer(() => {
  return (
    <div>
      <button onClick={() => store.fetchData()}>Fetch Data</button>
      {store.data && <pre>{JSON.stringify(store.data, null, 2)}</pre>}
    </div>
  );
});

5. 如何在 React 中使用 Redux?

步骤

  1. 安装 Redux 和 React-Redux

    npm install redux react-redux
    
  2. 创建 Reducer

    const initialState = { count: 0 };
    
    const counterReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'INCREMENT':
          return { ...state, count: state.count + 1 };
        case 'DECREMENT':
          return { ...state, count: state.count - 1 };
        default:
          return state;
      }
    };
    
  3. 创建 Store

    import { createStore } from 'redux';
    import counterReducer from './counterReducer';
    
    const store = createStore(counterReducer);
    
  4. 提供 Store

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux';
    import store from './store';
    import App from './App';
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
  5. 连接组件

    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    
    const Counter = () => {
      const count = useSelector(state => state.count);
      const dispatch = useDispatch();
    
      const increment = () => dispatch({ type: 'INCREMENT' });
      const decrement = () => dispatch({ type: 'DECREMENT' });
    
      return (
        <div>
          <h1>Count: {count}</h1>
          <button onClick={increment}>Increment</button>
          <button onClick={decrement}>Decrement</button>
        </div>
      );
    };
    
    export default Counter;
    

6. 什么是 Context API?它与 Redux 有什么区别?

Context API 是 React 提供的一种在组件树中传递数据的机制,无需通过 props 逐层传递。

区别

  • 简单性:Context API 更简单,适合小型应用或局部状态管理。
  • 性能:Context API 在状态变化时会重新渲染所有订阅的组件,可能会影响性能。
  • 功能:Redux 功能更强大,适合大型应用和全局状态管理,提供了更多的工具和中间件支持。

示例

import React, { createContext, useContext, useState } from 'react';

// 创建 Context
const ThemeContext = createContext();

// 提供 Context
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 使用 Context
const App = () => {
  return (
    <ThemeProvider>
      <Header />
      <Content />
    </ThemeProvider>
  );
};

const Header = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <header style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
  );
};

const Content = () => {
  const { theme } = useContext(ThemeContext);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <h1>Welcome to the App</h1>
    </div>
  );
};

7. 什么是 React Query?它解决了什么问题?

React Query 是一个用于管理和缓存数据的库,特别适合处理 API 请求和数据同步。

解决问题

  • 数据缓存:自动缓存 API 响应,减少不必要的网络请求。
  • 数据同步:自动处理数据的加载、更新和错误处理。
  • 简化代码:提供简单的 API,减少数据管理的样板代码。
  • 性能优化:通过缓存和优化请求,提高应用性能。

示例

import React from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';

const fetchUser = async (id) => {
  const response = await axios.get(`https://api.example.com/users/${id}`);
  return response.data;
};

const User = ({ userId }) => {
  const { data, error, isLoading } = useQuery(['user', userId], () => fetchUser(userId));

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>User: {data.name}</h1>
      <p>Email: {data.email}</p>
    </div>
  );
};

export default User;

生命周期

1. 请列出 React 组件的生命周期方法,并简要说明每个方法的作用。

React 组件的生命周期可以分为三个阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。以下是各个生命周期方法及其作用:

挂载阶段(Mounting)
  1. constructor(props)

    • 作用:初始化组件的 state 和绑定事件处理器。
    • 调用时机:组件实例被创建时。
  2. static getDerivedStateFromProps(props, state)

    • 作用:在组件实例被创建和更新时调用,用于根据 props 更新 state。
    • 调用时机:组件实例被创建和每次更新之前。
  3. render()

    • 作用:返回组件的 JSX,描述 UI 的结构。
    • 调用时机:组件实例被创建和每次更新时。
  4. componentDidMount()

    • 作用:组件挂载完成后调用,通常用于发起网络请求、设置定时器等。
    • 调用时机:组件首次渲染到 DOM 后。
更新阶段(Updating)
  1. static getDerivedStateFromProps(props, state)

    • 作用:在组件更新时调用,用于根据新的 props 更新 state。
    • 调用时机:组件接收到新 props 或 state 变化时。
  2. shouldComponentUpdate(nextProps, nextState)

    • 作用:决定组件是否需要重新渲染,默认返回 true
    • 调用时机:组件接收到新 props 或 state 变化时。
  3. render()

    • 作用:返回组件的 JSX,描述 UI 的结构。
    • 调用时机:组件实例被创建和每次更新时。
  4. getSnapshotBeforeUpdate(prevProps, prevState)

    • 作用:在组件更新前捕获一些信息,这些信息可以在 componentDidUpdate 中使用。
    • 调用时机:组件更新前。
  5. componentDidUpdate(prevProps, prevState, snapshot)

    • 作用:组件更新完成后调用,通常用于更新 DOM 或发起网络请求。
    • 调用时机:组件更新后。
卸载阶段(Unmounting)
  1. componentWillUnmount()

    • 作用:组件卸载前调用,通常用于清理工作,如取消网络请求、清除定时器等。
    • 调用时机:组件从 DOM 中移除前。

2. 什么是 React 的新生命周期方法?它们取代了哪些旧方法?

React 16.3 引入了一些新的生命周期方法,以解决旧方法的一些问题,并提高组件的可预测性和安全性。以下是新生命周期方法及其取代的旧方法:

  1. static getDerivedStateFromProps(props, state)

    • 取代componentWillReceiveProps
    • 作用:在组件实例被创建和更新时调用,用于根据 props 更新 state。
  2. getSnapshotBeforeUpdate(prevProps, prevState)

    • 取代componentWillUpdate
    • 作用:在组件更新前捕获一些信息,这些信息可以在 componentDidUpdate 中使用。
  3. componentDidCatch(error, info)

    • 新增:用于捕获和处理组件树中的错误。
    • 作用:在组件树中捕获错误时调用,可以用于记录错误或显示错误页面。

3. 什么是 getDerivedStateFromProps?它的作用是什么?

getDerivedStateFromProps 是一个静态方法,用于在组件实例被创建和更新时根据 props 更新 state。它是一个纯函数,不能执行副作用操作。

作用

  • 同步更新 state:确保组件的 state 始终与 props 保持一致。
  • 避免不必要的更新:通过返回 null,可以避免不必要的 state 更新。

示例

class MyComponent extends React.Component {
  static getDerivedStateFromProps(props, state) {
    if (props.counter !== state.prevCounter) {
      return { prevCounter: props.counter, hasChanged: true };
    }
    return null;
  }

  render() {
    return (
      <div>
        <p>Counter: {this.props.counter}</p>
        {this.state.hasChanged && <p>Counter has changed!</p>}
      </div>
    );
  }
}

4. 什么是 getSnapshotBeforeUpdate?它的作用是什么?

getSnapshotBeforeUpdate 是一个生命周期方法,用于在组件更新前捕获一些信息,这些信息可以在 componentDidUpdate 中使用。它通常用于捕获 DOM 的当前状态。

作用

  • 捕获 DOM 状态:在组件更新前捕获 DOM 的当前状态,以便在更新后进行比较或处理。
  • 避免闪烁:确保在更新后能够平滑地过渡到新的状态。

示例

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>
        {this.props.list.map(item => (
          <div key={item.id}>{item.name}</div>
        ))}
      </div>
    );
  }
}

5. 什么是 componentDidCatch?它的作用是什么?

componentDidCatch 是一个生命周期方法,用于捕获和处理组件树中的错误。它类似于 JavaScript 中的 try...catch 语句,但作用于 React 组件。

作用

  • 捕获错误:在组件树中捕获任何未捕获的错误。
  • 记录错误:可以用于记录错误信息,发送错误报告等。
  • 显示错误页面:可以用于显示友好的错误页面,提升用户体验。

示例

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null, info: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, info) {
    this.setState({ error, info });
    // 可以在这里记录错误信息
    console.error("Caught an error:", error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

const App = () => (
  <ErrorBoundary>
    <MyComponent />
  </ErrorBoundary>
);

Hooks

1. 什么是 Hooks?它们解决了什么问题?

Hooks 是 React 16.8 引入的新特性,允许你在不编写类组件的情况下使用状态和其他 React 特性。Hooks 使得函数组件可以拥有状态和生命周期方法,从而提高了代码的可读性和可维护性。

解决的问题

  • 状态管理:在函数组件中管理状态,而不需要转换为类组件。
  • 生命周期方法:在函数组件中使用生命周期方法,而不需要编写复杂的类组件。
  • 逻辑复用:通过自定义 Hooks 复用组件逻辑,提高代码复用性。
  • 代码简洁:使得函数组件的代码更加简洁和易读。

2. 请列举并简要说明 React 中常用的 Hooks。

常用 Hooks

  • useState:用于在函数组件中添加状态。
  • useEffect:用于在函数组件中执行副作用操作,如数据获取、订阅或手动更改 DOM。
  • useContext:用于在函数组件中访问 React 的 Context。
  • useReducer:用于在函数组件中管理复杂的状态逻辑。
  • useMemo:用于 memoize 计算结果,避免不必要的计算。
  • useCallback:用于 memoize 回调函数,避免不必要的重新渲染。
  • useRef:用于在函数组件中创建一个可变的引用对象。
  • useImperativeHandle:用于自定义暴露给父组件的实例值。
  • useLayoutEffect:与 useEffect 类似,但在所有的 DOM 变更之后同步调用。
  • useDebugValue:用于在 React DevTools 中显示自定义 Hooks 的标签。

3. 什么是 useState?它如何工作?

useState 是一个 Hook,用于在函数组件中添加状态。

工作原理

  • 初始化状态:第一次调用 useState 时,传入的初始值会被用作初始状态。
  • 更新状态:返回一个数组,第一个元素是当前状态,第二个元素是一个用于更新状态的函数。

示例

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

4. 什么是 useEffect?它如何工作?

useEffect 是一个 Hook,用于在函数组件中执行副作用操作,如数据获取、订阅或手动更改 DOM。

工作原理

  • 执行副作用:在组件挂载和更新时执行副作用操作。
  • 清理副作用:返回一个可选的清理函数,用于在组件卸载或下次执行副作用前清理上一次的副作用。

示例

import React, { useState, useEffect } from 'react';

const DataFetcher = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('/api/data');
      const json = await response.json();
      setData(json);
    };

    fetchData();

    // 清理函数
    return () => {
      console.log('Cleanup');
    };
  }, []); // 依赖数组为空,表示仅在组件挂载时执行

  return (
    <div>
      {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}
    </div>
  );
};

export default DataFetcher;

5. 什么是 useContext?它如何工作?

useContext 是一个 Hook,用于在函数组件中访问 React 的 Context。

工作原理

  • 访问 Context:传入一个 Context 对象,返回该 Context 的当前值。
  • 订阅变化:当 Context 的值发生变化时,使用 useContext 的组件会重新渲染。

示例

import React, { createContext, useContext } from 'react';

// 创建 Context
const ThemeContext = createContext();

// 提供 Context
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = React.useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 使用 Context
const App = () => {
  return (
    <ThemeProvider>
      <Header />
      <Content />
    </ThemeProvider>
  );
};

const Header = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <header style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
  );
};

const Content = () => {
  const { theme } = useContext(ThemeContext);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <h1>Welcome to the App</h1>
    </div>
  );
};

6. 什么是 useReducer?它如何工作?

useReducer 是一个 Hook,用于在函数组件中管理复杂的状态逻辑。

工作原理

  • 定义 Reducer:传入一个 reducer 函数,该函数接收当前状态和一个 action,返回新的状态。
  • 初始化状态:传入初始状态或一个初始化函数。
  • 派发 Action:返回一个数组,第一个元素是当前状态,第二个元素是一个用于派发 action 的函数。

示例

import React, { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const increment = () => {
    dispatch({ type: 'increment' });
  };

  const decrement = () => {
    dispatch({ type: 'decrement' });
  };

  return (
    <div>
      <h1>Count: {state.count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

7. 什么是 useMemouseCallback?它们的作用是什么?

useMemo

  • 作用:用于 memoize 计算结果,避免不必要的计算。
  • 工作原理:传入一个计算函数和一个依赖数组,只有当依赖数组中的值发生变化时,才会重新计算。

示例

import React, { useMemo } from 'react';

const ExpensiveComponent = ({ count }) => {
  const expensiveCalculation = useMemo(() => {
    console.log('Performing expensive calculation');
    return count * 1000;
  }, [count]);

  return <div>Expensive Calculation Result: {expensiveCalculation}</div>;
};

export default ExpensiveComponent;

useCallback

  • 作用:用于 memoize 回调函数,避免不必要的重新渲染。
  • 工作原理:传入一个回调函数和一个依赖数组,只有当依赖数组中的值发生变化时,才会返回一个新的回调函数。

示例

import React, { useCallback, useState } from 'react';

const ChildComponent = ({ onIncrement }) => {
  return <button onClick={onIncrement}>Increment</button>;
};

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      <h1>Count: {count}</h1>
      <ChildComponent onIncrement={increment} />
    </div>
  );
};

export default ParentComponent;

8. 什么是 useRef?它如何工作?

useRef 是一个 Hook,用于在函数组件中创建一个可变的引用对象。

工作原理

  • 创建引用:返回一个可变的引用对象,其 .current 属性可以保存任何值。
  • 持久化值:引用对象在组件的整个生命周期内保持不变,可以用于保存 DOM 节点、计时器等。

示例

import React, { useRef } from 'react';

const FocusInput = () => {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
};

export default FocusInput;

9. 什么是自定义 Hooks?请给出一个实际的例子。

自定义 Hooks 是一种将逻辑提取到可重用函数中的方法。自定义 Hooks 以 use 开头,可以组合多个内置 Hooks,封装复杂逻辑。

示例: 假设我们需要一个自定义 Hook 来处理 API 请求。

import { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const json = await response.json();
        setData(json);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

const DataFetcher = () => {
  const { data, loading, error } = useFetch('/api/data');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default DataFetcher;

性能优化

1. 什么是 React 的性能优化技术?请列举并简要说明。

React 提供了多种性能优化技术,以提高应用的响应速度和用户体验。以下是一些常见的性能优化技术:

  1. Key

    • 作用:在列表渲染中,为每个列表项提供一个唯一的标识符,帮助 React 识别哪些项发生了变化、添加或删除。
    • 好处:提高虚拟 DOM 的 diff 效率,减少不必要的重新渲染。
  2. Code Splitting

    • 作用:将应用的代码分割成多个小块,按需加载。
    • 好处:减少初始加载时间,提高首屏渲染速度。
  3. Lazy Loading

    • 作用:延迟加载组件,直到需要时才加载。
    • 好处:减少初始加载时间,提高应用性能。
  4. Memoization

    • 作用:缓存计算结果,避免不必要的重复计算。
    • 好处:提高组件的渲染性能,减少计算开销。
  5. React.memo

    • 作用:对函数组件进行浅比较,如果 props 没有变化,则跳过重新渲染。
    • 好处:减少不必要的重新渲染,提高性能。
  6. ShouldComponentUpdate

    • 作用:在类组件中,通过返回布尔值来决定组件是否需要重新渲染。
    • 好处:减少不必要的重新渲染,提高性能。
  7. PureComponent

    • 作用:继承自 React.PureComponent 的组件会进行浅比较,如果 props 和 state 没有变化,则跳过重新渲染。
    • 好处:减少不必要的重新渲染,提高性能。
  8. UseCallback 和 useMemo

    • 作用:分别用于 memoize 回调函数和计算结果,避免不必要的重新渲染和计算。
    • 好处:提高组件的渲染性能,减少计算开销。
  9. Profiler API

    • 作用:用于测量应用的性能,找出性能瓶颈。
    • 好处:帮助开发者优化应用性能,提高用户体验。

2. 什么是 Key?它在列表渲染中的作用是什么?

Key 是 React 用于识别列表中每个元素的唯一标识符。在列表渲染中,为每个列表项提供一个唯一的 key 属性,可以帮助 React 识别哪些项发生了变化、添加或删除,从而提高虚拟 DOM 的 diff 效率。

作用

  • 提高性能:通过 key,React 可以更高效地比对和更新列表项,减少不必要的重新渲染。
  • 避免错误:没有 key 时,React 会发出警告,并可能导致意外的行为。

示例

import React from 'react';

const ItemList = ({ items }) => {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
};

const items = [
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Cherry' }
];

export default () => <ItemList items={items} />;

3. 什么是 Code Splitting?如何在 React 中实现 Code Splitting?

Code Splitting 是一种将应用的代码分割成多个小块的技术,按需加载这些代码块,从而减少初始加载时间,提高首屏渲染速度。

实现方法

  • 动态导入:使用 import() 语法动态导入模块。
  • React.lazy 和 Suspense:结合 React.lazySuspense 实现组件的懒加载。

示例

import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = React.lazy(() => import('./components/Home'));
const About = React.lazy(() => import('./components/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
      </Switch>
    </Suspense>
  </Router>
);

export default App;

4. 什么是 Lazy Loading?如何在 React 中实现 Lazy Loading?

Lazy Loading 是一种延迟加载组件的技术,直到需要时才加载。这可以减少初始加载时间,提高应用性能。

实现方法

  • React.lazy:用于声明懒加载的组件。
  • Suspense:用于在组件加载时显示一个 fallback UI。

示例

import React, { lazy, Suspense } from 'react';

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

const App = () => (
  <div>
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  </div>
);

export default App;

5. 什么是 Memoization?如何在 React 中实现 Memoization?

Memoization 是一种优化技术,通过缓存计算结果来避免不必要的重复计算。在 React 中,可以使用 useMemouseCallback 来实现 memoization。

实现方法

  • useMemo:用于 memoize 计算结果。
  • useCallback:用于 memoize 回调函数。

示例

import React, { useMemo, useCallback } from 'react';

const ExpensiveComponent = ({ count }) => {
  const expensiveCalculation = useMemo(() => {
    console.log('Performing expensive calculation');
    return count * 1000;
  }, [count]);

  return <div>Expensive Calculation Result: {expensiveCalculation}</div>;
};

const ParentComponent = () => {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState('');

  const increment = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      <h1>Count: {count}</h1>
      <input type="text" value={text} onChange={e => setText(e.target.value)} />
      <ExpensiveComponent count={count} />
      <button onClick={increment}>Increment</button>
    </div>
  );
};

export default ParentComponent;

路由

1. 什么是 React Router?它的主要特点是什么?

React Router 是一个用于 React 应用的路由库,它允许你在单页应用(SPA)中实现多页面的导航和路由管理。

主要特点

  • 声明式路由:使用声明式的方式来定义路由,使代码更加清晰和易于维护。
  • 动态路由匹配:支持动态参数匹配,可以根据 URL 参数动态加载不同的组件。
  • 嵌套路由:支持嵌套路由,可以轻松实现多级嵌套的页面结构。
  • 编程式导航:提供编程式导航的方法,可以在代码中控制页面的跳转。
  • 路由守卫:支持路由守卫,可以在路由切换前后执行特定的逻辑。
  • 懒加载:支持代码分割和懒加载,可以按需加载组件,提高应用性能。

2. 如何在 React 中实现路由?

在 React 中使用 React Router 实现路由的基本步骤如下:

  1. 安装 React Router

    npm install react-router-dom
    
  2. 引入必要的组件

    jsx
    深色版本
    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    
  3. 配置路由

    import React from 'react';
    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    import Home from './Home';
    import About from './About';
    import Contact from './Contact';
    
    const App = () => (
      <Router>
        <div>
          <nav>
            <ul>
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/contact">Contact</Link></li>
            </ul>
          </nav>
    
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
            <Route path="/contact" component={Contact} />
          </Switch>
        </div>
      </Router>
    );
    
    export default App;
    

3. 什么是 Route 和 Link?它们的作用是什么?

Route

  • 作用:定义一个路由规则,当 URL 匹配时,会渲染对应的组件。

  • 属性

    • path:定义匹配的路径。
    • component:匹配路径时渲染的组件。
    • exact:是否精确匹配路径。
    • render:匹配路径时渲染的函数。
    • children:匹配路径时渲染的子组件。

示例

<Route exact path="/" component={Home} />
<Route path="/about" component={About} />

Link

  • 作用:用于在应用中创建导航链接,点击后会改变 URL 并触发路由匹配。

  • 属性

    • to:目标路径。

示例

<Link to="/">Home</Link>
<Link to="/about">About</Link>

4. 什么是 Switch?它的作用是什么?

Switch

  • 作用:用于包裹多个 Route 组件,确保只有一个 Route 被渲染。当找到第一个匹配的 Route 时,停止匹配后续的 Route
  • 优点:避免多个 Route 同时匹配,确保路由的唯一性。

示例

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/contact" component={Contact} />
</Switch>

5. 如何实现嵌套路由?

嵌套路由是指在一个路由组件中定义子路由,实现多级嵌套的页面结构。

示例

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
import Profile from './Profile';

const App = () => (
  <Router>
    <div>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>

      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/contact" component={Contact} />
      </Switch>
    </div>
  </Router>
);

const Contact = () => (
  <div>
    <h2>Contact</h2>
    <nav>
      <ul>
        <li><Link to="/contact/profile">Profile</Link></li>
      </ul>
    </nav>

    <Switch>
      <Route path="/contact/profile" component={Profile} />
    </Switch>
  </div>
);

export default App;

6. 什么是 Redirect?它的作用是什么?

Redirect

  • 作用:用于在组件中进行重定向,将用户从当前路径重定向到另一个路径。

  • 属性

    • from:当前路径。
    • to:目标路径。
    • push:是否将重定向路径添加到历史记录中。

示例

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';

const App = () => (
  <Router>
    <div>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>

      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/contact" component={Contact} />
        <Redirect from="/old-path" to="/new-path" />
      </Switch>
    </div>
  </Router>
);

export default App;

其他

1. 什么是 TypeScript?如何在 React 中使用 TypeScript?

TypeScript 是一种静态类型的编程语言,它是 JavaScript 的超集,增加了类型系统和编译时检查。TypeScript 可以帮助开发者提前发现类型错误,提高代码质量和可维护性。

如何在 React 中使用 TypeScript

  1. 安装 TypeScript

    npm install typescript @types/react @types/react-dom --save-dev
    
  2. 创建 TypeScript 文件: 将 .js 文件改为 .tsx 文件(对于 React 组件),并在文件顶部添加类型声明。

  3. 配置 tsconfig.json: 在项目根目录下创建 tsconfig.json 文件,配置 TypeScript 编译选项。

    {
      "compilerOptions": {
        "target": "es5",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "noFallthroughCasesInSwitch": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx"
      },
      "include": ["src"]
    }
    
  4. 编写带有类型的 React 组件

    import React from 'react';
    
    interface Props {
      name: string;
    }
    
    const HelloWorld: React.FC<Props> = ({ name }) => {
      return <div>Hello, {name}!</div>;
    };
    
    export default HelloWorld;
    

2. 什么是 Webpack?它的主要功能是什么?

Webpack 是一个模块打包工具,它可以将各种资源(如 JavaScript、CSS、图片等)视为模块,并将其打包成一个或多个 bundle 文件。

主要功能

  • 模块打包:将多个模块打包成一个或多个 bundle 文件。
  • 代码分割:将代码分割成多个小块,按需加载。
  • 加载器(Loaders) :通过加载器处理不同类型的文件(如 CSS、图片等)。
  • 插件(Plugins) :通过插件扩展 Webpack 的功能,如代码压缩、HTML 生成等。
  • 热模块替换(HMR) :在开发过程中实时更新模块,无需刷新页面。

示例配置

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [
    // 插件配置
  ],
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    hot: true
  }
};

3. 什么是 Babel?它的主要功能是什么?

Babel 是一个 JavaScript 编译器,可以将现代 JavaScript 代码转换为向后兼容的版本,使其能够在更广泛的环境中运行。

主要功能

  • 语法转换:将 ES6+ 语法转换为 ES5 语法。
  • 插件系统:通过插件扩展 Babel 的功能,如转换装饰器、类属性等。
  • 预设:预设是一组插件的集合,简化配置过程。

示例配置

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-proposal-class-properties"]
}

4. 什么是 ESLint?它的主要功能是什么?

ESLint 是一个用于识别和报告 JavaScript 代码中模式问题的工具,旨在统一代码风格并发现潜在错误。

主要功能

  • 代码检查:检查代码风格和潜在错误。
  • 规则配置:通过配置文件自定义检查规则。
  • 插件系统:通过插件扩展 ESLint 的功能,如支持 TypeScript、React 等。

示例配置

{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": "eslint:recommended",
  "rules": {
    "indent": ["error", 2],
    "quotes": ["error", "double"],
    "semi": ["error", "always"]
  }
}

5. 什么是 Jest?它的主要功能是什么?

Jest 是一个用于 JavaScript 的测试框架,特别适合 React 应用的单元测试和集成测试。

主要功能

  • 断言库:内置丰富的断言库。
  • 模拟函数:支持函数和模块的模拟。
  • 快照测试:自动生成和比对快照,确保组件输出的一致性。
  • 异步测试:支持 Promise 和 async/await 的异步测试。
  • 覆盖率报告:生成代码覆盖率报告。

示例测试

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  const { getByText } = render(<App />);
  const linkElement = getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

test('button click increments counter', () => {
  const { getByText } = render(<App />);
  const button = getByText('Click me');
  fireEvent.click(button);
  expect(getByText('Count: 1')).toBeInTheDocument();
});

6. 什么是 Enzyme?它的主要功能是什么?

Enzyme 是一个用于 React 组件的单元测试工具,提供了丰富的 API 用于渲染和查询组件。

主要功能

  • 浅渲染:只渲染组件的顶层,不渲染子组件。
  • 完整渲染:渲染整个组件树,适用于集成测试。
  • 静态渲染:将组件渲染为静态 HTML,适用于无 DOM 环境。
  • 选择器:提供丰富的选择器 API,用于查询和操作组件。

示例测试

import React from 'react';
import { shallow } from 'enzyme';
import App from './App';

describe('<App />', () => {
  it('renders without crashing', () => {
    const wrapper = shallow(<App />);
    expect(wrapper.find('h1').text()).toEqual('Hello, world!');
  });

  it('handles button click', () => {
    const wrapper = shallow(<App />);
    wrapper.find('button').simulate('click');
    expect(wrapper.find('p').text()).toEqual('Count: 1');
  });
});

7. 什么是 Storybook?它的主要功能是什么?

Storybook 是一个用于开发和展示 UI 组件的工具,支持 React、Vue、Angular 等多种框架。

主要功能

  • 组件隔离:在隔离的环境中开发和展示组件。
  • 文档生成:自动生成组件文档,展示组件的各种状态和用法。
  • 交互式 playground:提供交互式 playground,方便开发者测试和调试组件。
  • 插件系统:通过插件扩展功能,如添加样式指南、性能测试等。

示例配置

  1. 安装 Storybook

    npx sb init
    
  2. 编写故事

    import React from 'react';
    import { Button } from './Button';
    import { storiesOf } from '@storybook/react';
    
    storiesOf('Button', module)
      .add('with text', () => <Button>Click me</Button>)
      .add('with emoji', () => <Button>😀 😎 👍 💯</Button>);
    

8. 什么是 Server-Side Rendering(SSR)?如何在 React 中实现 SSR?

Server-Side Rendering (SSR) 是一种在服务器端生成 HTML 标记的技术,然后将生成的 HTML 发送到客户端浏览器。这可以提高首屏加载速度,改善 SEO。

如何在 React 中实现 SSR

  1. 安装必要的包

    npm install react react-dom express @loadable/server
    
  2. 创建服务器端渲染文件

    // server.js
    const express = require('express');
    const React = require('react');
    const ReactDOMServer = require('react-dom/server');
    const App = require('./App').default;
    
    const app = express();
    
    app.get('*', (req, res) => {
      const html = ReactDOMServer.renderToString(<App />);
      res.send(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>SSR Example</title>
          </head>
          <body>
            <div id="root">${html}</div>
            <script src="/static/bundle.js"></script>
          </body>
        </html>
      `);
    });
    
    app.listen(3000, () => {
      console.log('Server is running on port 3000');
    });
    
  3. 创建客户端入口文件

    // client.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    
    ReactDOM.hydrate(<App />, document.getElementById('root'));
    

9. 什么是 Static Site Generation(SSG)?如何在 React 中实现 SSG?

Static Site Generation (SSG) 是一种在构建时生成静态 HTML 文件的技术,然后将这些文件部署到静态文件服务器。这可以提高首屏加载速度,降低服务器负载。

如何在 React 中实现 SSG

  1. 使用 Next.js

    • 安装 Next.js

      npx create-next-app my-app
      cd my-app
      
    • 创建静态页面

      // pages/index.js
      import React from 'react';
      
      const Home = () => {
        return <div>Welcome to the Static Site</div>;
      };
      
      export default Home;
      
    • 生成静态文件

      npm run build
      npm start
      
  2. 使用 Gatsby

    • 安装 Gatsby

      npx gatsby-cli new my-app
      cd my-app
      
    • 创建静态页面

      // src/pages/index.js
      import React from 'react';
      
      const Home = () => {
        return <div>Welcome to the Static Site</div>;
      };
      
      export default Home;
      
    • 生成静态文件

      npm run build
      npm run serve
      

Vue 和 React 的区别

Vue 和 React 都是非常流行的前端框架,各有其特点和适用场景。下面详细对比 Vue 和 React 的主要区别:

1. 基本概念

React

  • 定义:React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发和维护。
  • 核心理念:组件化开发,虚拟 DOM,单向数据流。

Vue

  • 定义:Vue 是一个用于构建用户界面的渐进式框架,由尤雨溪开发和维护。
  • 核心理念:组件化开发,虚拟 DOM,双向数据绑定。

2. 学习曲线

React

  • 学习曲线:相对较陡峭,需要了解 JSX、状态管理、生命周期方法等概念。
  • 文档:官方文档详细,但内容较多,初学者可能需要花费更多时间学习。

Vue

  • 学习曲线:较为平缓,API 设计友好,文档清晰,初学者可以快速上手。
  • 文档:官方文档简洁明了,适合快速学习。

3. 模板语法

React

  • 模板语法:使用 JSX,将 JavaScript 和 HTML 结合在一起,需要一定的 JavaScript 基础。

  • 示例

    const App = () => (
      <div>
        <h1>Hello, {name}!</h1>
        <button onClick={() => setName('World')}>Change Name</button>
      </div>
    );
    

Vue

  • 模板语法:使用类似于 HTML 的模板语法,支持指令和插值表达式。

  • 示例

    <template>
      <div>
        <h1>Hello, {{ name }}!</h1>
        <button @click="changeName">Change Name</button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          name: 'Vue'
        };
      },
      methods: {
        changeName() {
          this.name = 'World';
        }
      }
    };
    </script>
    

4. 数据绑定

React

  • 数据绑定:单向数据流,通过 setState 更新状态。

  • 示例

    const [name, setName] = useState('React');
    const handleChange = (event) => {
      setName(event.target.value);
    };
    

Vue

  • 数据绑定:支持双向数据绑定,通过 v-model 实现。

  • 示例

    <template>
      <div>
        <input v-model="name" />
        <p>Name: {{ name }}</p>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          name: 'Vue'
        };
      }
    };
    </script>
    

5. 状态管理

React

  • 状态管理:通常使用 Redux 或 MobX 进行全局状态管理。

  • 示例

    import { useSelector, useDispatch } from 'react-redux';
    
    const App = () => {
      const count = useSelector(state => state.count);
      const dispatch = useDispatch();
    
      const increment = () => {
        dispatch({ type: 'INCREMENT' });
      };
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={increment}>Increment</button>
        </div>
      );
    };
    

Vue

  • 状态管理:通常使用 Vuex 进行全局状态管理。

  • 示例

    <template>
      <div>
        <p>Count: {{ count }}</p>
        <button @click="increment">Increment</button>
      </div>
    </template>
    
    <script>
    import { mapState, mapActions } from 'vuex';
    
    export default {
      computed: {
        ...mapState(['count'])
      },
      methods: {
        ...mapActions(['increment'])
      }
    };
    </script>
    

6. 生态系统

React

  • 生态系统:非常丰富,有大量的第三方库和工具支持,如 Redux、MobX、React Router、Styled Components 等。
  • 社区:活跃度高,社区贡献者众多。

Vue

  • 生态系统:也在不断壮大,有 Vuex、Vue Router、Vuetify 等官方和第三方库支持。
  • 社区:虽然不如 React 社区大,但也非常活跃,特别是在国内有较高的 popularity。

7. 性能

React

  • 性能:React 的虚拟 DOM 和高效的 diff 算法确保了良好的性能。
  • 优化:可以通过代码分割、懒加载、memoization 等技术进一步优化性能。

Vue

  • 性能:Vue 也采用了虚拟 DOM 和高效的 diff 算法,性能表现良好。
  • 优化:通过异步组件、懒加载、keep-alive 等技术优化性能。

8. 适用场景

React

  • 适用场景:适合大型复杂应用,特别是需要高度定制和灵活扩展的应用。
  • 企业级应用:广泛应用于企业级应用,如 Facebook、Instagram 等。

Vue

  • 适用场景:适合中小规模应用,特别是需要快速开发和迭代的应用。
  • 渐进式框架:可以逐步引入,适合现有项目的改造和升级。

9. 社区和支持

React

  • 社区:全球范围内非常活跃,有大量的教程、文档和社区支持。
  • 支持:Facebook 提供官方支持,社区贡献者众多。

Vue

  • 社区:虽然不如 React 社区大,但也在快速增长,特别是在国内有较高的 popularity。
  • 支持:官方文档详细,社区活跃,有许多中文资源。

总结

  • React 更适合大型复杂应用,需要高度定制和灵活扩展的场景。
  • Vue 更适合中小规模应用,需要快速开发和迭代的场景。

在这里插入图片描述

;