2023.04.02 更新前端面试问题总结(7道题)

222 阅读10分钟

2023.03.29 - 2023.04.02 更新前端面试问题总结(7道题)
获取更多面试问题可以访问
github 地址: github.com/pro-collect…
gitee 地址: gitee.com/yanleweb/in…

目录:

  • 中级开发者相关问题【共计 2 道题】

    • 240.[Redux] 的存储过程【web框架】【出题公司: 腾讯】
    • 245.react 中是如何实现 下拉菜单场景, 点击区域外关闭下拉组件?【web应用场景】【出题公司: 百度】
  • 高级开发者相关问题【共计 4 道题】

    • 238.如何保证用户的使用体验【web应用场景】【出题公司: 腾讯】
    • 239.如何在前端团队快速落地代码规范【工程化】【出题公司: 腾讯】
    • 241.[Redux] 简单实现一下核心源码【web框架】【出题公司: 腾讯】
    • 243.[webpack] webpack 是如何给 web 应用注入环境变量的, 原理是啥【工程化】【出题公司: 百度】
  • 资深开发者相关问题【共计 1 道题】

    • 242.[Redux] react-redux 是如何更新到 UI 的, 写一下这部分的核心源码【web框架】【出题公司: 腾讯】

中级开发者相关问题【共计 2 道题】

240.[Redux] 的存储过程【web框架】【出题公司: 腾讯】

Redux 的存储过程可以简单地分为以下几个步骤:

  1. Action Creator 函数被调用,生成一个 Action 对象;
  2. Action 对象被传递给 Store.dispatch() 方法;
  3. Redux Store 调用 Reducer 函数,将当前的 State 和 Action 作为参数传入;
  4. Reducer 函数根据 Action 的类型,生成一个新的 State;
  5. Redux Store 将新的 State 存储下来,用于下一次的状态更新;
  6. 组件通过调用 Store.subscribe() 方法,监听 Store 中 State 的变化;
  7. 当 State 发生变化时,Store 会通知所有的订阅者,订阅者会重新渲染相应的组件。

这个过程可以简单地描述为:Action -> Reducer -> Store -> View。其中,Action 是一个纯对象,它描述了发生的事件;Reducer 是一个纯函数,它接收当前的 State 和 Action,返回一个新的 State;Store 是将 Action 和 Reducer 结合起来的对象,它维护了应用程序的 State;View 则是 React 组件,它通过 Store.subscribe() 方法监听 State 的变化,根据 State 的变化重新渲染页面。

245.react 中是如何实现 下拉菜单场景, 点击区域外关闭下拉组件?【web应用场景】【出题公司: 百度】

在 React 中,要实现点击区域外关闭下拉组件,一般可以使用以下几种方法:

  1. 在下拉组件的根元素上监听点击事件,当点击区域不在下拉组件内时,触发关闭下拉组件的操作。这可以通过添加全局点击事件,然后在事件处理程序中判断点击区域是否在下拉组件内来实现。具体实现如下:
import React, { useRef, useEffect } from 'react';

function DropdownMenu(props) {
  const menuRef = useRef(null);

  useEffect(() => {
    function handleClickOutside(event) {
      if (menuRef.current && !menuRef.current.contains(event.target)) {
        props.onClose();
      }
    }

    document.addEventListener('click', handleClickOutside);
    return () => {
      document.removeEventListener('click', handleClickOutside);
    };
  }, [props]);

  return (
    <div ref={menuRef}>
      {/* 下拉菜单内容 */}
    </div>
  );
}
  1. 在下拉组件的父元素上监听点击事件,当点击区域不在下拉组件及其父元素内时,触发关闭下拉组件的操作。具体实现如下:
import React, { useState } from 'react';

function Dropdown(props) {
  const [isOpen, setIsOpen] = useState(false);

  function toggleDropdown() {
    setIsOpen(!isOpen);
  }

  function handleClickOutside(event) {
    if (!event.target.closest('.dropdown')) {
      setIsOpen(false);
    }
  }

  return (
    <div className="dropdown" onClick={handleClickOutside}>
      <button onClick={toggleDropdown}>Toggle Dropdown</button>
      {isOpen && <DropdownMenu onClose={() => setIsOpen(false)} />}
    </div>
  );
}

在上述代码中,我们在 Dropdown 组件的根元素上添加了点击事件处理程序 handleClickOutside,当点击区域不在 .dropdown 元素内时,触发关闭下拉组件的操作。由于 DropdownMenu 组件位于 Dropdown 组件内部,因此当点击下拉菜单时,事件会冒泡到 Dropdown 组件,从而不会触发关闭操作。

  1. 除了上述方法外,还可以使用 useRef 钩子来监听鼠标点击事件。具体实现可以在下拉组件的根元素上使用 ref 属性来获取 DOM 元素的引用,然后在组件挂载时使用 addEventListener 方法绑定 mousedown 事件,最后在事件处理函数中判断鼠标点击的位置是否在下拉组件内,如果不在,则关闭下拉组件。

示例代码如下:

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

function Dropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef(null);

  useEffect(() => {
    function handleClickOutside(event) {
      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
        setIsOpen(false);
      }
    }

    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [dropdownRef]);

  return (
    <div ref={dropdownRef}>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle Dropdown</button>
      {isOpen && (
        <ul>
          <li>Option 1</li>
          <li>Option 2</li>
          <li>Option 3</li>
        </ul>
      )}
    </div>
  );
}

这种方法可以在组件内部处理点击事件,不需要将事件处理函数传递给父组件。但是相对而言代码会比较繁琐,需要手动处理事件绑定和解绑。

高级开发者相关问题【共计 4 道题】

238.如何保证用户的使用体验【web应用场景】【出题公司: 腾讯】

【如何保证用户的使用体验】这个也是一个较为复杂的话题, 这个也不是问题了, 这个算是话题吧;

主要从以下几个方面思考问题:

  1. 性能方向的思考
  2. 用户线上问题反馈,线上 on call 的思考
  3. 用户使用体验的思考, 交互体验使用方向
  4. 提升用户能效方向思考

239.如何在前端团队快速落地代码规范【工程化】【出题公司: 腾讯】

// todo 待整理

241.[Redux] 简单实现一下核心源码【web框架】【出题公司: 腾讯】

实现 Redux 的源码主要包括以下几个步骤:

  1. 实现 createStore 函数,创建 store 对象,该函数接收一个 reducer 函数作为参数,返回一个对象。
  2. 在 createStore 函数内部,定义一个 state 变量来存储当前的状态值,定义一个 listeners 数组来存储所有的监听函数。
  3. 实现 getState 方法,返回当前的状态值。
  4. 实现 dispatch 方法,接收一个 action 对象作为参数,将当前的状态值和 action 对象传给 reducer 函数,更新状态值。然后遍历 listeners 数组,调用所有的监听函数。
  5. 实现 subscribe 方法,接收一个监听函数作为参数,将该函数添加到 listeners 数组中,以便在状态更新时调用。
  6. 实现 combineReducers 函数,将多个 reducer 函数合并成一个 reducer 函数。
  7. 在 createStore 函数内部,将传入的 reducer 函数或者合并后的 reducer 函数赋值给一个内部的 currentReducer 变量。
  8. 在 dispatch 方法内部,将 currentReducer 赋值给一个局部变量,以保证在 reducer 函数中调用 dispatch 方法时可以获取到最新的 reducer 函数。

下面是代码实现:

// 实现 createStore 函数
function createStore(reducer) {
  let state;
  const listeners = [];

  function getState() {
    return state;
  }

  function dispatch(action) {
    state = reducer(state, action);
    for (let i = 0; i < listeners.length; i++) {
      listeners[i]();
    }
  }

  function subscribe(listener) {
    listeners.push(listener);
  }

  dispatch({});

  return {
    getState,
    dispatch,
    subscribe
  };
}

// 实现 combineReducers 函数
function combineReducers(reducers) {
  return function(state = {}, action) {
    const nextState = {};
    for (const key in reducers) {
      nextState[key] = reducers[key](state[key], action);
    }
    return nextState;
  };
}

在使用时,可以先定义 reducer 函数:

function todos(state = [], action) {
  switch (action.type) {
    case "ADD_TODO":
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case "TOGGLE_TODO":
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
}

function visibilityFilter(state = "SHOW_ALL", action) {
  switch (action.type) {
    case "SET_VISIBILITY_FILTER":
      return action.filter;
    default:
      return state;
  }
}

然后将它们传入 combineReducers 函数,创建一个 reducer 函数:

const reducer = combineReducers({
  todos,
  visibilityFilter
});

最后调用 createStore 函数,创建一个 store 对象:

const store = createStore(reducer);

现在就可以使用 store 对象来获取状态值、派发 action、监听状态变化了。

可以参考文档: juejin.cn/post/684490…

243.[webpack] webpack 是如何给 web 应用注入环境变量的, 原理是啥【工程化】【出题公司: 百度】

Webpack 可以通过 DefinePlugin 插件给 web 应用注入环境变量。该插件会在编译过程中替换掉代码中指定的变量,以实现在运行时替换成环境变量的值。

在 webpack 的配置文件中,需要先引入该插件,然后将需要注入的环境变量通过该插件进行配置。例如:

const webpack = require('webpack');

module.exports = {
  // 其他配置
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      },
      API_URL: JSON.stringify('http://api.example.com')
    })
  ]
};

上述配置中,定义了两个需要注入的变量,分别是 process.env.NODE_ENV 和 API_URL。其中,process.env.NODE_ENV 是一个常用的环境变量,用来标识当前是开发环境还是生产环境;API_URL 是一个自定义的环境变量,用来存储 API 的地址。

在代码中使用这些环境变量时,只需要直接引用即可:

if (process.env.NODE_ENV === 'production') {
  console.log('当前为生产环境');
}

fetch(API_URL + '/users')
  .then(response => response.json())
  .then(data => console.log(data));

Webpack 在编译时会将这些变量替换成对应的值,例如:

if ('production' === 'production') {
  console.log('当前为生产环境');
}

