likes
comments
collection
share

前端面试第 67 期 - 2024.09.28 更新前端面试问题总结(20 道题)前端面试第 67 期(20 道题) 获

作者站长头像
站长
· 阅读数 16

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

目录:

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

      1. 如何限制 input 框只能输入正整数【热度: 230】【web 应用场景】【出题公司: 百度】
      1. localStorage 是同步还是异步【热度: 210】【web 应用场景】【出题公司: 百度】
  • 中级开发者相关问题【共计 6 道题】

      1. 手写实现 lodash.flattenDeep 将 array 递归为一维数组【热度: 345】【web 应用场景】【出题公司: 小米】
      1. 防止对象被篡改有哪些方式【热度: 260】【JavaScript】【出题公司: 阿里巴巴】
      1. axios 有哪些特性【热度: 147】【网络、web 应用场景】
      1. 数组里面有 10 万个数据,取第一个元素和第 10 万个元素的时间相差多少【热度: 775】【JavaScript】
      1. 想设置 axios 全局通用配置, 有哪些方法【热度: 298】【网络、web 应用场景】
      1. 给 axios 做一个通用拦截器,实现功能为状态码非 200 的时候,统一拦截错误, 提示一个 toast, 该如何实现【热度: 339】【网络、web 应用场景】
  • 高级开发者相关问题【共计 12 道题】

    • 936. [Vue] 对比一下 vuex 和 Pinia 两个状态库【热度: 300】【web 框架】【出题公司: 小米】
      1. 动画性能如何检测【热度: 262】【web 应用场景】
      1. [webpack] 发中使用的是 esm 和 webpack, 是否可以直接调用 commonjs 模块化的三方包【热度: 359】【工程化】【出题公司: 阿里巴巴】
      1. axios 支持哪些常用的配置【热度: 196】【网络、web 应用场景】
      1. 解释下 axios withCredentials 配置【热度: 197】【网络、web 应用场景】
      1. axios 如何注销拦截器【热度: 140】【网络、web 应用场景】
      1. axios 如何取消请求【热度: 218】【网络、web 应用场景】
      1. 在 forEach 中和 for 循环 中调用异步函数的区别【热度: 371】【JavaScript、web 应用场景】
      1. mobx 和 redux 有什么区别【热度: 277】【web 应用场景】【出题公司: 阿里巴巴】
      1. [webpack] tree-shaking 原理【热度: 722】【工程化】【出题公司: 腾讯】
      1. 前端应用有哪些代码测试手段【热度: 385】【web 应用场景】【出题公司: 腾讯】
      1. 单元测试、E2E 测试有和区别?【热度: 258】【web 应用场景】【出题公司: 腾讯】

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

937. 如何限制 input 框只能输入正整数【热度: 230】【web 应用场景】【出题公司: 百度】

在前端,可以通过以下几种方式来限制 <input> 框只能输入正整数:

一、使用 HTML5 的输入类型和属性

  1. 使用 type="number" 属性: <input type="number"> 设置输入框的类型为数字。然而,这并不能完全确保只输入正整数,因为用户仍然可以输入小数。

  2. 添加 step="1"min="0" 属性: <input type="number" step="1" min="0">step="1" 确保输入的步长为 1,即只能输入整数。min="0" 限制输入值不能小于 0。

    这种方法在大多数现代浏览器中能提供一定程度的限制,但用户仍然可以通过浏览器的开发者工具等方式绕过这些限制。

二、使用 JavaScript 进行实时验证

  1. 使用 oninput 事件处理函数:

    <input id="myInput" />
    
    const inputElement = document.getElementById("myInput");
    inputElement.oninput = function () {
      // 只保留数字字符
      this.value = this.value.replace(/[^0-9]/g, "");
    };
    

    这个方法在用户输入时实时过滤非数字字符。但是,它不能确保输入的是正整数,只是限制了输入为数字。

  2. 结合正则表达式进行更严格的验证:

    inputElement.oninput = function () {
      const value = this.value;
      if (!/^\d+$/.test(value)) {
        // 如果输入不是正整数,清空输入框
        this.value = "";
      }
    };
    

    这里使用正则表达式 /^\d+$/ 来检查输入是否只包含一个或多个数字。如果输入不符合要求,就清空输入框。

938. localStorage 是同步还是异步【热度: 210】【web 应用场景】【出题公司: 百度】

在大多数现代浏览器中,localStorage的操作是同步的。

当你使用localStorage.setItem()来存储数据或者localStorage.getItem()来获取数据时,这些操作会立即执行并且不会返回一个 Promise 或者使用回调函数来处理异步操作。

例如:

localStorage.setItem("key", "value");
console.log(localStorage.getItem("key"));

在上面的代码中,设置和获取localStorage中的数据的操作会按顺序立即执行,不会像异步操作那样需要等待一段时间后再执行后续代码。

然而,需要注意的是,虽然localStorage操作本身是同步的,但如果存储的数据量较大,可能会导致性能问题,因为这些操作会阻塞浏览器的主线程。在这种情况下,可能会感觉操作像是异步的,因为浏览器可能会出现卡顿或响应缓慢的情况。

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

939. 手写实现 lodash.flattenDeep 将 array 递归为一维数组【热度: 345】【web 应用场景】【出题公司: 小米】

关键词:lodash.flattenDeep 实现

以下是用 JavaScript 手写实现类似于 lodash.flattenDeep 的函数来将数组递归展平为一维数组:

function flattenDeep(arr) {
  let result = [];
  for (let item of arr) {
    if (Array.isArray(item)) {
      result = result.concat(flattenDeep(item));
    } else {
      result.push(item);
    }
  }
  return result;
}

你可以使用以下方式测试这个函数:

const nestedArray = [1, [2, [3, [4]]]];
const flattenedArray = flattenDeep(nestedArray);
console.log(flattenedArray); // [1, 2, 3, 4]

940. 防止对象被篡改有哪些方式【热度: 260】【JavaScript】【出题公司: 阿里巴巴】

关键词:防止对象篡改

在 JavaScript 中,可以通过以下几种方式防止对象被篡改:

一、使用Object.freeze()

  1. 功能:

    • 完全冻结一个对象,使其不能添加新属性、删除现有属性或修改现有属性的值。
    • 对嵌套对象也会进行深度冻结。
  2. 示例:

    const obj = {
      prop1: "value1",
      prop2: { nestedProp: "nestedValue" },
    };
    Object.freeze(obj);
    
    // 以下操作都会抛出错误
    obj.newProp = "newValue";
    delete obj.prop1;
    obj.prop1 = "newValue1";
    obj.prop2.nestedProp = "newNestedValue";
    

