likes
comments
collection
share

前端面试第63期 - 2024.08.31 更新前端面试问题总结(20 道题)2024.08.31 更新前端面试问题总结

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

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

目录:

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

      1. 内联加载样式和外联加载样式有啥区别【热度: 383】【CSS】【出题公司: Shopee】
  • 中级开发者相关问题【共计 11 道题】

      1. 如何判断页签是否为活跃状态【热度: 153】【web 应用场景】【出题公司: 百度】
      1. 如果在网络带宽一定的情况下, 切片上传感觉和整体上传, 消费的时间应该是差不多的。 这种说法正确吗【热度: 363】【web 应用场景】【出题公司: 百度】
      1. 可有办法判断用户的网络条件, 判断网速快慢,网络状态?【热度: 195】【网络】【出题公司: 小米】
      1. 在页面关闭时执行方法,该如何做【热度: 334】【web 应用场景】【出题公司: 腾讯】
      1. 长文本溢出,展开/收起如何实现【热度: 895】【web 应用场景】【出题公司: 阿里巴巴】
      1. 如何实现鼠标拖拽【热度: 212】【web 应用场景】
      1. ResizeObserver 作用是什么【热度: 416】【web 应用场景】【出题公司: 美团】
      1. 要实时统计用户浏览器窗口大小,该如何做【热度: 210】【web 应用场景】【出题公司: 美团】
      1. flex 布局中,有哪些常用的属性,分别作用是啥【热度: 300】【CSS】【出题公司: 百度】
      1. 实现阿拉伯数字转中文【热度: 673】【代码实现/算法】【出题公司: 小米】
      1. TypeScript 中 any、never、unknown、null & undefined 和 void 有什么区别【热度: 334】【TypeScript】
  • 高级开发者相关问题【共计 8 道题】

    • 858. 大文件切片上传的时候,确定切片数量的时候,有那些考量因素【热度: 366】【web 应用场景】【出题公司: 百度】
      1. 如何统计用户 pv 访问的发起请求数量(所有域名的)【热度: 469】【web 应用场景】【出题公司: 百度】
      1. [React] 性能调优中,如何确定哪个数据变化引起的组件渲染【热度: 500】【web 框架】【出题公司: 阿里巴巴】
      1. 统计全站每一个静态资源加载耗时, 该如何做【热度: 564】【web 应用场景】【出题公司: 阿里巴巴】
      1. 如何防止前端页面重复请求【热度: 451】【web 应用场景】【出题公司: 京东】
      1. JS 项目逐步迁移到 TS 项目,该如何做【热度: 870】【TypeScript】【出题公司: 阿里巴巴】
      1. tsconfig.json 中有哪些重用的配置项【热度: 289】【TypeScript】【出题公司: 阿里巴巴】
      1. 如何开启 ts 类型强校验,ts 类型错误 webpack 直接编译失败【热度: 232】【TypeScript】【出题公司: 小米】

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

870. 内联加载样式和外联加载样式有啥区别【热度: 383】【CSS】【出题公司: Shopee】

关键词:样式加载

内联样式(Inline Style)和外联样式(External Style)是 CSS 应用在 HTML 文档中的两种不同方法,它们主要的区别在于如何将 CSS 规则与 HTML 元素关联起来。

内联样式

  • 定义方式:通过元素的style属性直接在 HTML 标签内定义 CSS 样式。
  • 优先级:内联样式的优先级高于外联样式和嵌入样式(在<head>标签内的<style>标签中定义的样式),因为它是直接应用到元素上的。
  • 应用场景:适合对单个元素进行样式定义,或者进行快速测试。但如果用于大量元素的样式定义,会使 HTML 文档变得非常臃肿,难以维护。
  • 示例
<div style="color: blue; font-size: 14px;">这是一段内联样式的文本。</div>

外联样式

  • 定义方式:将 CSS 样式定义在一个外部的.css文件中,然后通过<link>标签在 HTML 的<head>中引用。
  • 优先级:一般情况下,外联样式的优先级低于内联样式。但在多个样式之间的优先级还取决于选择器的具体性、样式定义的顺序等因素。
  • 应用场景:适合网站或应用的全局样式定义,能够实现样式的复用和统一管理,便于维护和更新。
  • 示例
<!-- HTML文件中引用 -->
<link rel="stylesheet" href="style.css" />

/* style.css文件中定义样式 */ div { color: red; font-size: 16px; }

主要区别

  1. 加载方式:内联样式直接写在 HTML 元素的style属性中,而外联样式则放在单独的 CSS 文件中,通过<link>标签引入。
  2. 复用性:外联样式可以被多个 HTML 页面共享,提高了样式的复用性;内联样式只作用于具体的元素,无法复用。
  3. 维护性:外联样式易于维护和更新,只需修改一个 CSS 文件即可影响引用该 CSS 文件的所有页面;内联样式则需要逐个元素修改,维护成本较高。
  4. 优先级:内联样式的优先级高于外联样式和嵌入式样式,因为它更“接近”元素。
  5. 性能影响:外联样式可利用浏览器缓存,有助于减少页面加载时间;而大量使用内联样式会增加 HTML 文档的大小,可能对性能产生不利影响。

通常,推荐使用外联样式来实现样式的规范化管理和复用,特别是在大型项目和团队协作的场景中。内联样式则适用于对单个元素快速测试样式或进行特殊样式覆盖的情况。

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

855. 如何判断页签是否为活跃状态【热度: 153】【web 应用场景】【出题公司: 百度】

关键词:页签活跃状态

判断页面页签(Tab)是否为活跃状态,可以通过监听 visibilitychange 事件来实现。这个事件是由 document 对象触发的,可以用来判断页面是否对用户可见。当用户切换到其他标签页、最小化浏览器窗口、或是锁屏时,页面就会变为不可见状态。如果页面对用户可见,那么页面就处于活跃状态。

使用 document.visibilityState 属性可以检查页面的当前可视状态,这个属性有以下可能的值:

  • "visible":页面至少部分可见。在桌面端,这通常意味着页面是当前激活的标签页。
  • "hidden":页面对用户不可见。
  • "prerender""unloaded":这两个值用于特殊情况,通常较少用到。

示例代码

下面的代码演示了如何使用 visibilitychange 事件和 document.visibilityState 来判断页面是否为活跃状态:

document.addEventListener("visibilitychange", function () {
  if (document.visibilityState === "visible") {
    console.log("页面现在是活跃状态。");
  } else {
    console.log("页面现在不是活跃状态。");
  }
});

每当用户切换到该页签或从该页签切换走时,会触发 visibilitychange 事件。通过检查 document.visibilityState 的值,你可以判断页面是变为活跃状态还是变为非活跃状态。

