Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
深入聊聊Node 异步和事件循环的底层实现和执行机制_创想鸟

深入聊聊Node 异步和事件循环的底层实现和执行机制

深入聊聊Node 异步和事件循环的底层实现和执行机制

Node 最初是为打造高性能的 Web 服务器而生,作为 JavaScript 的服务端运行时,具有事件驱动、异步 I/O、单线程等特性。基于事件循环的异步编程模型使 Node 具备处理高并发的能力,极大地提升服务器的性能,同时,由于保持了 JavaScript 单线程的特点,Node 不需要处理多线程下状态同步、死锁等问题,也没有线程上下文切换所带来的性能上的开销。基于这些特性,使 Node  具备高性能、高并发的先天优势,并可基于它构建各种高速、可伸缩网络应用平台。

本文将深入 Node 异步和事件循环的底层实现和执行机制,希望对你有所帮助。

为什么要异步?

Node 为什么要使用异步来作为核心编程模型呢?

前面说过,Node 最初是为打造高性能的 Web 服务器而生,假设业务场景中有几组互不相关的任务要完成,现代主流的解决方式有以下两种:

单线程串行依次执行。

多线程并行完成。

单线程串行依次执行,是一种同步的编程模型,它虽然比较符合程序员按顺序思考的思维方式,易写出更顺手的代码,但由于是同步执行 I/O,同一时刻只能处理单个请求,会导致服务器响应速度较慢,无法在高并发的应用场景下适用,且由于是阻塞 I/O,CPU 会一直等待 I/O 完成,无法做其他事情,使 CPU 的处理能力得不到充分利用,最终导致效率的低下,

而多线程的编程模型也会因为编程中的状态同步、死锁等问题让开发人员头疼。尽管多线程在多核 CPU 上能够有效提升 CPU 的利用率。

虽然单线程串行依次执行和多线程并行完成的编程模型有其自身的优势,但是在性能、开发难度等方面也有不足之处。

除此之外,从响应客户端请求的速度出发,如果客户端同时获取两个资源,同步方式的响应速度会是两个资源的响应速度之和,而异步方式的响应速度会是两者中最大的一个,性能优势相比同步十分明显。随着应用复杂度的增加,该场景会演变成同时响应 n 个请求,异步相比于同步的优势将会凸显出来。

综上所述,Node 给出了它的答案:利用单线程,远离多线程死锁、状态同步等问题;利用异步 I/O,让单线程远离阻塞,以更好地使用 CPU。这就是 Node 使用异步作为核心编程模型的原因。

此外,为了弥补单线程无法利用多核 CPU 的缺点,Node 也提供了类似浏览器中 Web Workers 的子进程,该子进程可以通过工作进程高效地利用 CPU。

如何实现异步?

聊完了为什么要使用异步,那要如何实现异步呢?

我们通常所说的异步操作总共有两类:一是像文件 I/O、网络 I/O 这类与 I/O 有关的操作;二是像 setTimeOutsetInterval 这类与 I/O 无关的操作。很明显我们所讨论的异步是指与 I/O 有关的操作,即异步 I/O。

异步 I/O 的提出是期望 I/O 的调用不会阻塞后续程序的执行,将原有等待 I/O 完成的这段时间分配给其余需要的业务去执行。要达到这个目的,就需要用到非阻塞 I/O。

阻塞 I/O 是 CPU 在发起 I/O 调用后,会一直阻塞,等待 I/O 完成。知道了阻塞 I/O,非阻塞 I/O 就很好理解了,CPU 在发起 I/O 调用后会立即返回,而不是阻塞等待,在 I/O 完成之前,CPU 可以处理其他事务。显然,相比于阻塞 I/O,非阻塞 I/O 多于性能的提升是很明显的。

那么,既然使用了非阻塞 I/O,CPU 在发起 I/O 调用后可以立即返回,那它是如何知道 I/O 完成的呢?答案是轮询。

为了及时获取 I/O 调用的状态,CPU 会不断重复调用 I/O 操作来确认 I/O 是否已经完成,这种重复调用判断操作是否完成的技术就叫做轮询。