二、使用Object.seal()

  1. 功能:

    • 密封一个对象,阻止添加新属性和删除现有属性,但可以修改现有属性的值。
    • 对嵌套对象不进行深度密封。
  2. 示例:

    const obj = {
      prop1: "value1",
      prop2: { nestedProp: "nestedValue" },
    };
    Object.seal(obj);
    
    // 以下操作会抛出错误或不被允许
    obj.newProp = "newValue";
    delete obj.prop1;
    
    // 这个操作是允许的
    obj.prop1 = "newValue1";
    obj.prop2.nestedProp = "newNestedValue";
    

三、使用const声明对象引用

  1. 功能:

    • 使用const声明的变量不能被重新赋值,但对象本身的属性仍然可以被修改,除非使用上述冻结或密封的方法。
  2. 示例:

    const obj = { prop: "value" };
    // 以下操作会报错
    obj = { newProp: "newValue" };
    
    // 这个操作是允许的
    obj.prop = "newValue1";
    

四、使用代理(Proxy)进行拦截

  1. 功能:

    • 通过创建一个代理对象,可以拦截对目标对象的各种操作,如属性访问、赋值、删除等,并根据需要进行控制。
  2. 示例:

    const targetObject = { prop: "value" };
    const handler = {
      set(target, key, value) {
        throw new Error("Object is immutable.");
      },
      deleteProperty(target, key) {
        throw new Error("Object is immutable.");
      },
    };
    const immutableObject = new Proxy(targetObject, handler);
    
    // 以下操作都会抛出错误
    immutableObject.prop = "newValue";
    delete immutableObject.prop;
    

942. axios 有哪些特性【热度: 147】【网络、web 应用场景】

关键词:axios 特性

作者备注

以下特性信息都是直接来自于官网 axios-http.com/docs/intro

但是可能很多同学并不会去好好看官网, 所以这个问题, 只是考察同学们的学习主动性

  • 从浏览器发出 XMLHttpRequests
  • 从 node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 超时
  • 查询参数序列化并支持嵌套条目
  • 自动将请求主体序列化为:
  • JSON ( application/json)
  • 多部分 / 表单数据 ( multipart/form-data)
  • URL 编码形式 ( application/x-www-form-urlencoded)
  • 将 HTML 表单发布为 JSON
  • 响应中的自动 JSON 数据处理
  • 浏览器和 node.js 的进度捕获以及附加信息(速度、剩余时间)
  • 为 node.js 设置带宽限制
  • 兼容符合规范的 FormData 和 Blob (包括 node.js)
  • 客户端支持防范 XSRF

943. 数组里面有 10 万个数据,取第一个元素和第 10 万个元素的时间相差多少【热度: 775】【JavaScript】

关键词:数组访问

在 JavaScript 中,从数组中获取第一个元素和获取最后一个元素(假设这里你说的第 10 万个元素相当于数组的最后一个元素)的时间差异通常非常小,几乎可以忽略不计。

数组在内存中通常是连续存储的,访问数组中的元素可以通过索引快速定位。无论是访问第一个元素还是最后一个元素,时间复杂度都是 O(1),即可以在常数时间内完成。

然而,在实际情况中,可能会有一些微小的差异,这取决于以下因素:

  1. 数组的存储方式和实现细节:不同的 JavaScript 引擎可能在内部实现上有一些差异,但这些差异通常非常小。
  2. 计算机的内存和处理器性能:如果计算机的内存或处理器性能较低,可能会导致一些微小的延迟,但这种延迟不太可能在获取不同位置的元素之间有明显差异。

以下是一个简单的测试示例:

const largeArray = Array.from({ length: 100000 }, (_, index) => index);

console.time("accessFirstElement");
const firstElement = largeArray[0];
console.timeEnd("accessFirstElement");

console.time("accessLastElement");
const lastElement = largeArray[99999];
console.timeEnd("accessLastElement");

在大多数情况下,你会发现这两个操作的时间非常接近,甚至可能在同一毫秒内完成。

947. 想设置 axios 全局通用配置, 有哪些方法【热度: 298】【网络、web 应用场景】

关键词:axios 配置

在 Axios 中,可以通过以下几种方法设置全局通用配置:

一、使用axios.defaults

  1. 直接设置属性:

    • 可以直接在axios.defaults上设置各种配置属性,如baseURLtimeoutheaders等。这些设置将应用于所有后续的 Axios 请求。
    axios.defaults.baseURL = "https://api.example.com";
    axios.defaults.timeout = 5000;
    axios.defaults.headers.common["Authorization"] = "Bearer your_token";
    
  2. 设置transformRequesttransformResponse

    • 可以设置全局的请求和响应数据转换函数。这些函数将在每个请求和响应上被调用,允许对数据进行预处理和后处理。
    axios.defaults.transformRequest = [
      function (data, headers) {
        // 对请求数据进行转换
        return data;
      },
    ];
    
    axios.defaults.transformResponse = [
      function (data) {
        // 对响应数据进行转换
        return data;
      },
    ];
    

二、创建自定义 Axios 实例并设置配置

  1. 创建实例并设置配置:

    • 可以创建一个自定义的 Axios 实例,并在创建时设置特定的配置。这个实例可以有自己独立的配置,与默认的 Axios 实例分开。
    const customAxios = axios.create({
      baseURL: "https://custom-api.example.com",
      timeout: 10000,
      headers: {
        "Custom-Header": "custom-value",
      },
    });
    
  2. 使用自定义实例:

    • 可以在应用中的任何地方使用这个自定义实例来发送请求,它将使用设置的全局配置。
    customAxios
      .get("/data")
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        console.error(error);
      });
    

三、使用插件或中间件(如果适用)

  1. 寻找合适的插件:

    • 有些第三方插件可以帮助你设置 Axios 的全局配置。例如,axios-interceptors插件可以方便地设置请求和响应拦截器,从而实现全局的配置和处理逻辑。
    import axios from "axios";
    import axiosInterceptors from "axios-interceptors";
    
    const axiosInstance = axiosInterceptors(axios);
    
    axiosInstance.useInterceptors({
      request: (config) => {
        // 在请求发送前进行一些处理
        config.headers["Custom-Header"] = "custom-value";
        return config;
      },
      response: (response) => {
        // 在响应返回后进行一些处理
        return response;
      },
    });
    
  2. 自定义中间件:

    • 根据你的应用需求,你也可以创建自己的中间件来设置全局配置。中间件可以在请求发送前或响应返回后进行特定的处理。
    axios.interceptors.request.use(
      (config) => {
        // 全局请求处理逻辑
        return config;
      },
      (error) => {
        return Promise.reject(error);
      }
    );
    
    axios.interceptors.response.use(
      (response) => {
        // 全局响应处理逻辑
        return response;
      },
      (error) => {
        return Promise.reject(error);
      }
    );
    

通过这些方法,你可以灵活地设置 Axios 的全局通用配置,以满足不同的应用需求。同时,要注意合理管理全局配置,避免出现冲突或不可预期的行为。

