讲解npm dev命令执行流程,并介绍git-lrc AI审查工具。工具宣传性质为主,技术深度有限。
你好,我是 Maneshwar。我正在开发 git-lrc——一个会在每次 commit 时运行的微型 AI 代码审查工具。它完全免费,并已在 Github 上公开源代码。欢迎为 git-lrc 点 Star,帮助更多开发者发现这个项目。也请试用一下,并分享你的反馈,帮助我们改进项目。
你这辈子大概已经运行过 1,000 次 npm run dev 了。
你看着终端闪烁。
然后切换到浏览器。
但你真的知道背后发生了什么吗?我是说,真正了解吗?
今天,我们将一路深入底层,理解 shell、进程生成、模块图、Hot Module Replacement 和 React Fast Refresh。
npm run dev
shell 会在 PATH 中查找 npm 并找到它——通常位于 nvm 安装 Node 的某个目录中,或者 /usr/local/bin/npm 这样的系统路径下。随后,它会把控制权交给 npm CLI,而 npm CLI 本身其实只是一个 Node.js 脚本。
接下来,npm 只做一件事:读取你的 package.json。
{
"scripts": {
"dev": "vite"
}
}
它找到 "dev": "vite",然后准备运行这条命令。
npm 会临时把 ./node_modules/.bin 添加到你的 PATH 中,然后生成一个运行 dev 命令的子进程。
因此,"vite" 会解析到 ./node_modules/.bin/vite。它是一个符号链接,指向 node_modules/vite/bin/vite.js 中真正的 Vite 可执行文件。
没有什么魔法。只是 Node 在运行一个 JS 文件。
Vite 会读取你的 vite.config.js(或 .ts)文件、注册插件,并在提供任何内容之前完成一项非常巧妙的工作。
Vite 使用由 Go 编写、速度快得离谱的 esbuild,对 node_modules 中的依赖进行预处理。
将 CommonJS 转换为 ESM。浏览器无法原生加载 require(),因此 esbuild 会重写它。
把包含大量内部文件的 package 合并成一个文件。lodash 有 600 多个小文件,esbuild 会把它们合并起来,这样浏览器只需发起一个请求,而不是 600 个。
处理结果会缓存在 .vite/deps/ 中。动过 node_modules?缓存就会失效。否则,重新启动时会完全跳过这一步。
Vite 会在 localhost:5173 上启动一个普通的 Node.js HTTP server。
没有 webpack dev server 的魔法,也没有复杂的 middleware 调用链。
当浏览器请求一个文件时,Vite 会:
.css → 注入的 <style>)浏览器自身会通过 <script type="module"> 处理模块解析。
关键点在于:Vite 在 dev mode 下根本不会打包你的源代码。
浏览器会把每个文件作为 ES module 单独获取。
这就是为什么无论项目有多大,Vite 的 dev 启动速度都接近瞬时完成——它只处理浏览器真正请求的内容。
每一条箭头都代表一次真实的 HTTP 请求。浏览器正在替你遍历模块图。
随着文件陆续被请求,Vite 会在内部构建一个模块依赖图,用于记录谁导入了谁。
这个图是惰性构建的,只有被请求过的节点才会存在于图中。
如果你从未进入某个路由,对应的组件就永远不会被获取,也不会出现在图中。
正是这种结构让 HMR 如此迅速。
当文件发生变化时,Vite 不会扫描你的全部代码。
它只需要遍历这个图。
Vite 使用 chokidar,它对 OS 原生的文件系统事件进行了封装:
完全不需要轮询。编辑器把内容写入磁盘的那一刻,OS 就会立即拍一拍 Vite 的肩膀。
Vite 从发生变化的文件开始,沿模块图向上遍历,寻找第一个通过 import.meta.hot.accept() 注册了 HMR handler 的祖先模块。
使用 React 时,你不需要亲自编写 import.meta.hot.accept()。
@vitejs/plugin-react 插件使用 React Fast Refresh,在转换阶段自动把它注入每一个组件文件中。整个过程对你不可见。
如果一路向上直到 main.jsx 都没有找到边界,就会执行整页刷新。
当你编辑 vite.config.js,或者编辑一个不支持 HMR 的非组件 JS 工具文件时,就会看到这种情况。
Vite 只会让 Button.jsx 重新通过它的插件流水线,包括 JSX 转换、所有 Babel 插件等。
最终结果是内存中的一个全新 ES module。
HMR client(Vite 注入页面的一小段脚本)会收到有更新可用的通知,然后调用:
import('/components/Button.jsx?t=1718023456789')
其中的 ?t=timestamp 查询参数用于绕过缓存。浏览器看到一个从未获取过的 URL,就会发起一次真实的 HTTP 请求,获得新模块。
Vite 的 HMR runtime 围绕两个函数构建。你需要在 import.meta.hot 上注册它们;使用 React 时,则由插件负责注册:
// In a module that wants to handle its own updates:
// Clean up before this module is replaced —
// cancel timers, unsubscribe, remove listeners.
import.meta.hot.dispose((data) => {
clearInterval(timer)
})
// Receive the freshly imported module and re-wire things.
import.meta.hot.accept((newModule) => {
// newModule is the updated exports
})
这里有一个细微但很重要的细节:Vite 的 HMR 并不会真的为调用链上游的所有模块替换原来导入的模块对象。
接受更新的模块,也就是 HMR 边界,需要负责获取新的 exports 并应用它们。
正是这个简化模型,让 Vite 可以跳过重写所有 importer 的昂贵工作;对于几乎所有真实的开发场景来说,这已经足够了。
你可以在 dispose 中取消 setInterval、退订 store,或者移除 event listener——也就是清理那些在旧模块直接被废弃时可能造成泄漏的内容。
关于 CSS:stylesheet 采用的路径略有不同。当 .css 文件发生变化时,Vite 不会动态重新导入一个 JS module,而是将 <link>(或 <style>)标签替换为更新后的 stylesheet,从而避免出现无样式内容闪烁。
React Fast Refresh 是 React 团队构建的一套独立系统。当新的组件模块到达时,它会:
useState、useEffect,更新后仍然包含 useState、useEffect,那么 state 就会保留。添加或删除了一个 hook?只会重置这个组件的 state。useState、useEffect,更新后仍然包含 useState、useEffect,那么 state 就会保留。添加或删除了一个 hook?只会重置这个组件的 state。这就是为什么你可以在表单填写到一半时修改组件样式,而表单不会被重置。表单 state 存在于父组件或兄弟组件中——Fast Refresh 根本没有动过它。
如果你使用 webpack dev server,保存文件会触发整个 bundle 的重新构建,即使启用了 HMR 也是如此。对于中等规模的项目,这可能意味着从保存文件到浏览器完成更新之间会出现明显的延迟。
借助 Vite 的原生 ESM 方案,server 几乎什么都不用做——只转换一个文件;浏览器只获取一个 URL;Fast Refresh 也只会更新一棵子树。
整个往返过程通常非常快,而且随着项目规模增长,依然能够保持这种速度。
这绝不是微不足道的改进。
它从根本上改变了你的工作方式——编辑 → 看到结果,几乎瞬间完成。
AI Agent 写代码很快,但它们也会在不通知你的情况下,悄悄删除逻辑、改变行为并引入 bug。你往往要到 production 环境中才会发现。
git-lrc 解决了这个问题。它会接入 git commit,在每一个 diff 落地之前进行审查。60 秒即可完成配置。完全免费。*
欢迎提供任何反馈,也欢迎参与贡献!它已经上线、公开源代码,任何人都可以直接使用。
| 🇩🇰 Dansk | 🇪🇸 Español | 🇮🇷 Farsi | 🇫🇮 Suomi | 🇯🇵 日本語 | 🇳🇴 Norsk | 🇵🇹 Português | 🇷🇺 Русский | 🇦🇱 Shqip | 🇨🇳 中文 | 🇮🇳 हिन्दी |
|---|
AI Agent 写代码很快,但它们也会在不通知你的情况下,悄悄删除逻辑、改变行为并引入 bug。你往往要到 production 环境中才会发现。
git-lrc 解决了这个问题。它会接入 git commit,在每一个 diff 落地之前进行审查。60 秒即可完成配置。完全免费。
看看 git-lrc 如何发现严重的安全问题,例如凭据泄漏、成本高昂的云端操作,以及 log statement 中的敏感信息。
🤖 AI Agent 会悄悄破坏代码:代码被删除、逻辑被修改、边界情况消失。直到问题进入 production,你才会注意到。
🔍 在代码发布之前发现问题。由 AI 驱动的 inline comment 会准确告诉你哪些内容发生了变化,以及哪些地方看起来不对劲。
部分评论可能仅对已登录的访客可见。请登录以查看全部评论。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。