这个功能可以用于多种场合,比如:

  • 停止或开始运行页面上的动画。
  • 控制媒体播放(比如自动暂停视频播放)。
  • 调整页面或应用的资源消耗(对于非活跃页签减少资源使用)。
  • 发送用户行为统计数据,以记录用户实际查看页面的时间。

这种方法的优点是兼容性好,现代浏览器都支持 visibilitychange 事件,可以用于构建响应用户行为的 web 应用。

856. 如果在网络带宽一定的情况下, 切片上传感觉和整体上传, 消费的时间应该是差不多的。 这种说法正确吗【热度: 363】【web 应用场景】【出题公司: 百度】

关键词:切换上传合理性

这种说法在某些情况下是正确的,但实际上,切片上传相较于整体上传,在多种情况下可能更加高效,即使网络带宽固定。切片上传的优势并不仅仅在于可能节约的时间,还包括以下几点:

1. 提高上传的可靠性

  • 切片上传允许在遇到网络中断或其他传输错误时只重新上传失败的那一部分,而不是重新上传整个文件。这在大文件传输中尤其重要。

2. 实现上传进度的精确控制

  • 通过切片上传,可以更精确地控制和显示上传进度,提高用户体验。

3. 带宽利用率

  • 切片上传可以更有效地管理带宽,尤其是在网络条件不稳定的环境中。通过并行上传多个切片,可以更充分地利用可用带宽,从而在理论上减少等待时间,特别是在高延迟的环境中。

4. 服务器处理

  • 对于服务器来说,处理多个小文件比处理一个大文件具有更高的灵活性和效率,尤其是在服务器负载高的情况下。此外,小文件的处理错误不会影响到整个文件,使得错误恢复更简单。

5. 安全性

  • 切片上传还可以增强安全性,因为单个切片的加密和传输比一个大文件来得容易和安全;此外,即使攻击者截获了部分数据,也难以重构出原始文件。

综合考虑

然而,切片上传也有其缺点,例如增加了客户端和服务器端处理的复杂性,需要正确管理和重组文件的各个部分。此外,在某些情况下(尤其是文件较小时),切片上传相较于整体上传并不会带来明显的时间优势,且可能因为初始化多个连接而略微增加总体上传时间。

所以,是否选择切片上传,取决于文件大小、网络稳定性、服务器能力以及应用场景。对于大文件上传、网络条件不佳或需要高可靠性的场景,切片上传通常是更优的选择。

857. 可有办法判断用户的网络条件, 判断网速快慢,网络状态?【热度: 195】【网络】【出题公司: 小米】

关键词:网络状态

确定用户的网络条件,包括网络速度和连接状态,对于提供优质用户体验至关重要。以下是一些方法可以帮助你判断用户的网络条件:

1. Navigator Connection API

这个 API 提供有关系统的网络连接的信息,如网络的类型和下载速度。这个 API 的支持度不是全局性的,但在许多现代浏览器上可用。使用这个 API,你可以获取到有关用户网络连接的详细信息。

if ("connection" in navigator) {
  const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

  console.log(`网络类型: ${connection.effectiveType}`);
  console.log(`估计的下行速度: ${connection.downlink}Mbps`);
  console.log(`RTT: ${connection.rtt}ms`);

  // 监听网络类型变化
  connection.addEventListener("change", (e) => {
    console.log(`网络类型变化为: ${connection.effectiveType}`);
  });
}
  • connection.effectiveType 提供了网络的类型,如 '4g''3g',代表网络速度。
  • connection.downlink 提供了网络的下载速度信息,单位是 Mbps。
  • connection.rtt 提供了来回时间信息,单位是毫秒。

2. 观测发送请求的速度

通过发送一个小请求(可能是一个小文件或 API 请求)并测量它完成的时间,可以粗略地估计当前的网络速度。

let startTime = new Date().getTime(); // 记录开始时间
fetch("your-small-file-or-api-url").then((response) => {
  let endTime = new Date().getTime(); // 记录结束时间
  let duration = endTime - startTime; // 请求持续时间
  console.log(`请求持续时间: ${duration}ms`);
  // 根据持续时间和文件大小估计网速
});

3. 监听在线和离线事件

HTML5 引入了在线和离线事件监听,可以用来简单判断用户是否连接到网络。

window.addEventListener("online", () => console.log("网络已连接"));
window.addEventListener("offline", () => console.log("网络已断开"));

根据navigator.onLine的属性值,你可以检测用户是否在线。

if (navigator.onLine) {
  console.log("用户在线");
} else {
  console.log("用户离线");
}

结论

虽然无法精确地测量用户的网速,但以上方法提供了一些手段来估计用户的网络状况。这样的信息可以用来动态调整网站或应用的行为,例如,通过降低图像质量、推迟非关键资源的加载或取消某些动画,以改善慢速连接下的用户体验。

859. 在页面关闭时执行方法,该如何做【热度: 334】【web 应用场景】【出题公司: 腾讯】

关键词:beforeunload 和 unload 事件

在页面关闭时执行特定的方法,你可以使用 window 对象的 beforeunloadunload 事件。不过,这两个事件有一些微妙的区别和适用场景。

使用 beforeunload 事件

beforeunload 事件在窗口、文档或其资源即将卸载时触发,这一点让它成为在页面关闭前提示用户保存未保存更改的理想选择。在绑定到该事件的处理函数中,你可以执行特定的逻辑,但请注意,按照现代浏览器的安全策略,除非你设置了 event.returnValue,否则不会显示自定义的离开提示信息。

window.addEventListener("beforeunload", (event) => {
  // 在这里执行你的清理逻辑或者其他操作
  // 例如,发送一个统计日志
  navigator.sendBeacon("/log", "用户即将离开页面");

  // 显示离开提示(大多数现代浏览器不支持自定义文本)
  event.returnValue = "您确定要离开此页面吗?";
});

使用 unload 事件

unload 事件在用户即将从页面导航走,或关闭页面时触发。你可以在这个事件的处理函数中执行不能阻止页面卸载的清理逻辑。不过需要注意,这个事件的执行时间非常短,某些操作(例如异步操作)可能无法完成。

window.addEventListener("unload", (event) => {
  // 执行简短的同步操作,例如发送统计信息
  // 注意:这种情况下 navigator.sendBeacon 是更好的选择
});

使用 navigator.sendBeacon

对于在页面卸载时需要发送数据到服务器的情况,使用 navigator.sendBeacon 方法是一种更可靠的方式。它有效地解决了通过异步 AJAX 请求可能导致的数据不被送出的问题。