948. 给 axios 做一个通用拦截器,实现功能为状态码非 200 的时候,统一拦截错误, 提示一个 toast, 该如何实现【热度: 339】【网络、web 应用场景】

关键词:axios 拦截器

作者备注

拦截器是 axios 的最核心功能之一, 该问题只是考察 axios 的核心功能的基本使用

以下是使用 Axios 的拦截器来实现当状态码非 200 时统一拦截错误并提示 toast 的方法:

假设你使用了一个名为 toast 的函数来显示 toast 消息,这个函数可以根据你的实际使用的 UI 库进行调整。

import axios from "axios";

// 创建 Axios 实例
const instance = axios.create();

// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    // 在发送请求之前做些什么,比如添加请求头、加载动画等
    return config;
  },
  (error) => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  (response) => {
    // 对响应数据做点什么
    return response;
  },
  (error) => {
    if (error.response && error.response.status !== 200) {
      // 非 200 状态码时显示 toast
      toast("请求错误!");
    }
    return Promise.reject(error);
  }
);

export default instance;

在上述代码中,首先创建了一个 Axios 实例,然后分别添加了请求拦截器和响应拦截器。在响应拦截器中,当响应状态码不是 200 时,调用toast函数显示错误提示信息。

请注意,这里的toast函数只是一个示例,你需要根据实际使用的 UI 框架或库来实现具体的 toast 显示功能。

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

936. [Vue] 对比一下 vuex 和 Pinia 两个状态库【热度: 300】【web 框架】【出题公司: 小米】

关键词:vue 状态库对比

Vuex 和 Pinia 都是用于 Vue 应用程序的状态管理库,它们有一些相似之处,但也存在一些差异。以下是它们的对比:

一、相似之处

  1. 集中式状态管理

    • 两者都提供了一种集中式的方式来管理应用程序的状态。这使得状态可以在不同的组件之间共享,并且可以更容易地跟踪和调试状态的变化。
    • 例如,在一个电商应用中,用户的购物车状态可以存储在状态管理库中,以便在不同的页面和组件中访问和更新。
  2. 响应式状态

    • Vuex 和 Pinia 都与 Vue 的响应式系统集成,使得状态的变化可以自动触发相关组件的重新渲染。
    • 当购物车中的商品数量发生变化时,相关的组件可以自动更新以反映这个变化。

二、不同之处

  1. 语法和 API

    • Pinia

      • Pinia 提供了一种更加简洁和直观的 API。它使用类似于 Vue 组件的语法来定义状态和操作,使得代码更加易读和易于维护。
      • 例如,定义一个 store 可以像这样:
      import { defineStore } from "pinia";
      
      export const useCartStore = defineStore("cart", {
        state: () => ({
          items: [],
        }),
        actions: {
          addItem(item) {
            this.items.push(item);
          },
        },
      });
      
    • Vuex

      • Vuex 的语法相对较为复杂,需要定义 mutations、actions 和 getters 等不同的概念来管理状态。
      • 例如,定义一个 store 可能如下所示:
      import Vuex from "vuex";
      
      const store = new Vuex.Store({
        state: {
          items: [],
        },
        mutations: {
          ADD_ITEM(state, item) {
            state.items.push(item);
          },
        },
        actions: {
          addItem({ commit }, item) {
            commit("ADD_ITEM", item);
          },
        },
        getters: {
          cartItems: (state) => state.items,
        },
      });
      
  2. 模块系统

    • Pinia

      • Pinia 的模块系统更加灵活和易于使用。可以轻松地将 store 拆分为多个模块,并且可以在不同的模块之间共享状态和操作。
      • 例如,可以创建一个名为user的模块和一个名为cart的模块,并在它们之间共享一些状态和操作:
      import { defineStore } from "pinia";
      
      const useUserStore = defineStore("user", {
        //...
      });
      
      const useCartStore = defineStore("cart", {
        state: () => ({
          //...
        }),
        actions: {
          addItem(item) {
            // 可以访问 userStore 的状态
            if (useUserStore().isLoggedIn) {
              //...
            }
          },
        },
      });
      
    • Vuex

      • Vuex 的模块系统也很强大,但相对来说更加复杂。需要使用命名空间来区分不同模块的 actions、mutations 和 getters,并且在模块之间共享状态和操作需要一些额外的配置。
  3. 类型支持

    • Pinia

      • Pinia 对 TypeScript 的支持非常好,可以轻松地为 store 定义类型,并且在开发过程中可以获得更好的类型提示和错误检查。
      • 例如,可以使用 TypeScript 来定义一个 store 的类型:
      import { defineStore } from 'pinia';
      
      interface CartItem {
        id: number;
        name: string;
        price: number;
      }
      
      export const useCartStore = defineStore('cart', {
        state: () => ({
          items: [] as CartItem[],
        }),
        //...
      });
      
    • Vuex

      • Vuex 也支持 TypeScript,但相对来说需要一些额外的配置和类型定义文件来获得更好的类型支持。
  4. 开发体验

    • Pinia
      • Pinia 提供了一些开发工具,如 Pinia Devtools,可以方便地调试和检查 store 的状态和操作。它还与 Vue Devtools 集成,使得在开发过程中可以更好地跟踪状态的变化。
      • Pinia 的 API 更加简洁,使得开发过程更加高效和愉快。
    • Vuex
      • Vuex 也有一些开发工具,如 Vuex Devtools,但相对来说功能可能没有 Pinia Devtools 那么强大。
      • Vuex 的语法相对较为复杂,可能需要一些时间来适应和掌握。

总的来说,Pinia 和 Vuex 都是强大的状态管理库,选择哪一个取决于你的具体需求和个人偏好。如果你喜欢简洁和直观的 API,并且对 TypeScript 有较好的支持需求,那么 Pinia 可能是一个更好的选择。如果你已经熟悉 Vuex 并且对其功能和模块系统有特定的需求,那么 Vuex 也是一个可靠的选择。

941. 动画性能如何检测【热度: 262】【web 应用场景】

关键词:动画性能

作者备注, 这个问题其实没有啥太大的价值, 可以当做科普看看即可

一、使用浏览器开发者工具

  1. Chrome 开发者工具:

    • 打开 Chrome 浏览器,进入要检测动画性能的页面。
    • 按下 F12 打开开发者工具,切换到“Performance”(性能)选项卡。
    • 点击“Record”(录制)按钮开始录制页面的交互和动画。
    • 进行页面上的动画操作,如滚动、拖动、动画播放等。
    • 停止录制后,开发者工具会生成一个性能分析报告,其中包括 CPU 使用率、FPS(每秒帧数)、帧时间线等信息,可以帮助你分析动画的性能瓶颈。

二、使用 JavaScript 性能分析工具