显然,轮询会让 CPU 不断重复地执行状态判断,是对 CPU 资源的浪费。并且,轮询的间间隔很难控制,如果间隔太长,I/O 操作的完成得不到及时的响应,间接降低应用程序的响应速度;如果间隔太短,难免会让 CPU 花在轮询的耗时变长,降低 CPU 资源的利用率。

因此,轮询虽然满足了非阻塞 I/O 不会阻塞后续程序的执行的要求,但是对于应用程序而言,它仍然只能算是一种同步,因为应用程序仍然需要等待 I/O 完全返回,依旧花费了很多时间来等待。

我们所期望的完美的异步 I/O,应该是应用程序发起非阻塞调用,无须通过轮询的方式不断查询 I/O 调用的状态,而是可以直接处理下一个任务,在 I/O 完成后通过信号量或回调将数据传递给应用程序即可。

如何实现这种异步 I/O 呢?答案是线程池。

虽然本文一直提到,Node 是单线程执行的,但此处的单线程是指 JavaScript 代码是执行在单线程上的,对于 I/O 操作这类与主业务逻辑无关的部分,通过运行在其他线程的方式实现,并不会影响或阻塞主线程的运行,反而可以提高主线程的执行效率,实现异步 I/O。

通过线程池,让主线程仅进行 I/O 的调用,让其他多个线程进行阻塞 I/O 或者非阻塞 I/O 加轮询技术完成数据获取,再通过线程之间的通信将 I/O 得到的数据进行传递,这就轻松实现了异步 I/O:

1.png

主线程进行 I/O 调用,而线程池进行 I/O 操作,完成数据的获取,然后通过线程之间的通信将数据传递给主线程,即可完成一次 I/O 的调用,主线程再利用回调函数,将数据暴露给用户,用户再利用这些数据来完成业务逻辑层面的操作,这就是 Node 中一次完整的异步 I/O 流程。而对于用户来说,不必在意底层这些繁琐的实现细节,只需要调用 Node 封装好的异步 API,并传入处理业务逻辑的回调函数即可,如下所示:

const fs = require("fs");fs.readFile('example.js', (data) => {  // 进行业务逻辑的处理});

Nodejs 的异步底层实现机制在不同平台下有所不同:Windows 下主要通过 IOCP 来向系统内核发送 I/O 调用和从内核获取已完成的 I/O 操作,配以事件循环,以此完成异步 I/O 的过程;Linux 下通过 epoll 实现这个过程;FreeBSD下通过 kqueue 实现,Solaris 下通过 Event ports 实现。线程池在 Windows 下由内核(IOCP)直接提供,*nix 系列则由 libuv 自行实现。

由于 Windows 平台和 *nix 平台的差异,Node 提供了 libuv 作为抽象封装层,使得所有平台兼容性的判断都由这一层来完成,保证上层的 Node 与下层的自定义线程池及 IOCP 之间各自独立。Node 在编译期间会判断平台条件,选择性编译 unix 目录或是 win 目录下的源文件到目标程序中:

2.png

以上就是 Node 对异步的实现。

(线程池的大小可以通过环境变量 UV_THREADPOOL_SIZE 设置,默认值为 4,用户可结合实际情况来调整这个值的大小。)

那么问题来了,在得到线程池传递过来的数据后,主线程是如何、何时调用回调函数的呢?答案是事件循环。

基于事件循环的异步编程模型

既然使用回调函数来进行对 I/O 数据的处理,就必然涉及到何时、如何调用回调函数的问题。在实际开发中,往往会涉及到多个、多类异步 I/O 调用的场景,如何合理安排这些异步 I/O 回调的调用,确保异步回调的有序进行是一个难题,而且,除了异步 I/O 之外,还存在定时器这类非 I/O 的异步调用,这类 API 实时性强,优先级相应地更高,如何实现不同优先级回调地调度呢?

因此,必须存在一个调度机制,对不同优先级、不同类型的异步任务进行协调,确保这些任务在主线程上有条不紊地运行。与浏览器一样,Node 选择了事件循环来承担这项重任。

Node 根据任务的种类和优先级将它们分为七类:Timers、Pending、Idle、Prepare、Poll、Check、Close。对于每类任务,都存在一个先进先出的任务队列来存放任务及其回调(Timers 是用小顶堆存放)。基于这七个类型,Node 将事件循环的执行分为如下七个阶段:

timers

这个阶段的执行优先级是最高的。

事件循环在这个阶段会检查存放定时器的数据结构(最小堆),对其中的定时器进行遍历,逐个比较当前时间和过期时间,判断该定时器是否过期,如果过期的话,就将该定时器的回调函数取出并执行。

pending

该阶段会执行网络、IO 等异常时的回调。一些 *nix 上报的错误,在这个阶段会得到处理。另外,一些应该在上轮循环的 poll 阶段执行的 I/O 回调会被推迟到这个阶段执行。

协和·太初 协和·太初

国内首个针对罕见病领域的AI大模型

协和·太初 38 查看详情 协和·太初

idle、prepare

这两个阶段仅在事件循环内部使用。

poll

检索新的 I/O 事件;执行与 I/O 相关的回调(除了关闭回调、定时器调度的回调和 之外几乎所有回调setImmediate());节点会在适当的时候阻塞在这里。

poll,即轮询阶段是事件循环最重要的阶段,网络 I/O、文件 I/O 的回调都主要在这个阶段被处理。该阶段有两个主要功能:

计算该阶段应该阻塞和轮询 I/O 的时间。

处理 I/O 队列中的回调。

当事件循环进入 poll 阶段并且没有设置定时器时:

如果轮询队列不为空,则事件循环将遍历该队列,同步地执行它们,直到队列为空或达到可执行的最大数量。

如果轮询队列为空,则会发生另外两种情况之一:

如果有 setImmediate() 回调需要执行,则立即结束 poll 阶段,并进入 check 阶段以执行回调。

如果没有 setImmediate() 回调需要执行,事件循环将停留在该阶段以等待回调被添加到队列中,然后立即执行它们。在超时时间到达前,事件循环会一直停留等待。之所以选择停留在这里是因为 Node 主要是处理 IO 的,这样可以更及时地响应 IO。

一旦轮询队列为空,事件循环将检查已达到时间阈值的定时器。如果有一个或多个定时器达到时间阈值,事件循环将回到 timers 阶段以执行这些定时器的回调。

check

该阶段会依次执行 setImmediate() 的回调。

close

该阶段会执行一些关闭资源的回调,如 socket.on('close', ...)。该阶段晚点执行也影响不大,优先级最低。

当 Node 进程启动时,它会初始化事件循环,执行用户的输入代码,进行相应异步 API 的调用、计时器的调度等等,然后开始进入事件循环:

   ┌───────────────────────────┐┌─>│           timers          ││  └─────────────┬─────────────┘│  ┌─────────────┴─────────────┐│  │     pending callbacks     ││  └─────────────┬─────────────┘│  ┌─────────────┴─────────────┐│  │       idle, prepare       ││  └─────────────┬─────────────┘      ┌───────────────┐│  ┌─────────────┴─────────────┐      │   incoming:   ││  │           poll            │<─────┤  connections, ││  └─────────────┬─────────────┘      │   data, etc.  ││  ┌─────────────┴─────────────┐      └───────────────┘│  │           check           ││  └─────────────┬─────────────┘│  ┌─────────────┴─────────────┐└──┤      close callbacks      │   └───────────────────────────┘

事件循环的每一轮循环(通常被称为 tick),会按照如上给定的优先级顺序进入七个阶段的执行,每个阶段会执行一定数量的队列中的回调,之所以只执行一定数量而不全部执行完,是为了防止当前阶段执行时间过长,避免下一个阶段得不到执行。

OK,以上就是事件循环的基本执行流程。现在让我们来看另外一个问题。

对于以下这个场景:

const server = net.createServer(() => {}).listen(8080);server.on('listening', () => {});

当服务成功绑定到 8000 端口,即 listen() 成功调用时,此时 listening 事件的回调还没有绑定,因此端口成功绑定后,我们所传入的 listening 事件的回调并不会执行。

再思考另外一个问题,我们在开发中可能会有一些需求,如处理错误、清理不需要的资源等等优先级不是那么高的任务,如果以同步的方式执行这些逻辑,就会影响当前任务的执行效率;如果以异步的方式,比如以回调的形式传入 setImmediate() 又无法保证它们的执行时机,实时性不高。那么要如何处理这些逻辑呢?

基于这几个问题,Node 参考了浏览器,也实现了一套微任务的机制。在 Node 中,除了调用 new Promise().then() 所传入的回调函数会被封装成微任务外,process.nextTick() 的回调也会被封装成微任务,并且后者的执行优先级比前者高。

有了微任务后,事件循环的执行流程又是怎么样的呢?换句话说,微任务的执行时机在什么时候?

node 11 及 11 之后的版本,一旦执行完一个阶段里的一个任务就立刻执行微任务队列,清空该队列。

在 node11 之前执行完一个阶段后才开始执行微任务。

因此,有了微任务后,事件循环的每一轮循环,会先执行 timers 阶段的一个任务,然后按照先后顺序清空 process.nextTick()new Promise().then() 的微任务队列,接着继续执行 timers 阶段的下一个任务或者下一个阶段,即 pending 阶段的一个任务,按照这样的顺序以此类推。

利用 process.nextTick(),Node 就可以解决上面的端口绑定问题:在 listen() 方法内部,listening 事件的发出会被封装成回调传入 process.nextTick() 中,如下伪代码所示:

function listen() {    // 进行监听端口的操作    ...    // 将 `listening` 事件的发出封装成回调传入 `process.nextTick()` 中    process.nextTick(() => {        emit('listening');    });};

在当前代码执行完毕后便会开始执行微任务,从而发出 listening 事件,触发该事件回调的调用。

一些注意事项

由于异步本身的不可预知性和复杂性,在使用 Node 提供的异步 API 的过程中,尽管我们已经掌握了事件循环的执行原理,但是仍可能会有一些不符合直觉或预期的现象产生。

比如定时器(setTimeoutsetImmediate)的执行顺序会因为调用它们的上下文而有所不同。如果两者都是从顶层上下文中调用的,那么它们的执行时间取决于进程或机器的性能。

我们来看以下这个例子:

setTimeout(() => {  console.log('timeout');}, 0);setImmediate(() => {  console.log('immediate');});

以上代码的执行结果是什么呢?按照我们刚才对事件循环的描述,你可能会有这样的答案:由于 timers 阶段会比 check 阶段先执行,因此 setTimeout() 的回调会先执行,然后再执行 setImmediate() 的回调。

实际上,这段代码的输出结果是不确定的,可能先输出 timeout,也可能先输出 immediate。这是因为这两个定时器都是在全局上下文中调用的,当事件循环开始运行并执行到 timers 阶段时,当前时间可能大于 1 ms,也可能不足 1 ms,具体取决于机器的执行性能,因此 setTimeout() 在第一个 timers 阶段是否会被执行实际上是不确定的,因此才会出现不同的输出结果。

(当 delaysetTimeout 的第二个参数)的值大于 2147483647 或小于 1 时, delay 会被设置为 1。)

我们接着看下面这段代码:

const fs = require('fs');fs.readFile(__filename, () => {  setTimeout(() => {    console.log('timeout');  }, 0);  setImmediate(() => {    console.log('immediate');  });});

可以看到,在这段代码中两个定时器都被封装成回调函数传入 readFile 中,很明显当该回调被调用时当前时间肯定大于 1 ms 了,所以 setTimeout 的回调会比 setImmediate 的回调先得到调用,因此打印结果为:timeout immediate

以上是在使用 Node 时需要注意的与定时器相关的事项。除此之外,还需注意 process.nextTick()new Promise().then() 还有 setImmediate() 的执行顺序,由于这部分比较简单,前面已经提到过,就不再赘述了。

总结

文章开篇从为什么要异步、如何实现异步两个角度出发,较详细地阐述了 Node 事件循环的实现原理,并提到一些需要注意的相关事项,希望对你有所帮助。

更多node相关知识,请访问:nodejs 教程!

以上就是深入聊聊Node 异步和事件循环的底层实现和执行机制的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/548924.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
iPhone直通手势旁白触感在哪开启
上一篇 2025年11月9日 19:09:37
圆通官网查询快递单号 圆通速递物流平台官网入口
下一篇 2025年11月9日 19:09:41

相关推荐

  • Bun 1.3 正式发布

    2025年10月10日,高性能 javascript 运行时 bun 发布了 1.3 版本。这是 bun 项目迄今为止最重大的版本更新,标志着 bun 从单纯的运行时工具演变为一个功能完备的全栈 javascript 开发平台。 从运行时到全栈平台的跨越 Bun 1.3 的核心突破在于将前端开发能力…

    2026年9月21日
    000
  • Hazelcast缓存数据无法通过Map获取的解决方案

    本文旨在解决在使用Spring Cache结合Hazelcast时,通过@CachePut注解成功将数据添加到缓存,但无法通过HazelcastInstance的getMap方法获取的问题。文章将详细介绍如何正确配置Spring Cache和Hazelcast,并提供代码示例和注意事项,确保缓存数据…

    2026年9月21日
    500
  • 使用本地HTML文件运行JavaScript脚本失败的原因及解决方案

    本文旨在帮助开发者理解在没有Web服务器的情况下,直接通过浏览器打开本地HTML文件时,JavaScript脚本可能无法正常运行的原因,并提供相应的解决方案。文章将深入探讨浏览器安全策略、相对路径问题以及如何正确引入和执行JavaScript脚本,确保你的HTML、CSS和JavaScript代码能…

    2026年9月21日
    000
  • PHPComposer怎么安装_PHPComposer依赖管理工具安装与使用指南

    PHPComposer是PHP的依赖管理工具,类似npm或pip。需先安装PHP,再下载并验证composer-setup.php,执行安装生成composer.phar,推荐全局安装至/usr/local/bin/composer,运行composer –version验证。使用com…

    2026年9月21日
    100
  • Java OOP如何使用内部类提高代码组织性

    内部类提升Java代码组织性与封装性,成员内部类增强封装,静态内部类分离逻辑,局部与匿名内部类简化回调,私有内部类隐藏实现细节。 内部类在Java面向对象编程中是一种有效提升代码组织性和封装性的工具。通过将一个类定义在另一个类的内部,可以更好地表达类之间的逻辑关系,控制访问权限,并减少命名冲突。合理…

    2026年9月21日
    100
  • mysql如何实现后台管理系统

    答案:基于MySQL的%ignore_a_1%需设计用户、权限、日志等表结构,通过后端语言实现安全的CRUD接口与JWT认证,前端展示数据并控制权限,确保系统安全稳定。 实现一个基于 MySQL 的后台管理系统,核心是构建一个安全、稳定、可扩展的系统架构,将数据库作为数据存储层,配合后端语言和前端界…

    2026年9月21日
    100
  • 怎样配置VSCode与Jest、Cypress等测试框架进行集成测试?

    首先安装Jest和Cypress插件及依赖,配置jest.config.js和.vscode/settings.json实现Jest自动运行,再通过launch.json添加Cypress调试配置,最后在package.json中定义统一脚本命令,使两者在VSCode中高效协同工作。 要在 VSCo…

    2026年9月21日
    000
  • JSF应用中Markdown文档动态链接处理指南

    本教程旨在解决jsf web应用程序中集成markdown文档时,如何动态处理内部链接以实现页面局部更新的问题。通过结合服务器端markdown渲染和客户端javascript事件监听,我们可以拦截markdown生成的html链接点击事件,利用ajax异步加载并渲染目标markdown文件,从而在…

    2026年9月21日
    600
  • VSCode的自动保存与文件监听功能如何结合以避免不必要的构建触发?

    通过配置VSCode自动保存延迟和构建工具防抖,减少频繁触发构建。设置”files.autoSave”: “afterDelay”与”files.autoSaveDelay”: 3000,结合Vite或Webpack的watch…

    2026年9月21日
    000
  • Linux怎么监控特定进程的运行状态

    Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态

    监控Linux进程需综合使用ps、top、htop、pgrep和systemctl等工具,结合资源占用、进程状态、日志输出和进程数量判断是否异常,并通过systemd的Restart机制或看门狗脚本实现自动重启,同时利用journalctl、sar、atop及Prometheus+Grafana等方…

    2026年9月21日 用户投稿
    100
  • Linux如何创建符号链接和硬链接

    Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接

    符号链接是快捷方式,指向文件或目录路径,原文件删除后链接失效;2. 硬链接共享同一inode,不能跨文件系统或链接目录;3. 使用ln -s创建符号链接,ln创建硬链接;4. 符号链接可跨分区,硬链接删除原文件后仍可访问数据。 在Linux中,创建符号链接(软链接)和硬链接是管理文件和目录的常用操作…

    2026年9月21日 用户投稿
    100
  • 从 API 响应中提取元素并在 Java 中使用

    本文介绍了如何在 Java 中解析 API 响应,并从中提取特定元素的值。以 JSON 格式的响应为例,演示了如何使用 Jackson 库将 JSON 字符串转换为 Java 对象,并提取所需的数据,例如账户 ID,以便在后续操作中使用。 在 Java 开发中,经常需要与 API 进行交互,并从 A…

    2026年9月21日
    100
  • 如何调整VSCode的设置以获得最佳性能?

    合理配置VSCode可显著提升性能。1. 禁用不必要扩展,减少后台资源占用;2. 在settings.json中设置files.watcherExclude和search.exclude以降低CPU负载;3. 启用editor.renderLineHighlight和largeFileOptimiz…

    2026年9月20日
    500
  • VSCode的扩展推荐是怎么工作的?

    VSCode的扩展推荐基于用户行为和项目环境智能生成,当你打开.py文件时会推荐Python相关工具,打开.ts、.vue等文件则触发对应语言插件;系统通过分析package.json、requirements.txt等依赖文件识别技术栈,推荐Docker、ESLint等匹配扩展;同时记录常用操作如…

    2026年9月20日
    000
  • Linux怎么查看进程使用的端口号

    答案是使用netstat、ss或lsof命令可查看Linux进程占用的端口。首先推荐ss命令,如ss -tulnp | grep 8080,能快速显示监听端口及对应进程;其次netstat -tulnp | grep 8080用法类似,但速度较慢;lsof -i :8080可精确查看指定端口的进程信…

    2026年9月20日
    100
  • 当VSCode启动或运行变慢时,有哪些系统性的排查和优化步骤?

    答案:VSCode变慢主要由扩展、文件监控和设置引起。先以安全模式启动排查扩展影响,使用内置性能工具分析启动耗时,优化工作区的文件监听与搜索范围,调整渲染设置并清理缓存,可显著提升运行效率。 VSCode 启动或运行变慢通常涉及扩展、设置、系统资源或文件索引等问题。以下是系统性的排查与优化步骤,帮助…

    2026年9月20日
    000
  • Linux如何解决rpm依赖关系错误

    Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误

    使用YUM可自动解决RPM依赖,通过yum localinstall安装本地包或yum install自动处理依赖;2. 较新系统推荐使用DNF,命令为dnf install 包名.rpm,依赖解析更高效;3. 无法使用YUM/DNF时可手动处理,通过rpm -ivh查看缺失依赖,再下载并按序安装;…

    2026年9月20日 用户投稿
    000
  • VSCode的侧边栏图标代表什么?

    资源管理器(文件夹图标)用于管理项目文件结构,支持新建、重命名、删除和拖拽操作;2. 搜索(放大镜图标)实现全局文本查找与替换,支持正则表达式及范围筛选;3. 源代码管理(分支图标)集成Git功能,可查看变更、提交代码并同步远程仓库;4. 运行和调试(虫子图标)支持断点调试、变量监控及多语言启动配置…

    2026年9月20日
    000
  • 如何为VSCode添加自定义主题?

    可通过安装扩展、手动添加JSON文件或使用Yeoman生成器创建主题。首先安装现成主题扩展并从颜色主题列表中选择应用;其次将自定义主题JSON文件放入用户themes目录后在命令面板中启用;最后可用yo code生成项目开发发布主题,需编辑配色文件并预览效果。 为 VSCode 添加自定义主题有几种…

    2026年9月20日
    000
  • edge浏览器无法打开本地HTML文件或显示空白怎么办_Edge浏览器打开本地HTML文件失败解决方法

    1、检查文件路径并选择Edge打开,确保路径为纯英文;2、在edge://flags中启用“Allow file access from files”;3、使用开发者工具排查资源加载错误;4、通过命令行启动Edge绕过安全限制;5、推荐使用npx live-server搭建本地服务器运行HTML文件…

    2026年9月20日
    000

发表回复

登录后才能评论
关注微信