window.addEventListener("unload", (event) => {
  navigator.sendBeacon("/log-out", "用户离开");
});

注意事项

  • 不是所有浏览器都完全一样地支持这些事件和 navigator.sendBeacon 方法。实施时应当考虑兼容性。
  • beforeunloadunload 事件中执行大量的同步操作或长时间运行的脚本可能会导致用户体验下降。推荐尽量使用简洁快速的逻辑。
  • beforeunload 事件可以控制是否提示用户离开页面的确认对话框,但自定义的确认对话框信息可能不被所有浏览器支持。
  • 使用 navigator.sendBeacon 来发送数据是因为它能在请求中携带足够的数据量,且即使页面卸载过程中也能确保数据被发送。

根据你的应用需求,选择合适的事件和方法,确保页面关闭时能够执行你的逻辑。

861. 长文本溢出,展开/收起如何实现【热度: 895】【web 应用场景】【出题公司: 阿里巴巴】

关键词:长文本溢出

长文本溢出展开/收起功能通常需要使用一些 JavaScript 来动态控制文本的显示状态,及 CSS 来处理文本的默认显示样式。以下是一个基本实现示例,展示了如何结合 HTML、CSS 和 JavaScript 来实现这个功能。

HTML 结构

我们定义一个容器来显示文本,并添加一个用于触发展开/收起操作的按钮。

<div id="textContainer" class="text-overflow">
  这是一段可能很长的文本,我们希望在一开始时只显示部分,点击“展开”按钮后显示全部内容,再次点击则“收起”文本。
</div>
<button id="toggleButton">展开</button>

CSS 样式

使用 CSS 设置文本的默认显示状态为隐藏超出部分,并且用省略号表示溢出。

.text-overflow {
  /* 设置一个高度限制,模拟文本“收起”时的状态 */
  max-height: 60px; /* 这个值根据需要调整 */
  overflow: hidden;
  position: relative;
  line-height: 20px; /* 根据实际情况调整 */
  padding-right: 20px;
}

JavaScript 代码

使用 JavaScript 来控制文本的“展开”和“收起”状态。我们监听按钮的点击事件来切换文本的显示状态。

document.getElementById("toggleButton").addEventListener("click", function () {
  var textContainer = document.getElementById("textContainer");
  var button = document.getElementById("toggleButton");

  // 检查当前是展开还是收起状态
  if (button.textContent === "展开") {
    // 修改文本容器的最大高度以显示全部文本
    textContainer.style.maxHeight = "none";
    button.textContent = "收起";
  } else {
    // 重新设置最大高度以隐藏文本
    textContainer.style.maxHeight = "60px"; // 与CSS中定义的相同
    button.textContent = "展开";
  }
});

这只是实现长文本溢出展开/收起的一种基本方法。根据具体需求,这个示例可以进一步扩展或修改,比如添加动画效果使展开/收起操作更平滑,或者根据文本长度动态决定是否显示“展开/收起”按钮等。

还有其他方法可以实现这一功能,包括使用纯 CSS 的技巧(虽然可能不那么灵活),或者利用现成的 JavaScript 库和框架来简化实现过程。

更有多实现细节, 可以参考以下文档

863. 如何实现鼠标拖拽【热度: 212】【web 应用场景】

关键词:拖拽 api、mousedownmousemovemouseup事件

实现鼠标拖拽功能通常涉及到监听和处理鼠标事件,比如:mousedownmousemovemouseup事件。下面是一个基本的步骤指南以及一个简易的示例代码(使用 HTML 和 JavaScript),展示了如何实现一个元素的鼠标拖拽功能。

基本步骤

  1. 监听mousedown事件: 当用户按下鼠标按钮时,记录被拖拽元素的初始位置,并设置一个标志(如isDragging)表示拖拽开始。

  2. 监听mousemove事件: 当用户移动鼠标时,如果拖拽已开始,则根据鼠标当前位置和初始位置的差值,更新被拖拽元素的位置。

  3. 监听mouseup事件: 当用户释放鼠标按钮时,清除拖拽开始的标志(如isDragging),表示拖拽结束。

示例代码

这里是一个简单的 HTML 和 JavaScript 示例,演示了如何让一个div元素可拖拽:

<!DOCTYPE html>
<html>
  <head>
    <title>鼠标拖拽示例</title>
    <style>
      #draggable {
        width: 100px;
        height: 100px;
        background-color: red;
        position: absolute;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div id="draggable"></div>

    <script>
      // 获取元素
      var draggable = document.getElementById("draggable");
      var isDragging = false;
      var offset = { x: 0, y: 0 };

      draggable.addEventListener("mousedown", function (e) {
        isDragging = true;
        offset.x = e.clientX - draggable.getBoundingClientRect().left;
        offset.y = e.clientY - draggable.getBoundingClientRect().top;
      });

      document.addEventListener("mousemove", function (e) {
        if (isDragging) {
          draggable.style.left = e.clientX - offset.x + "px";
          draggable.style.top = e.clientY - offset.y + "px";
        }
      });

      document.addEventListener("mouseup", function () {
        isDragging = false;
      });
    </script>
  </body>
</html>

注意事项

  • 这个示例仅作为演示使用,实际应用可能需要更多的错误处理和边界条件判断。
  • 为了防止拖拽时的文本选中现象,可能需要监听并阻止mousemove事件的默认行为。
  • 记得附加适当的样式(如cursor: move;),提升用户体验。

根据你的需要,这个基本的逻辑和代码可以进行调整和扩展,以实现更复杂的拖拽功能。

866. ResizeObserver 作用是什么【热度: 416】【web 应用场景】【出题公司: 美团】

关键词:ResizeObserver api

ResizeObserver 的作用是监测元素的尺寸变化。这是一种强大的 Web API,允许开发者在元素的尺寸发生改变时(无论是因为元素内容的变化、窗口大小的调整还是其他原因导致的尺寸改变),执行一些操作或布局更新。在过去,开发者通常需要依赖定时器或者窗口的 resize 事件来间接监测元素尺寸的变化,这种方法不仅不够精确,而且效率低下。ResizeObserver 提供了一种更为直接和高效的方式来响应尺寸变化。

如何使用 ResizeObserver

使用 ResizeObserver 很简单,你只需要创建一个 ResizeObserver 实例,并为它提供一个回调函数。在回调函数中,你可以基于元素尺寸的变化来执行相应的操作。然后,使用 observe 方法来指定需要被观察尺寸变化的元素。

示例代码

下面的示例代码展示了如何使用 ResizeObserver 来监测一个元素的尺寸变化,并在尺寸变化时输出新的尺寸信息:

// 监测的目标元素
const targetElement = document.querySelector(".resizable");