Frame Timing API 可以用于检测动画性能。

2.1、Frame Timing API 的作用

Frame Timing API 提供了一种方式来测量浏览器渲染每一帧所花费的时间。这对于分析动画性能非常有用,因为它可以帮助你确定动画是否流畅,是否存在卡顿或掉帧的情况。

2.2、如何使用 Frame Timing API 进行性能检测

  1. 获取帧时间数据:

    • 使用 performance.getEntriesByType('frame') 方法可以获取帧时间数据的数组。每个条目代表一帧的信息,包括帧的开始时间、持续时间等。
    const frameEntries = performance.getEntriesByType("frame");
    frameEntries.forEach((entry) => {
      console.log(`Frame start time: ${entry.startTime}`);
      console.log(`Frame duration: ${entry.duration}`);
    });
    
  2. 分析帧时间数据:

    • 通过分析帧的持续时间,可以判断动画是否在预期的时间内渲染。如果帧的持续时间过长,可能表示动画出现了卡顿。
    • 可以计算平均帧时间、最大帧时间等指标,以评估动画的整体性能。
  3. 结合其他性能指标:

    • Frame Timing API 可以与其他性能指标一起使用,如 FPS(每秒帧数)、CPU 使用率等,以全面了解动画性能。
    • 例如,可以使用 requestAnimationFrame 来计算 FPS,并结合帧时间数据来确定动画的流畅度。

2.3、优势和局限性

  1. 优势:

    • 提供精确的帧时间信息,有助于深入了解动画的性能细节。
    • 可以在浏览器中直接使用,无需安装额外的工具。
  2. 局限性:

    • 并非所有浏览器都完全支持 Frame Timing API,可能存在兼容性问题。
    • 仅提供帧时间数据,对于复杂的动画性能问题,可能需要结合其他工具和技术进行分析。

三、使用在线性能检测工具

  1. WebPageTest:

    • WebPageTest 是一个在线工具,可以对网页的性能进行全面的测试,包括加载时间、动画性能、资源加载等。
    • 输入要测试的网页 URL,选择测试参数,如浏览器类型、地理位置等,然后启动测试。
    • 测试完成后,会生成详细的报告,包括视频录制、性能指标图表等,可以帮助你分析动画性能问题。
  2. Lighthouse:

    • Lighthouse 是一个由 Google 开发的开源工具,可以作为 Chrome 浏览器的扩展或命令行工具使用。
    • 运行 Lighthouse 对网页进行审计,它会评估网页的性能、可访问性、最佳实践等方面,并提供详细的报告和建议。
    • 在性能部分,会包括对动画性能的评估,如 FPS、主线程忙碌时间等指标。

944. [webpack] 发中使用的是 esm 和 webpack, 是否可以直接调用 commonjs 模块化的三方包【热度: 359】【工程化】【出题公司: 阿里巴巴】

关键词:模块化混用

在使用 Webpack 作为构建工具的项目中,ESM (ECMAScript Modules) 和 CommonJS 模块系统可以混用。

Webpack 提供了对两种模块化标准的支持,能够理解和处理它们之间的差异,使得这两种不同类型的模块可以在同一个项目中共存。

混用时的考虑因素

虽然 ESM 和 CommonJS 可以混用,但是还需要注意一些关键的点:

  • 导入方式:当你从一个 ESM 模块中导入 CommonJS 模块时,导入的内容会被当成默认导出处理。这意味着,即使 CommonJS 模块导出了多个成员,你也需要以默认导入的方式来获取整个 exports 对象。

    // CommonJS 模块
    module.exports = {
      foo: "bar",
      baz: "qux",
    };
    
    // 在 ESM 中导入 CommonJS 模块
    import cjsModule from "./cjs-module";
    console.log(cjsModule.foo); // 输出: 'bar'
    
  • 导出方式:在一个 CommonJS 模块中,你可以通过 require 导入一个 ESM,但是这通常需要额外配置,因为 ESM 默认是异步加载的。此外,ESM 模块的导出在被 CommonJS 模块通过 require 导入时,必须访问它们的 default 属性或使用 import() 异步导入语句。

  • 动态与静态:ESM 是静态的,这意味着你不能动态地导入或导出模块,所有的导入和导出必须在模块的顶层发生。CommonJS 模块是动态的,允许你将 require 语句放在代码的任何位置。由于这种静态与动态的区别,混用时要考虑代码组织和模块加载顺序。

  • 构建与树摇(Tree Shaking):Webpack 可以对 ESM 进行树摇优化,以去除未使用的导出。这可以减少最终构建包的大小。由于 CommonJS 模块是动态的,它们不完全支持树摇。如果在意最终包的大小,优先使用 ESM 来编写新模块可能会更好。

总结

在 Webpack 下,ESM 和 CommonJS 可以混用,但是混用时需要注意导入导出的细微差别,以及可能对构建优化产生的影响。理解这些模块系统之间的差异,并合理组织代码,可以充分利用 Webpack 提供的灵活性和功能,编写高效且可维护的现代 JavaScript 应用。

945. axios 支持哪些常用的配置【热度: 196】【网络、web 应用场景】

关键词:axios 配置

作者备注

这个其实直接看官网即可 axios-http.com/docs/req_co…

以下是翻译的全配置