fetch('http://api.example.com' + '/users')
  .then(response => response.json())
  .then(data => console.log(data));

通过这种方式,我们就可以在代码中方便地使用环境变量,同时保证了在不同环境下都能正确地使用相应的变量值。

资深开发者相关问题【共计 1 道题】

242.[Redux] react-redux 是如何更新到 UI 的, 写一下这部分的核心源码【web框架】【出题公司: 腾讯】

react-redux 和 redux 的关系

Redux 和 React-Redux 是两个独立的库,但它们通常一起使用,因为 Redux 库本身并不针对 React,而是一个通用的状态管理库,而 React-Redux 则是一个用于将 Redux 集成到 React 应用程序中的库。

React-Redux 提供了一组 React 组件和钩子,使得在 React 应用程序中使用 Redux 变得更加容易。它提供了一个 Provider 组件,可以将 Redux store 注入到整个 React 应用程序中,并且可以使用 connect 函数将组件连接到 Redux store,使它们可以访问和修改 store 中的数据。

使用 React-Redux,我们可以将 Redux 的状态管理能力与 React 的组件化能力结合起来,使得应用程序的状态可以很方便地被管理和共享,并且组件也可以方便地访问和修改应用程序的状态。

react-redux 是如何集成到 UI 的?

react-redux 提供了两个主要的组件 Provider 和 connect,它们用于将 Redux 状态管理与 React 组件相结合。

首先,使用 Provider 组件将 Redux store 传递给整个应用程序。可以将 <Provider> 组件作为最高层的组件,这样在应用程序中的所有组件中都可以访问到 Redux store。

下一步,使用 connect 函数连接 Redux store 和组件。connect 函数是一个高阶函数,它接收两个参数:mapStateToProps 和 mapDispatchToProps,并返回另一个函数,这个函数接受一个组件作为参数,并返回一个增强版的组件。

mapStateToProps 函数用于从 Redux store 中获取需要的 state 数据,并将其映射到组件的 props 上。mapDispatchToProps 函数用于将 action creator 映射到组件的 props 上,这样组件就可以直接调用 action creator 发起 action,而不需要手动分发 dispatch。

使用 connect 函数生成的增强版组件可以访问到 Redux store 中的 state 和 dispatch,并将它们作为 props 传递给原始组件。在组件中,可以直接使用这些 props 来获取和更新 state,以及发起 action。当组件中的 state 或 props 发生变化时,connect 函数会自动更新组件,以反映最新的 state 和 props。

通过这种方式,react-redux 让我们可以在 React 组件中使用 Redux 进行状态管理,实现了 Redux 和 React 的无缝集成。

简单写一下更新 UI 核心代码实现

react-redux 是基于 React 和 Redux 的,主要用于将 Redux 的状态管理功能集成到 React 应用程序中。它主要包括两个部分:Provider 和 connect。

Provider 组件是 react-redux 的核心,它将 Redux 的 store 作为 props 传递给 React 组件,并通过 React 的上下文(Context)使得后代组件能够访问到 store。

connect 函数用于连接 React 组件与 Redux store,返回一个新的组件。该函数的主要作用是在组件中提供 mapStateToProps 和 mapDispatchToProps 函数,从而使组件能够从 Redux store 中读取数据,并向 store 分发 action。

下面是一个简单的实现,用于说明 react-redux 是如何集成到 UI 的:

// Provider.js
import React from 'react';
import PropTypes from 'prop-types';

export const StoreContext = React.createContext();

export default function Provider(props) {
  const { store, children } = props;
  return (
    <StoreContext.Provider value={store}>
      {children}
    </StoreContext.Provider>
  );
}

Provider.propTypes = {
  store: PropTypes.object.isRequired,
  children: PropTypes.any,
};

// connect.js
import React from 'react';
import PropTypes from 'prop-types';
import { StoreContext } from './Provider';

export default function connect(mapStateToProps, mapDispatchToProps) {
  return function wrapWithConnect(WrappedComponent) {
    class Connect extends React.Component {
      componentDidMount() {
        const { store } = this.context;
        this.unsubscribe = store.subscribe(this.handleChange.bind(this));
      }

      componentWillUnmount() {
        this.unsubscribe();
      }

      handleChange() {
        this.forceUpdate();
      }

      render() {
        const { store } = this.context;
        const props = {
          ...this.props,
          ...mapStateToProps(store.getState(), this.props),
          ...mapDispatchToProps(store.dispatch, this.props),
        };
        return <WrappedComponent {...props} />;
      }
    }

    Connect.contextType = StoreContext;
    Connect.propTypes = {
      store: PropTypes.object,
    };
    Connect.displayName = `Connect(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;

    return Connect;
  };
}

这里的 Provider 组件用于将 Redux 的 store 传递给 React 组件:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './App';
import rootReducer from './reducers';

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

而 connect 函数用于将 React 组件连接到 Redux store:

import React from 'react';
import { connect } from 'react-redux';
import { increment } from './actions';

function Counter(props) {
  const { count, increment } = props;
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => increment()}>+</button>
    </div>
  );
}

const mapStateToProps = state => {
  return {
    count: state.count,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch(increment()),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);