// 创建 ResizeObserver 实例
const resizeObserver = new ResizeObserver((entries) => {
  for (let entry of entries) {
    // entry.target 是被观察的元素
    // entry.contentRect 包含了元素的尺寸信息
    console.log("Element size changed:", entry.target);
    console.log(`New width: ${entry.contentRect.width}`);
    console.log(`New height: ${entry.contentRect.height}`);
  }
});

// 开始观察目标元素
resizeObserver.observe(targetElement);

应用场景

ResizeObserver 的常见应用场景包括:

  • 响应式布局:当容器的尺寸改变时,动态调整内容或布局,提供更好的响应式设计。
  • 图表和可视化:在图表或数据可视化的容器大小改变时,重新绘制图表来适应新的尺寸。
  • 动态元素(如弹出窗口和下拉菜单):监测并根据内容大小自动调整元素的尺寸。

867. 要实时统计用户浏览器窗口大小,该如何做【热度: 210】【web 应用场景】【出题公司: 美团】

关键词:resize 事件应用

要实时统计用户浏览器窗口大小,可以利用 JavaScript 中的 resize 事件。当浏览器窗口尺寸变化时,此事件会被触发。通过侦听此事件,可以实时获取并处理浏览器窗口的宽度和高度。

基础示例

下面是一个简单的示例,展示如何使用 resize 事件来获取并打印当前浏览器窗口的宽度和高度:

// 定义一个函数来处理窗口大小变化
function handleResize() {
  const width = window.innerWidth;
  const height = window.innerHeight;
  console.log(`当前窗口大小:宽度 = ${width}, 高度 = ${height}`);
}

// 在窗口 resize 事件上添加监听器
window.addEventListener("resize", handleResize);

// 初始化时执行一次,确保获取初始窗口大小
handleResize();

节流优化

如果你担心 resize 事件触发得太频繁,可能会影响页面性能,可以引入“节流”(throttle)机制来限制事件处理函数的执行频率。节流确保了即使事件持续触发,事件处理函数也只在每隔一段时间执行一次。

以下是如何应用节流优化的示例:

function throttle(fn, wait) {
  let inThrottle, lastFn, lastTime;
  return function () {
    const context = this,
      args = arguments;
    if (!inThrottle) {
      fn.apply(context, args);
      lastTime = Date.now();
      inThrottle = true;
    } else {
      clearTimeout(lastFn);
      lastFn = setTimeout(function () {
        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args);
          lastTime = Date.now();
        }
      }, Math.max(wait - (Date.now() - lastTime), 0));
    }
  };
}

// 使用节流函数包装我们的处理器
const throttledHandleResize = throttle(handleResize, 100);

// 添加节流化的事件监听
window.addEventListener("resize", throttledHandleResize);

这个 throttle 函数通过确保被包装的 handleResize 函数在指定的时间间隔(本例中为 100 毫秒)内最多只执行一次,来减少 resize 事件处理函数的调用频率。

应用场景

这样实时统计用户浏览器窗口大小的方法可以用于多种应用场景,如响应式布局调整、基于窗口大小动态加载资源、或者其他需要根据视窗大小变化进行调整的交互效果实现。

使用这种方法时,重要的是平衡事件处理函数的执行频率和页面的性能,特别是当你的窗口大小调整处理函数中包含复杂操作时。通过合理利用“节流”或“防抖”(debounce)技术,可以有效地解决这个问题。

868. flex 布局中,有哪些常用的属性,分别作用是啥【热度: 300】【CSS】【出题公司: 百度】

关键词:flex 属性

Flex 布局(即 Flexible Box 布局)提供了一种更有效的方式来布置、对齐和分布容器内项目的空间,即使它们的大小是未知或者动态变化的。以下是 Flex 布局中一些常用属性及其作用的简介:

容器属性(应用于 flex 容器)

  1. display

    • 设置为flexinline-flex以启用 flex 布局。
    • flex使容器成为块级元素;
    • inline-flex使容器成为行内元素。
  2. flex-direction

    • 确定主轴的方向(即项目的排列方向)。
    • 可选值包括row(默认,水平方向)、row-reverse(水平方向,反向)、column(垂直方向)、column-reverse(垂直方向,反向)。
  3. flex-wrap

    • 控制容器是单行还是多行,以及如何换行。
    • 可选值包括nowrap(默认,不换行)、wrap(换行,第一行在上方)、wrap-reverse(换行,第一行在下方)。
  4. flex-flow

    • flex-directionflex-wrap两个属性的简写形式。
    • 默认值为row nowrap
  5. justify-content

    • 定义了项目在主轴上的对齐方式。
    • 可选值包括flex-start(默认,起点对齐)、flex-end(终点对齐)、center(居中对齐)、space-between(两端对齐,项目之间的间隔相等)、space-around(每个项目两侧的间隔相等)、space-evenly(所有项目之间及周围的空间完全相等)。
  6. align-items

    • 定义项目在交叉轴上如何对齐。
    • 可选值包括flex-start(交叉轴的起点对齐)、flex-end(交叉轴的终点对齐)、center(交叉轴的中点对齐)、baseline(项目的第一行文字的基线对齐)、stretch(默认,如果项目未设置高度或设为 auto,将占满整个容器的高度)。
  7. align-content

    • 定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
    • 可选值和justify-content类似,包括:flex-startflex-endcenterspace-betweenspace-aroundstretch(默认值)。

项目属性(应用于 flex 项目)

  1. order

    • 定义项目的排列顺序。数值越小,排列越靠前,默认为 0。
  2. flex-grow

    • 定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大。
  3. flex-shrink

    • 定义项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。
  4. flex-basis

    • 设置或检索弹性盒伸缩基准值,默认值为auto,即项目本来的大小。
  5. flex

    • flex-growflex-shrinkflex-basis的简写,默认值为0 1 auto。后两个属性可选。
  6. align-self

    • 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。
    • 默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch
    • 可选的值除了auto,还有flex-startflex-endcenterbaselinestretch

869. 实现阿拉伯数字转中文【热度: 673】【代码实现/算法】【出题公司: 小米】

关键词:数字转中文

将阿拉伯数字转换成中文数字,主要考虑到以下几个转换规则:

  1. 基本数字:0-9 对应的汉字数字。
  2. 单位:十、百、千、万、亿等。
  3. 规则:数字从右到左,每 4 位一小节,小节内部和小节之间的转换规则。

实现思路

  1. 将阿拉伯数字分解成单个数字,从右到左进行处理。
  2. 对每 4 位数字进行处理,即一个小节,处理完再根据小节的位置添加对应的单位(万、亿等)。
  3. 处理当前小节内部的数字,并添加十、百、千的单位,注意去除连续的零,并且在必要时加入“零”字。
  4. 将各个小节合并得到最终结果。