{
  // `url` 是将用于请求的服务器 URL。
  url: '/user',

  // `method` 是进行请求时要使用的请求方法。
  method: 'get', // 默认值。

  // `baseURL` 将被添加到 `url` 的前面,除非 `url` 是绝对路径。设置 axios 实例的 `baseURL` 可以方便地向该实例的方法传递相对 URL。
  baseURL: 'https://some-domain.com/api',

  // `transformRequest` 允许在将请求数据发送到服务器之前对其进行更改。这仅适用于请求方法为 'PUT'、'POST'、'PATCH' 和 'DELETE'。数组中的最后一个函数必须返回一个字符串或 `Buffer`、`ArrayBuffer`、`FormData` 或 `Stream` 的实例。你可以修改 `headers` 对象。
  transformRequest: [function (data, headers) {
    // 做任何你想对数据进行的转换。

    return data;
  }],

  // `transformResponse` 允许在将响应数据传递给 `then/catch` 之前对其进行更改。
  transformResponse: [function (data) {
    // 做任何你想对数据进行的转换。

    return data;
  }],

  // `headers` 是要发送的自定义请求头。
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是要与请求一起发送的 URL 参数。必须是一个普通对象或 `URLSearchParams` 对象。注意:值为 `null` 或 `undefined` 的参数不会在 URL 中呈现。
  params: {
    ID: 12345
  },

  // `paramsSerializer` 是一个可选的负责序列化 `params` 的函数(例如:https://www.npmjs.com/package/qs,http://api.jquery.com/jquery.param/)。
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'});
  },

  // `data` 是要作为请求主体发送的数据。仅适用于请求方法为 'PUT'、'POST'、'DELETE' 和 'PATCH'。当没有设置 `transformRequest` 时,必须是以下类型之一:字符串、普通对象、`ArrayBuffer`、`ArrayBufferView`、`URLSearchParams`;仅在浏览器中:`FormData`、`File`、`Blob`;仅在 Node.js 中:`Stream`、`Buffer`。
  data: {
    firstName: 'Fred'
  },

  // 发送数据到主体的语法替代方法,方法为 post,仅发送值,不发送键。
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout` 指定请求超时的毫秒数。如果请求花费的时间超过 `timeout`,请求将被中止。
  timeout: 1000, // 默认值为 `0`(无超时)。

  // `withCredentials` 指示是否应使用凭证进行跨站点的 Access-Control 请求。
  withCredentials: false, // 默认值。

  // `adapter` 允许对请求进行自定义处理,这使得测试更加容易。返回一个 Promise 并提供有效的响应(请参阅 lib/adapters/README.md)。
  adapter: function (config) {
    /*... */
  },

  // `auth` 表示应使用 HTTP 基本认证,并提供凭证。这将设置一个 `Authorization` 请求头,覆盖你使用 `headers` 设置的任何现有的 `Authorization` 自定义请求头。请注意,仅可通过此参数配置 HTTP 基本认证。对于 Bearer 令牌等,应使用 `Authorization` 自定义请求头代替。
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 指示服务器将响应的数据类型。选项有:'arraybuffer'、'document'、'json'、'text'、'stream';仅在浏览器中:'blob'。
  responseType: 'json', // 默认值。

  // `responseEncoding` 指示用于解码响应的编码(仅在 Node.js 中有效)。注意:对于 `responseType` 为 'stream' 或客户端请求将被忽略。
  responseEncoding: 'utf8', // 默认值。

  // `xsrfCookieName` 是用作 xsrf 令牌值的 cookie 的名称。
  xsrfCookieName: 'XSRF-TOKEN', // 默认值。

  // `xsrfHeaderName` 是携带 xsrf 令牌值的 HTTP 请求头的名称。
  xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值。

  // `onUploadProgress` 允许处理上传的进度事件(仅在浏览器中有效)。
  onUploadProgress: function (progressEvent) {
    // 对原生进度事件做任何你想做的处理。
  },

  // `onDownloadProgress` 允许处理下载的进度事件(仅在浏览器中有效)。
  onDownloadProgress: function (progressEvent) {
    // 对原生进度事件做任何你想做的处理。
  },

  // `maxContentLength` 定义在 Node.js 中允许的 HTTP 响应内容的最大字节大小。
  maxContentLength: 2000,

  // `maxBodyLength`(仅在 Node.js 中有效)定义允许的 HTTP 请求内容的最大字节大小。
  maxBodyLength: 2000,

  // `validateStatus` 定义对于给定的 HTTP 响应状态码是应该解析还是拒绝 Promise。如果 `validateStatus` 返回 `true`(或设置为 `null` 或 `undefined`),则 Promise 将被解析;否则,Promise 将被拒绝。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值。
  },

  // `maxRedirects` 定义在 Node.js 中要遵循的最大重定向次数。如果设置为 0,则不会遵循重定向。
  maxRedirects: 5, // 默认值。

  // `socketPath` 定义在 Node.js 中要使用的 UNIX 套接字。例如:'/var/run/docker.sock' 用于向 Docker 守护进程发送请求。只能指定 `socketPath` 或 `proxy` 中的一个。如果两者都指定,则使用 `socketPath`。
  socketPath: null, // 默认值。

  // `httpAgent` 和 `httpsAgent` 定义在 Node.js 中执行 HTTP 和 HTTPS 请求时分别要使用的自定义代理。这允许添加像 `keepAlive` 这样的选项,这些选项在默认情况下是未启用的。
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy` 定义代理服务器的主机名、端口和协议。你也可以使用传统的 `http_proxy` 和 `https_proxy` 环境变量来定义你的代理。如果你使用环境变量来进行代理配置,你还可以定义一个 `no_proxy` 环境变量,作为不应被代理的域名的逗号分隔列表。使用 `false` 来禁用代理,忽略环境变量。`auth` 表示应使用 HTTP 基本认证来连接到代理,并提供凭证。这将设置一个 `Proxy-Authorization` 请求头,覆盖你使用 `headers` 设置的任何现有的 `Proxy-Authorization` 自定义请求头。如果代理服务器使用 HTTPS,则你必须将协议设置为 `https`。
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken` 指定一个取消令牌,可用于取消请求(请参阅下面的“取消”部分了解详细信息)。
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress` 指示是否应自动解压缩响应主体。如果设置为 `true`,还将从所有解压缩响应的对象中删除 'content-encoding' 请求头。仅在 Node.js 中有效(XHR 无法关闭解压缩)。
  decompress: true // 默认值。

}

946. 解释下 axios withCredentials 配置【热度: 197】【网络、web 应用场景】

关键词:axios 配置

作者备注

这个问题太冷门了, 如果不是细看过顾问囊的人, 是不会知道有这个配置项的。 可以当做科普即可, 面试中问到的可能性也非常的低。

在 Axios 中,withCredentials是一个配置选项,用于处理跨源请求时是否携带用户凭证(cookies、HTTP 认证信息等)。

一、作用

  1. 默认行为:

    • 默认情况下,withCredentials的值为false。这意味着在跨源请求中,浏览器不会自动发送用户凭证。
    • 例如,当你使用 Axios 向不同域名的服务器发送请求时,如果withCredentialsfalse,浏览器不会在请求中包含任何用户凭证信息。
  2. 启用凭证发送:

    • 如果将withCredentials设置为true,则在跨源请求中,浏览器会自动包含用户凭证信息,如 cookies、HTTP 认证信息等。
    • 这对于需要在不同域名之间共享用户认证状态的应用非常有用。例如,一个单页应用(SPA)可能需要与不同的后端服务进行交互,并且希望在这些服务之间共享用户登录状态。

二、注意事项

  1. 服务器端配置:

    • 要使withCredentials生效,服务器端也需要进行相应的配置,以允许接收跨源请求中的凭证信息。
    • 服务器需要设置适当的 CORS(跨源资源共享)响应头,如Access-Control-Allow-Credentials: true,并且指定允许的源Access-Control-Allow-Origin不能为通配符*,而必须是具体的源地址。
  2. 安全考虑:

    • 启用withCredentials可能会带来安全风险,因为用户凭证可能会被发送到不受信任的服务器。因此,在使用时需要谨慎考虑安全问题,并确保只向可信任的服务器发送凭证信息。

例如:

axios
  .get("http://another-domain.com/api/data", {
    withCredentials: true,
  })
  .then((response) => {
    console.log(response.data);
  })
  .catch((error) => {
    console.error(error);
  });