下面的 JavaScript 函数实现了阿拉伯数字到中文数字的基本转换:

const number2text = (number, type = "upper") => {
  // 配置
  const confs = {
    lower: {
      num: ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"],
      unit: ["", "十", "百", "千", "万"],
      level: ["", "万", "亿"],
    },
    upper: {
      num: ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"],
      unit: ["", "拾", "佰", "仟"],
      level: ["", "万", "亿"],
    },
    decimal: {
      unit: ["分", "角"],
    },
    maxNumber: 999999999999.99,
  };

  // 过滤不合法参数
  if (Number(number) > confs.maxNumber) {
    console.error(`The maxNumber is ${confs.maxNumber}. ${number} is bigger than it!`);
    return false;
  }

  const conf = confs[type];
  const numbers = String(Number(number).toFixed(2)).split(".");
  const integer = numbers[0].split("");
  const decimal = Number(numbers[1]) === 0 ? [] : numbers[1].split("");

  // 四位分级
  const levels = integer.reverse().reduce((pre, item, idx) => {
    let level = pre[0] && pre[0].length < 4 ? pre[0] : [];
    let value = item === "0" ? conf.num[item] : conf.num[item] + conf.unit[idx % 4];
    level.unshift(value);

    if (level.length === 1) {
      pre.unshift(level);
    } else {
      pre[0] = level;
    }

    return pre;
  }, []);

  // 整数部分
  const _integer = levels.reduce((pre, item, idx) => {
    let _level = conf.level[levels.length - idx - 1];
    let _item = item.join("").replace(/(零)\1+/g, "$1"); // 连续多个零字的部分设置为单个零字

    // 如果这一级只有一个零字,则去掉这级
    if (_item === "零") {
      _item = "";
      _level = "";

      // 否则如果末尾为零字,则去掉这个零字
    } else if (_item[_item.length - 1] === "零") {
      _item = _item.slice(0, _item.length - 1);
    }

    return pre + _item + _level;
  }, "");

  // 小数部分
  let _decimal = decimal
    .map((item, idx) => {
      const unit = confs.decimal.unit;
      const _unit = item !== "0" ? unit[unit.length - idx - 1] : "";

      return `${conf.num[item]}${_unit}`;
    })
    .join("");

  // 如果是整数,则补个整字
  return `${_integer}元` + (_decimal || "整");
};

871. TypeScript 中 any、never、unknown、null & undefined 和 void 有什么区别【热度: 334】【TypeScript】

关键词:TS 类型

在 TypeScript 中,anyneverunknownnull & undefined 以及 void 都是类型系统的一部分,各自具有不同的用途和含义,下面是它们的主要区别:

any

  • 含义any 类型表示任何 JavaScript 值都可以赋值给它。使用 any 类型,可以绕过 TypeScript 的静态类型检查。
  • 用途:适用于你不想给变量设置具体类型的情况,或者在迁移旧 JavaScript 项目到 TypeScript 时临时使用。
  • 示例
    let anything: any = "Hello world";
    anything = 25; // ok
    anything = false; // ok
    

never

  • 含义never 类型表示永远不存在的值的类型。例如,never 类型是那些总是抛出异常或根本就不会有返回值的函数表达式或箭头函数的返回类型。
  • 用途never 用于表示那些总是异常或无限循环的函数返回类型,或者用在永远不可能有匹配结果的类型守卫条件。
  • 示例
    function error(message: string): never {
      throw new Error(message);
    }
    

unknown

  • 含义unknown 类型表示任何值。它类似于 any,但是更安全,因为对 unknown 类型的值执行大多数操作都是不允许的,直到我们通过类型检查缩小了该值的类型。
  • 用途:当我们不确定将要使用的变量的类型时可以使用 unknown 类型,它是 any 类型的类型安全等价物。
  • 示例
    let uncertainValue: unknown = 4;
    uncertainValue = "maybe a string instead";
    // TypeScript会阻止你执行不安全的操作
    // console.log(uncertainValue.length); // Error
    

null & undefined

  • 含义nullundefined 在 TypeScript 里分别有各自的类型,分别叫做 nullundefinednull 是一个表示无值的特殊值,而 undefined 表示未定义。
  • 用途nullundefined 分别用于表示变量的“空”或“未定义”状态。
  • 示例
    let empty: null = null;
    let notDefined: undefined = undefined;
    

void

  • 含义void 类型与 anyneverunknown 不同,它表示没有任何类型。在函数中使用 void 类型,表示该函数没有返回值。
  • 用途:主要用在没有返回值的函数的返回类型注解上。
  • 示例
    function warnUser(): void {
      console.log("This is a warning message");
    }
    

总结如下:

  • any 允许你对值执行任何操作,但是使用它会放弃类型检查的保护。
  • never 用于函数永远不会正常结束的返回类型。
  • unknown 用在不确定类型时,比 any 更安全因为它不允许你随便操作这个值。
  • nullundefined 用于表示没有值或值未定义。
  • void 用于没有返回任何值的函数。

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

858. 大文件切片上传的时候,确定切片数量的时候,有那些考量因素【热度: 366】【web 应用场景】【出题公司: 百度】

关键词:文件切片上传

大文件切片上传时,切片数量取决于几个关键因素:文件总大小、每个切片的大小(即切片大小),以及任何特定于应用或服务的限制。计算切片数量的过程包括确定合理的切片大小,然后根据文件总大小来计算需要多少个这样大小的切片。以下是一些步骤和考虑因素,可以帮助你确定切片数量:

1. 确定切片大小

  • 切片大小:首先,需要确定每个切片的大小。这通常是一个权衡的结果,考虑到效率、可靠性和服务器限制。太小的切片会增加请求的数量,降低效率;而太大的切片可能会增加单个请求失败的风险,并且对于每次请求消耗更多的内存和带宽。
  • 通常,切片大小选取在 1MB10MB 之间比较合适,当然这取决于具体应用和网络环境。

2. 计算切片数量

  • 文件总大小:知道文件的总大小后,可以通过简单的数学计算来决定切片的数量。公式如下:

    切片数量 = 向上取整(文件总大小 / 每个切片的大小)
    
  • 例如,如果文件是 50MB,每个切片大小为 5MB,则切片数量为 10

3. 考虑特殊情况

  • 最后一个切片可能会小于你设定的标准切片大小,这是正常情况,需要在上传逻辑中进行处理。

4. 示例代码

function calculateChunks(fileSize, chunkSize) {
  // 文件总大小(byte),切片大小(byte)
  const chunksCount = Math.ceil(fileSize / chunkSize);
  return chunksCount;
}

// 示例:文件大小 52MB,切片大小 5MB
const fileSize = 52 * 1024 * 1024; // 52MB
const chunkSize = 5 * 1024 * 1024; // 5MB
const chunksCount = calculateChunks(fileSize, chunkSize);

console.log(`需要切片数量: ${chunksCount}`);

注意事项

  • 网络条件:切片大小可能需要根据网络环境调整。在网络条件较差的情况下,选择更小的切片大小可能更加可靠。
  • 服务器限制:某些服务器或云服务可能对上传文件的大小有限制。确保了解和遵守这些限制,以避免上传失败。
  • 并发上传:在选择切片大小和数量时,考虑是否会并行上传多个切片,因为这也会影响上传速度和效率。

通过以上步骤和考虑因素,你可以合理地决定大文件上传时的切片数量,以优化上传过程的效率和可靠性。

860. 如何统计用户 pv 访问的发起请求数量(所有域名的)【热度: 469】【web 应用场景】【出题公司: 百度】

统计用户 PV(页面访问量)期间发起的请求数量(涵盖所有域名)可以通过几种方法实现,包括使用浏览器的 Performance API、监听网络请求、或者通过服务端日志分析。每种方法有其优点和适用场景。下面是一些方法的简要说明和示例:

方法 1: 使用 Performance API

Performance API 提供了丰富的接口来访问和利用浏览器的性能数据。通过使用这个 API,可以获取到用户 PV 时所有资源请求的详细信息,包括请求的域名信息。

使用 PerformanceObserver 监听资源加载

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntriesByType("resource");
  console.log(`当前页面共发起了 ${entries.length} 个资源请求。`);
});
observer.observe({ entryTypes: ["resource"] });

这段代码会统计所有类型的资源加载(如图片、脚本、样式表等),不过它不会区别处理各个域名的请求。

方法 2: 监听所有网络请求 (XMLHttpRequest 和 Fetch API)

可以通过重新定义 XMLHttpRequest 和拦截 fetch API 的调用来监控所有通过这两种常见方法发起的网络请求。

拦截 XMLHttpRequest

(function () {
  let oldOpen = XMLHttpRequest.prototype.open;
  window.requestCount = 0;
  XMLHttpRequest.prototype.open = function () {
    window.requestCount++;
    console.log(`请求数量: ${window.requestCount}`);
    return oldOpen.apply(this, arguments);
  };
})();

拦截 Fetch API

let oldFetch = window.fetch;
window.fetch = function () {
  window.requestCount++;
  console.log(`请求数量: ${window.requestCount}`);
  return oldFetch.apply(this, arguments);
};

这两段代码分别拦截了 XMLHttpRequestfetch API 的请求,从而可以统计请求的总量。为了覆盖所有域名,代码没有对请求的 URL 进行筛选,但如果需要,可以通过分析 URL 来对请求进行分类统计。

方法 3: 服务端日志分析

如果你有权访问服务器日志,或者使用了像 Google Analytics 这样的分析工具,也可以通过分析服务端日志来统计 PV 过程中的请求数量。这种方法可能更适合统计对外部 API 的请求或者整体网站流量的分析。

方法选择建议

  • 如果你想要实时在前端捕获和反馈统计数据,建议使用Performance API或者通过拦截网络请求的方法。
  • 如果希望进行更全面的数据分析或长期趋势跟踪,服务端日志分析可能是更合适的选择。

862. [React] 性能调优中,如何确定哪个数据变化引起的组件渲染【热度: 500】【web 框架】【出题公司: 阿里巴巴】

关键词:react 渲染性能调优

帮助开发者排查是哪个属性改变导致了组件的 rerender。

直接接受 ahooks 里面的一个方法: useWhyDidYouUpdate

源码实现:

import { useEffect, useRef } from "react";

export type IProps = Record<string, any>;

export default function useWhyDidYouUpdate(componentName: string, props: IProps) {
  const prevProps = useRef<IProps>({});

  useEffect(() => {
    if (prevProps.current) {
      const allKeys = Object.keys({ ...prevProps.current, ...props });
      const changedProps: IProps = {};

      allKeys.forEach((key) => {
        if (!Object.is(prevProps.current[key], props[key])) {
          changedProps[key] = {
            from: prevProps.current[key],
            to: props[key],
          };
        }
      });

      if (Object.keys(changedProps).length) {
        console.log("[why-did-you-update]", componentName, changedProps);
      }
    }

    prevProps.current = props;
  });
}

864. 统计全站每一个静态资源加载耗时, 该如何做【热度: 564】【web 应用场景】【出题公司: 阿里巴巴】

关键词:统计资源加载耗时、PerformanceObserver PerformanceResourceTiming api 使用

要统计全站每一个静态资源(如图片、JS 脚本、CSS 样式表等)的加载耗时,你可以借助浏览器的 Performance API,特别是利用 PerformanceResourceTiming 接口来获取资源加载的详细时间信息。以下是一个基本的步骤指导和示例代码,展示如何实现这一功能:

步骤

  1. 使用 PerformanceObserver 创建一个 PerformanceObserver 实例来监听资源加载事件,能够实时收集性能数据,而且对性能影响较小。

  2. 过滤静态资源类型: 通过检查 initiatorType 属性,筛选出静态资源(例如 imgscriptcss 等)的加载事件。

  3. 计算和展示耗时: 对每个静态资源的加载耗时进行计算并展示。资源的耗时可以通过 duration 属性直接获取。

示例代码

以下是一个简单的 JavaScript 代码示例,展示了如何使用 PerformanceObserver 来统计全站静态资源的加载耗时:

// 创建性能观察者实例来监听资源加载事件
const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  for (const entry of entries) {
    // 过滤静态资源类型
    if (["img", "script", "css", "link"].includes(entry.initiatorType)) {
      console.log(`资源 ${entry.name} 类型 ${entry.initiatorType} 耗时:${entry.duration.toFixed(2)} 毫秒`);
    }
  }
});

// 开始观察 Resource Timing 类型的性能条目
observer.observe({ entryTypes: ["resource"] });

注意事项

  • 性能数据的准确性: 确保性能数据的准确性和实时性,你应该在页面加载的早期就开始监听资源加载事件,例如在 <head> 标签中就引入或嵌入这段脚本。
  • 跨域资源的时间信息: 如果你需要获取跨域资源的详细时间信息(如第三方字体或脚本),那么这些资源的服务器需要在响应头中包含 Timing-Allow-Origin 头。
  • 大量数据的处理: 如果页面包含大量静态资源,考虑如何存储、传输和分析这些数据,避免对性能和用户体验造成影响。