在这个例子中,Axios 发送一个跨源请求,并将withCredentials设置为true,以尝试在请求中包含用户凭证信息。

949. axios 如何注销拦截器【热度: 140】【网络、web 应用场景】

关键词:axios 拦截器

作者备注

这个问题稍微有点儿偏冷门, 需要阅读过 axios 官网才能正确作答, 考察的是同学自驱型学习能力

在 Axios 中,可以使用以下方法注销拦截器:

一、为拦截器分配一个引用

  1. 创建拦截器时保存引用:

    • 当创建一个 Axios 请求或响应拦截器时,可以将其分配给一个变量,以便后续可以引用并注销它。
    const requestInterceptor = axios.interceptors.request.use((config) => {
      // 请求拦截器逻辑
      return config;
    });
    
    const responseInterceptor = axios.interceptors.response.use((response) => {
      // 响应拦截器逻辑
      return response;
    });
    

二、使用Eject方法注销拦截器

  1. 注销单个拦截器:

    • 使用拦截器的引用和axios.interceptors.request.eject()axios.interceptors.response.eject()方法来注销特定的拦截器。
    axios.interceptors.request.eject(requestInterceptor);
    axios.interceptors.response.eject(responseInterceptor);
    
  2. 注销所有拦截器:

    • 如果需要注销所有的请求或响应拦截器,可以使用axios.interceptors.request.clear()axios.interceptors.response.clear()方法。
    axios.interceptors.request.clear();
    axios.interceptors.response.clear();
    

以下是一个完整的示例:

import axios from "axios";

const requestInterceptor = axios.interceptors.request.use((config) => {
  // 请求拦截器逻辑
  return config;
});

const responseInterceptor = axios.interceptors.response.use((response) => {
  // 响应拦截器逻辑
  return response;
});

// 注销特定拦截器
axios.interceptors.request.eject(requestInterceptor);
axios.interceptors.response.eject(responseInterceptor);

// 或者注销所有拦截器
// axios.interceptors.request.clear();
// axios.interceptors.response.clear();

通过这些方法,可以在需要的时候注销特定的拦截器或所有拦截器,以灵活地管理 Axios 的拦截器。

950. axios 如何取消请求【热度: 218】【网络、web 应用场景】

关键词:axios 取消请求

作者备注

新版本已经废弃使用 cancelToken ,改为使用 signal

可以参考官网:axios-http.com/docs/cancel…

取消请求

timeout在 axios 调用中设置属性可处理响应相关的超时。

在某些情况下(例如网络连接不可用),提前取消连接对 axios调用大有裨益。如果不取消连接,axios 调用可能会挂起,直到父代码/堆栈超时(在服务器端应用程序中可能需要几分钟)。

要终止 axios 调用,您可以使用以下方法:

  • signal
  • cancelToken(已弃用)

组合timeout和取消方法(例如signal)应该涵盖响应相关的超时和连接相关的超时。

signal:中止控制器

v0.22.0Axios 开始支持AbortController以 fetch API 方式取消请求:

const controller = new AbortController();

axios
  .get("/foo/bar", {
    signal: controller.signal,
  })
  .then(function (response) {
    //...
  });
// cancel the request
controller.abort();

使用最新 API nodejs 17.3+. 的超时示例AbortSignal.timeout()

axios
  .get("/foo/bar", {
    signal: AbortSignal.timeout(5000), //Aborts request after 5 seconds
  })
  .then(function (response) {
    //...
  });

具有超时辅助函数的示例:

function newAbortSignal(timeoutMs) {
  const abortController = new AbortController();
  setTimeout(() => abortController.abort(), timeoutMs || 0);

  return abortController.signal;
}

axios
  .get("/foo/bar", {
    signal: newAbortSignal(5000), //Aborts request after 5 seconds
  })
  .then(function (response) {
    //...
  });

取消令牌deprecated

还可以使用 CancelToken_取消请求。

Axios 取消令牌 API 基于已撤回的可取消承诺提案

此 API 已弃用v0.22.0,不应在新项目中使用

您可以使用工厂创建取消令牌CancelToken.source,如下所示:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios
  .get("/user/12345", {
    cancelToken: source.token,
  })
  .catch(function (thrown) {
    if (axios.isCancel(thrown)) {
      console.log("Request canceled", thrown.message);
    } else {
      // handle error
    }
  });

axios.post(
  "/user/12345",
  {
    name: "new name",
  },
  {
    cancelToken: source.token,
  }
);

// cancel the request (the message parameter is optional)
source.cancel("Operation canceled by the user.");

您还可以通过将执行器函数传递给构造函数来创建取消令牌CancelToken

const CancelToken = axios.CancelToken;
let cancel;

axios.get("/user/12345", {
  cancelToken: new CancelToken(function executor(c) {
    // An executor function receives a cancel function as a parameter
    cancel = c;
  }),
});

// cancel the request
cancel();

注意:您可以使用相同的取消令牌/信号取消多个请求。

在过渡期间,你可以使用这两个取消 API,即使对于同一个请求也是如此:

const controller = new AbortController();

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios
  .get("/user/12345", {
    cancelToken: source.token,
    signal: controller.signal,
  })
  .catch(function (thrown) {
    if (axios.isCancel(thrown)) {
      console.log("Request canceled", thrown.message);
    } else {
      // handle error
    }
  });

axios.post(
  "/user/12345",
  {
    name: "new name",
  },
  {
    cancelToken: source.token,
  }
);

// cancel the request (the message parameter is optional)
source.cancel("Operation canceled by the user.");
// OR
controller.abort(); // the message parameter is not supported

951. 在 forEach 中和 for 循环 中调用异步函数的区别【热度: 371】【JavaScript、web 应用场景】

关键词:循环中的异步

在 JavaScript 中,处理异步函数时,for 循环和 forEach 方法之间的行为有显著差异,尤其是当你试图在循环体中使用 async/await 时。理解这些差异对于编写正确且高效的异步代码是很重要的。

使用 forEach 调用异步函数

Array.prototype.forEach 方法用于对数组的每个元素执行一次提供的函数,但它不会等待异步操作完成。换句话说,即便是在 forEach 循环里使用了 async 函数,这个 async 函数依旧会立即执行,但 forEach 不会等待它完成。

const array = [1, 2, 3];

array.forEach(async (item) => {
  await doAsyncOperation(item); // 将立即开始但不会等待完成
});

console.log("Finished"); // 这行代码不会等待上面的异步操作完成就执行

上面的代码中,doAsyncOperation 会对数组中的每个元素立即开始执行,但 forEach 不会等待它们完成。因此,console.log('Finished') 会在所有异步操作完成之前执行。

使用 for 循环调用异步函数