865. 如何防止前端页面重复请求【热度: 451】【web 应用场景】【出题公司: 京东】

关键词:重复请求问题

防止前端页面的重复请求是提升用户体验和减轻服务器压力的重要策略之一。下面是一些实用的方法来减少或防止重复请求:

1. 使用锁或标志位

设置一个锁(flag)或状态标志位来控制请求的发送。在首次点击或请求发起时设置该标志位,直到请求完成后再清除该标志,以阻止在请求未完成前再次发起相同的请求。

示例:

let isRequestPending = false;

function fetchData() {
  if (isRequestPending) {
    return; // 如果请求已经在进行中,则不再发起新的请求
  }

  isRequestPending = true; // 设置请求标志

  fetch("your-api-endpoint")
    .then((response) => response.json())
    .then((data) => {
      console.log("请求成功:", data);
    })
    .catch((error) => {
      console.error("请求失败:", error);
    })
    .finally(() => {
      isRequestPending = false; // 请求结束,无论成功或失败,都清除请求标志
    });
}

// 模拟用户多次点击
fetchData();
fetchData(); // 这个请求将不会被执行

2. 使用防抖(Debounce)和节流(Throttle)

防抖和节流是限制函数执行频率的两种常见技术,它们可以有效防止重复请求。

  • 防抖:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
  • 节流:在规定的时间内只能触发一次函数。如果这个时间内触发多次函数,只有一次生效。

示例:

使用 lodash 的 _.debounce_.throttle 方法:

import _ from "lodash";

// 防抖函数
const fetchDataDebounced = _.debounce(fetchData, 300);

// 节流函数
const fetchDataThrottled = _.throttle(fetchData, 300);

// 使用防抖或节流方法来减少函数执行频率
button.addEventListener("click", fetchDataDebounced);

3. 使用缓存结果(最佳办法)

对于一些数据不经常变化的请求,例如用户信息、配置数据等,可以将请求的结果缓存起来。下一次请求相同的资源时,先从缓存中读取数据,如果缓存有效,则无需再发起新的网络请求。

思路类似于下面这张图 前端面试第63期 - 2024.08.31 更新前端面试问题总结(20 道题)2024.08.31 更新前端面试问题总结

要达到这样的效果,可以设计一个请求缓存管理器,来管理并发的请求。如果有相同的请求(URL、参数、方法相同)时,只发起一次网络调用,然后将结果分发给所有等待的请求。这种模式通常可以通过一个简单的缓存对象来实现,该对象将请求的唯一标识作为键,对应的 Promise 作为值。

以下是一个基本实现的示例:

class RequestCache {
  constructor() {
    this.cache = new Map();
  }

  // 生成请求的唯一标识符,这里仅以 URL 和 Method 为例,实际可能需要包括请求体等
  generateKey(url, method) {
    return `${method}:${url}`;
  }

  // 执行请求的方法,接受 fetch 的所有参数
  request(url, options = {}) {
    const { method = "GET" } = options;
    const key = this.generateKey(url, method);

    // 检查缓存中是否有相同的请求
    if (this.cache.has(key)) {
      return this.cache.get(key);
    }

    // 没有相同的请求,发起新的请求
    const requestPromise = fetch(url, options)
      .then((response) => response.json())
      .then((data) => {
        // 请求成功后,将其从缓存中移除
        this.cache.delete(key);
        return data;
      })
      .catch((error) => {
        // 请求失败也应该从缓存中移除
        this.cache.delete(key);
        throw error;
      });

    // 将新的请求 Promise 保存在缓存中
    this.cache.set(key, requestPromise);

    return requestPromise;
  }
}

// 使用示例
const cache = new RequestCache();
const URL = "https://api.example.com/data";

// 假设这三个请求几乎同时发起
cache.request(URL).then((data) => console.log("请求1:", data));
cache.request(URL).then((data) => console.log("请求2:", data));
cache.request(URL).then((data) => console.log("请求3:", data));

这个简单的 RequestCache 类通过一个内部的 Map 对象管理缓存的请求。当一个新的请求发起时,它会首先检查是否已经有相同的请求存在。如果已存在,那么它只返回先前请求的 Promise;如果不存在,它会发起一个新的网络请求,并将请求的 Promise 存储在缓存中,直到请求完成(无论是成功还是失败)之后,再将其从缓存中移除。

请注意,这里的示例非常基础,且主要用于说明如何缓存并复用请求的结果。在实际应用中,你可能还需要考虑更多细节,比如如何更精细地处理 POST 请求的请求体内容、如何设置缓存的过期时间、错误处理策略、缓存大小限制等。

872. JS 项目逐步迁移到 TS 项目,该如何做【热度: 870】【TypeScript】【出题公司: 阿里巴巴】

关键词:TS 项目迁移

在 JavaScript 项目迁移到 TypeScript 的过程中确实会出现大量 JS 和 TS 文件共存的情况。要配置项目以使它们兼容并顺利运行,你需要进行以下设置:

1. 初始化 TypeScript 配置

首先,创建tsconfig.json文件来配置 TypeScript 编译选项。可以通过运行npx tsc --init来自动生成一个基础的配置文件。为了使 JavaScript 和 TypeScript 文件共存,你需要确保tsconfig.json中包含以下配置:

{
  "compilerOptions": {
    "allowJs": true, // 允许编译JavaScript文件
    "checkJs": false, // 禁用对JS文件的检查,使迁移更加平滑
    "outDir": "./dist", // 指定输出目录
    "target": "es5", // 目标编译版本
    "module": "commonjs", // 模块化标准,根据项目情况调整
    "strict": false, // 可以开始时设置为false,逐步提高严格性
    "esModuleInterop": true
  },
  "include": [
    "src/**/*" // 指定项目源代码目录
  ]
}

2. 配置构建工具

如果你的项目是基于 React 的,并且你希望在迁移过程中同时使用 TypeScript 和 Babel 来处理 JSX 和最新的 JavaScript 特性,以下是对上面 Webpack 示例的补充,以支持这些需求:

2.1. 安装必要的包

首先,你需要安装与 React、TypeScript、Babel 相关的 npm 包:

npm install --save react react-dom
npm install --save-dev typescript @types/react @types/react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev ts-loader @babel/core @babel/preset-env @babel/preset-react babel-loader

2.2. 配置 Babel

创建或更新项目根目录下的.babelrcbabel.config.json文件,以包含 React 的预设和对最新 ECMAScript 特性的支持:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

如果你使用 TypeScript,你还可以在 Babel 配置中添加@babel/preset-typescript,这样 Babel 也可以直接处理.ts.tsx文件:

{
  "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"]
}

2.3. 配置 Webpack

更新webpack.config.js配置以使用babel-loader,并确保它能够正确处理.js.jsx以及.ts.tsx文件:

const path = require("path");

module.exports = {
  entry: "./src/index.tsx", // 假设你的入口文件是一个TypeScript文件
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  resolve: {
    // 添加.ts 和 .tsx 作为解析扩展名,确保导入时可以省略扩展名
    extensions: [".tsx", ".ts", ".jsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/, // 同时匹配 TS(TSX) 和 JS(JSX) 文件
        exclude: /node_modules/,
        use: {
          loader: "babel-loader", // 使用Babel加载器处理
          options: {
            // 在此传递Babel预设也是可行的,但最好在Babel配置文件中统一配置
            presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
          },
        },
      },
    ],
  },
  // 如果你需要的话,加入source map支持
  devtool: "source-map",
  // 配置 webpack-dev-server
  devServer: {
    contentBase: "./dist",
    hot: true,
  },
};

通过上述配置,Webpack 将能够正确地处理你的 React 项目中的.js.jsx.ts.tsx文件。Babel 会负责转译 JSX 和 TypeScript,而 Webpack 会负责打包它们。

3. 逐步迁移

开始逐步将.js文件重命名为.ts文件,并解决任何类型错误。这可以逐个文件进行,以避免项目变得不可管理。一般建议先从项目的底层(即不依赖其他文件或依赖较少的文件)开始迁移,逐步向上。

4. Linting

为了保证代码质量,在项目中配置 ESLint 是个好主意。如果还没有配置 ESLint,你可以如下安装:

npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev

然后,在.eslintrc文件中配置 ESLint 来支持 TS:

{
  "parser": "@typescript-eslint/parser",
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "rules": {
    // 自定义规则
  }
}

5. 运行和测试

确保你的运行和测试脚本兼容 TS 文件。可能需要配置或更新一些依赖,比如使用ts-node而不是node来运行 TS 文件,或者更新 Jest 配置以支持 TS。

通过上述步骤,你的项目应该能够在迁移到 TypeScript 的同时继续正常运行和构建。记住,这是一个逐步的过程,不需要急于一时完成所有迁移。

873. tsconfig.json 中有哪些重用的配置项【热度: 289】【TypeScript】【出题公司: 阿里巴巴】

关键词:TS 配置项

tsconfig.json是 TypeScript 项目的配置文件,它指定了用于编译该项目的根文件及编译器选项。以下是一些重要的配置项和它们的作用:

compilerOptions

这部分包含了一系列用来告诉 TypeScript 编译器如何编译代码的标志。

  • target: 设置编译后的 JavaScript 目标版本,比如"ES5""ES6"等。
  • module: 指定生成的代码所使用的模块系统,如"CommonJS""AMD""System""UMD""ES6""ES2015"等。
  • lib: 指定编译过程中需要包含的库文件的列表,如["dom", "es6"]等。
  • outDir: 指定输出目录,编译后的文件将放在这个目录下。
  • outFile: 将所有文件输出到一个文件中,仅在module"system""amd"时有效。
  • rootDir: 指定输入文件的根目录,用于控制输出目录结构。
  • allowJs: 允许编译.js文件,让 TypeScript 和 JavaScript 代码可以共存。
  • checkJs: 允许在.js文件中报告错误。
  • jsx: 在.tsx文件中支持 JSX,例如:"react"、"preserve"等。
  • declaration: 生成相应的.d.ts文件。
  • sourceMap: 生成相应的.map文件,用于调试。
  • strict: 启用所有严格类型检查选项。
  • noImplicitAny: 不允许具有隐式any类型的表达式和声明。
  • strictNullChecks: 在严格的null检查模式下,nullundefined值不包含在任何类型里,只允许用作它们各自的类型使用。
  • esModuleInterop: 通过为所有导入创建命名空间对象,实现 CommonJS 和 ES 模块之间的互操作性。

filesincludeexclude

这三个配置项控制 TypeScript 编译器应该编译哪些文件:

  • files: 指定一个确切的文件列表,只有这些文件会被编译。
  • include: 指定一个匹配模式列表,编译器会编译匹配上的文件。
  • exclude: 指定一个匹配模式列表以排除某些文件。

示例tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "jsx": "react",
    "outDir": "./dist",
    "esModuleInterop": true,
    "sourceMap": true,
    "allowJs": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

这只是tsconfig.json中常用配置项的概览。根据项目的不同需求,可能会有更多的配置项需要了解和调整。通过适当配置tsconfig.json文件,可以有效控制 TypeScript 项目的编译过程。

874. 如何开启 ts 类型强校验,ts 类型错误 webpack 直接编译失败【热度: 232】【TypeScript】【出题公司: 小米】

关键词:ts 类型强校验

要开启 TypeScript 的类型强校验,并使得 Webpack 在遇到类型错误时编译失败,可以通过以下步骤实现:

1. 开启 TS 严格模式

首先,在tsconfig.json中启用严格模式。这是通过设置"strict": true来实现的,这个选项会启用一系列严格的类型检查,帮助你写出更健壮的代码。

{
  "compilerOptions": {
    "strict": true,
    ...
  },
  ...
}

"strict": true基本等同于下面这些选项全部开启:

  • noImplicitAny
  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitThis
  • alwaysStrict

2. 使用ForkTsCheckerWebpackPlugin并配置使编译失败

当使用 Webpack 和ts-loader时,你可以通过安装并配置ForkTsCheckerWebpackPlugin来进行强类型校验。这个插件可以并行运行 TypeScript 类型检查器和 ESLint,并能在检测到错误时使 Webpack 编译失败。

首先,安装ForkTsCheckerWebpackPlugin(如果你还没安装):

npm install --save-dev fork-ts-checker-webpack-plugin

然后,在你的webpack.config.js配置文件中,修改ForkTsCheckerWebpackPlugin的配置,确保在遇到 TypeScript 类型错误时构建失败:

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
  ... // 其他webpack配置
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      async: false, // 关键配置:这将使得webpack等待TypeScript类型检查器和ESLint完成,如果发现任何错误都将导致构建失败
    }),
  ],
  ... // 其他webpack配置
};

注意

  • 设置async: false,会使得 Webpack 等待类型检查完成,如果有任何错误则构建失败。这对于生产构建很有用,但可能会降低开发环境的迭代速度。

  • 由于这个插件是并行在一个单独的进程中运行的,它不会延长 Webpack 的编译时间。然而,如果设置了async: false,则 Webpack 会等待该插件的结果,从而影响到构建的总时间。

转载自:https://juejin.cn/post/7409138396793110562
评论
请登录