forEach 方法不同,for 循环允许在每次迭代时使用 await 关键字等待异步操作的完成。因此,如果你需要按顺序等待每个异步操作完成,for 循环是更好的选择。

const array = [1, 2, 3];

(async () => {
  for (let item of array) {
    await doAsyncOperation(item); // 将等待异步操作完成后再继续下一次迭代
  }

  console.log("Finished"); // 仅在所有异步操作完成后执行
})();

这里,每个 doAsyncOperation 调用会在下一个开始之前完成。console.log('Finished') 等到所有异步操作完成后才执行,这样就能保证异步操作的完成顺序。

总结

  • 当你不关心异步操作完成的顺序,或不需要等待所有异步操作完成时,可以使用 forEach
  • 如果你需要按顺序等待每个异步操作完成,使用 for 循环(或其他支持 await 的循环,如 for...of 或传统的 for 循环)是一个更好的选择。
  • 一般而言,当涉及到异步函数时,使用 for 循环(允许 await)比 forEach 方法(不等待异步调用完成) 提供了更大的灵活性和控制力。

参考

可以参考下面的文档: github.com/lgwebdream/…

952. mobx 和 redux 有什么区别【热度: 277】【web 应用场景】【出题公司: 阿里巴巴】

关键词:mobx 和 redux 区别

MobX 和 Redux 都是流行的 JavaScript 状态管理库,广泛用于帮助开发者以可预测的方式管理应用的状态。尽管它们都旨在解决相同的问题,但它们的设计哲学、实现方式以及提倡的最佳实践有很大差异。

设计理念的不同

  • Redux:其设计理念是保持状态的可预测性,鼓励使用纯函数来执行状态变更。在 Redux 中,所有的状态变化都是通过派发(dispatching)一个动作(action)并通过一个或多个 reducer 来处理这些动作来实现的。这促进了一个单向数据流模型,使得状态管理变得清晰但也更加繁琐。
  • MobX:其通过透明的函数响应式编程(TFRP)原则来实现状态管理。在 MobX 中,你可以直接修改状态,而 MobX 会负责跟踪这些状态的变化,并自动应用任何副作用(如重新渲染 UI)。这种方式使得状态管理更加直观和灵活,但可能会牺牲一些可预测性。

使用和实现上的差异

  • Redux:通常需要更多的样板代码和设置。你需要定义 action 类型、action 创建函数以及 reducer。同时,它鼓励使用不可变数据模式,这可能需要使用额外的库(如 Immutable.js)。Redux 并不自带中间件和异步管理的解决方案,通常需要引入如 redux-thunk 或 redux-saga 等中间件来处理副作用。
  • MobX:使用起来更简单,几乎不需要样板代码。因为 MobX 通过跟踪状态的修改并自动应用更改来工作(使用代理或装饰器跟踪属性的读写),开箱即用,而无需担心不可变性。MobX 允许使用更自然的 JavaScript 数据结构(如对象、数组),并自动为你处理检测更改和更新 UI。

性能对比

  • Redux:对于大型应用,由于每次状态更新都会执行可能涉及整个状态树的深度比较,因此可能需要仔细优化选择器和避免不必要的渲染。
  • MobX:由于其使用响应式系统仅跟踪实际使用的状态部分,并在这部分状态变更时才更新,通常能提供更好的运行时性能,尤其是在大型应用中。

选择哪一个?

选择 Redux 还是 MobX,很大程度上取决于项目需求、团队偏好和项目的规模。

  • 如果你更喜欢函数式编程范式、需要更高程度的可预测性以及更好的时间旅行调试能力,那么 Redux 可能是更好的选择。
  • 如果你倾向于面向对象的编程范式、期望更少的样板代码以及更加灵活的状态管理方式,那么 MobX 可能更符合你的需求。

两者之间的选择并不是全有或全无,实际上有些项目也会结合使用两者的优点。正确理解每种库的设计理念和适用场景是做出选择的关键。

953. [webpack] tree-shaking 原理【热度: 722】【工程化】【出题公司: 腾讯】

关键词:tree-shaking 原理

作者备注

webpack 热门问题

Webpack 的 Tree Shaking 主要是用来消除未被使用的代码,以减小最终打包文件的体积。其原理如下:

一、静态分析

  1. 模块依赖分析:

    • Webpack 在构建过程中,会对项目中的模块进行依赖分析。它会解析每个模块的内容,确定模块之间的导入和导出关系。
    • 通过分析,可以构建出一个模块依赖图,展示了各个模块之间的引用关系。
  2. 识别未使用的代码:

    • 基于模块依赖图,Webpack 可以确定哪些模块被实际使用,哪些模块未被使用。
    • 对于 JavaScript 模块,它可以识别出未被调用的函数、未被访问的变量等。对于其他资源文件,如 CSS 和图片,也可以根据引用情况判断是否被使用。

二、代码优化

  1. 消除未使用的代码:

    • 一旦识别出未使用的代码,Webpack 会在打包过程中将这些代码从最终的输出文件中移除。
    • 这可以显著减小打包文件的大小,提高应用的加载速度和性能。
  2. 作用域分析:

    • 在消除未使用的代码时,Webpack 还会进行作用域分析。它会确保在移除代码的过程中,不会影响到实际使用的代码的正确性。
    • 例如,如果一个函数在一个模块中未被使用,但在另一个模块中被间接引用,Webpack 会谨慎处理,避免错误地移除该函数。

三、实现条件

  1. ES2015 模块语法:

    • Tree Shaking 主要依赖于 ES2015 模块语法(importexport)。这种模块语法是静态的,使得 Webpack 能够在编译时确定模块的导入和导出关系。
    • 相比之下,CommonJS 模块语法(requiremodule.exports)是动态的,难以在编译时进行准确的分析。
  2. 支持的模块类型:

    • Webpack 不仅可以对 JavaScript 模块进行 Tree Shaking,还可以对一些其他类型的模块进行处理,如 CSS 模块(通过特定的加载器和插件)。
    • 对于不同类型的模块,Webpack 可能会使用不同的技术和策略来实现 Tree Shaking。

总之,Webpack 的 Tree Shaking 通过静态分析模块依赖关系,识别并消除未使用的代码,从而优化打包文件的大小和性能。它依赖于 ES2015 模块语法和准确的模块依赖分析,同时需要注意一些实现条件和潜在的问题。

954. 前端应用有哪些代码测试手段【热度: 385】【web 应用场景】【出题公司: 腾讯】

关键词:测试手段

在前端应用中,有以下几种主要的代码测试手段:

一、单元测试

  1. 定义:

    • 针对应用程序中的最小可测试单元(如函数、方法或类)进行的测试。
    • 目的是确保每个独立的单元在各种输入情况下都能正确执行其预期的功能。
  2. 常用工具:

    • Jest:一个功能强大且流行的 JavaScript 测试框架,提供了丰富的断言库、模拟函数等功能。
    • Mocha:另一个广泛使用的测试框架,可以与各种断言库和测试运行器配合使用。
    • Jasmine:以简洁的语法和易于使用而著称,适合小型项目和快速测试。
  3. 测试示例:

    function add(a, b) {
      return a + b;
    }
    
    test("add function should add two numbers correctly", () => {
      expect(add(2, 3)).toBe(5);
      expect(add(-1, 1)).toBe(0);
    });
    

二、集成测试

  1. 定义:

    • 测试多个组件或模块之间的交互和集成。
    • 验证不同部分的代码在组合在一起时是否能正常工作。
  2. 实现方式:

    • 可以使用与单元测试类似的工具,但需要设置更复杂的测试环境来模拟多个组件的交互。
    • 例如,在前端应用中,可以使用测试框架来加载模拟的组件和数据,然后测试它们之间的通信和功能。
  3. 示例:

    • 假设一个前端应用有一个表单组件和一个提交按钮,集成测试可以验证当用户填写表单并点击提交按钮时,数据是否正确地发送到后端服务器。

三、端到端测试

  1. 定义:

    • 模拟用户与整个应用程序的交互,从用户界面开始,经过各个系统组件和层,一直到后端服务。
    • 确保整个应用在真实环境下的功能和性能符合预期。
  2. 常用工具:

    • Cypress:一个专门用于端到端测试的工具,提供了直观的 API 和强大的功能,如自动等待、截图、视频录制等。
    • Puppeteer:由 Google 开发的无头浏览器自动化工具,可以用于编写端到端测试脚本。
  3. 测试示例:

    • 使用 Cypress 测试一个电子商务网站的购物流程:
    describe("E-commerce Shopping Flow", () => {
      it("should add an item to the cart and complete the purchase", () => {
        cy.visit("https://your-ecommerce-site.com");
        cy.get(".product-item").first().click();
        cy.get(".add-to-cart-button").click();
        cy.get(".cart-icon").click();
        cy.get(".checkout-button").click();
        // 继续模拟填写表单和完成购买的步骤
      });
    });
    

四、组件测试

  1. 定义:

    • 专门针对前端应用中的组件进行测试。
    • 验证组件的渲染、交互和状态管理等功能。
  2. 常用工具:

    • React Testing Library:如果使用 React 开发,这个工具提供了一种以用户为中心的方式来测试 React 组件。
    • Vue Test Utils:对于 Vue.js 应用,用于测试 Vue 组件的工具。
  3. 测试示例:

    • 使用 React Testing Library 测试一个 React 组件:
    import React from "react";
    import { render, fireEvent } from "@testing-library/react";
    import YourComponent from "./YourComponent";
    
    test("YourComponent should render correctly and handle button click", () => {
      const { getByText, getByRole } = render(<YourComponent />);
      expect(getByText("Component Title")).toBeInTheDocument();
      const button = getByRole("button");
      fireEvent.click(button);
      expect(getByText("Button Clicked")).toBeInTheDocument();
    });
    

五、静态代码分析

  1. 定义:

    • 不执行代码,而是对代码进行静态分析,检查代码的质量、风格和潜在的错误。
    • 可以帮助开发者在早期发现代码中的问题,提高代码的可读性和可维护性。
  2. 常用工具:

    • ESLint:用于检查 JavaScript 和 TypeScript 代码的语法错误、风格问题和潜在的错误。
    • Stylelint:专门用于检查 CSS 和 SCSS 代码的风格问题。
    • Prettier:一个代码格式化工具,可以确保代码的风格一致,并且可以与 ESLint 和 Stylelint 集成。
  3. 示例配置:

    • 在项目中配置 ESLint,可以创建一个.eslintrc.js文件,定义规则和插件:
    module.exports = {
      extends: ["eslint:recommended", "plugin:react/recommended"],
      rules: {
        "no-console": "warn",
        quotes: ["error", "single"],
      },
    };
    

通过综合运用这些测试手段,可以有效地提高前端应用的质量和稳定性,减少错误和缺陷的出现。

955. 单元测试、E2E 测试有和区别?【热度: 258】【web 应用场景】【出题公司: 腾讯】

关键词:前端测试

单元测试(Unit Testing)和端到端测试(End-to-End Testing,E2E Testing)有以下主要区别:

一、测试范围

  1. 单元测试:

    • 聚焦于软件系统的最小可测试单元,通常是函数、方法或类。
    • 只测试单个功能单元的行为,隔离其他部分的影响。
    • 例如,测试一个特定的数学函数是否正确计算结果,或者一个类的某个方法是否按照预期执行特定任务。
  2. 端到端测试:

    • 模拟用户与整个应用程序的交互,从用户界面开始,经过各个系统组件和层,一直到后端服务。
    • 涵盖整个软件系统的多个模块、组件和服务的集成。
    • 例如,测试一个完整的用户注册流程,包括在用户界面输入信息、提交表单、后端验证、数据库存储以及最终的反馈显示。

二、测试目的

  1. 单元测试:

    • 主要目的是确保各个独立的功能单元正确工作,尽早发现代码中的错误。
    • 帮助开发者在开发过程中快速定位和修复问题,提高代码质量和可维护性。
  2. 端到端测试:

    • 验证整个系统在真实环境下的功能和性能是否符合预期。
    • 确保不同组件之间的集成没有问题,以及系统能够满足用户的实际需求。

三、依赖关系

  1. 单元测试:

    • 通常可以独立运行,不依赖于外部系统或复杂的环境设置。
    • 可以使用模拟对象(mocks)和桩对象(stubs)来模拟外部依赖,以便专注于测试目标单元的功能。
  2. 端到端测试:

    • 依赖于完整的系统部署和运行环境,包括数据库、服务器、网络等。
    • 需要确保所有相关的组件和服务都正常运行,以进行有效的测试。

四、测试速度

  1. 单元测试:

    • 通常执行速度很快,因为只测试小范围的代码,并且可以使用轻量级的测试框架和工具。
    • 开发者可以频繁运行单元测试,以便在开发过程中及时发现问题。
  2. 端到端测试:

    • 由于涉及到整个系统的模拟和运行,执行速度相对较慢。
    • 可能需要较长的时间来准备测试环境和执行测试用例。

五、维护成本

  1. 单元测试:

    • 相对容易维护,因为当代码发生变化时,只需要更新相关的单元测试。
    • 由于测试范围小,定位和修复问题也比较容易。
  2. 端到端测试:

    • 维护成本较高,因为系统的任何变化都可能影响到端到端测试的结果。
    • 需要不断更新测试用例以适应系统的变化,并且可能需要处理复杂的环境配置和依赖关系问题。
转载自:https://juejin.cn/post/7419209528263557172
评论
请登录