无题
学会了网页里 JS 的调试,我们再来学下 Node.js 代码的调试。
我们准备一段 Node.js 的代码:
123456789const fs = require('fs/promises');(async function() { const fileContent = await fs.readFile('./package.json', { encoding: 'utf-8' }); await fs.writeFile('./package2.json', fileContent);})();
就是简单的文件读写,先用 node index.js 跑一下:
然后以调试模式启动,加个 –inspect-brk 参数:
1node --inspect-brk ./index.js
–inspect 是以调试模式启动,–inspect-brk 是以调试模式启动并且在首行断住。
然后你就会发现它打印了 ws 的地址:
...
无题
每个前端项目都有 npm scripts,我们会用 npm scripts 来组织编译、打包、lint 等任务。
大家可能经常会跑 npm scripts,但却对这些命令行工具是怎么实现的并不了解。
那如果想了解这些工具的实现原理,应该怎么做呢?
这就是今天的主题:调试 npm scripts。
这些命令行工具的 package.json 里都会有个 bin 字段,来声明有哪些命令:
npm install 这个包以后,就会放到 node_modules/.bin 目录下:
这样我们就可以通过 node ./node_modules/.bin/xx 来跑不同的工具了。
我们也可以用 npx 来跑,比如 npx xx,它的作用就是执行 node_modules/.bin 下的本地命令,如果没有的话会从 npm 下载然后执行。
当然,最常用的还是放到 npm scripts 里:
这样就直接 npm run xxx 跑就行了。
npm scripts 本质上还是用 node 来跑这些 script 代码,所以调试他们和调试其他 nod ...
无题
前面两节我们学习了怎么调试 Node.js、npm scripts,这节我们来过一遍 VSCode Node Debugger 的常用配置。
首先,从 attch 的方式开始:
attach有这样一个 Node.js 文件:
12345678910const http = require('http');const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ data: 'Hello World!' }));});server.listen(8888);
我们以调试模式启动:
然后 VSCode Debugger 添加一个 attach 类型的 Node 调试配置,端口是 9229:
点击调试启动就可以连上。
打个断点 ...
无题
作用域想必大家都知道,就是变量生效的范围,比如函数就会生成一个作用域,声明的变量只在函数内生效。
调试的时候,可以看到作用域的信息,这样的作用域一共有 9 种。
下面我们就一起过一遍这 9 种作用域吧:
Global 作用域通过 var 声明一个变量,打个断点,可以看到 Scope 里有 Global 类型的作用域,也就是全局作用域,里面保存了变量 a:
在浏览器环境下,可以通过 a 访问全局变量,也可以通过 window.a 访问。
Local 作用域声明个函数,在函数内声明一个变量,调用这个函数的时候,可以看到 Scope 里有 Local 类型的作用域,也就是本地作用域,里面保存了变量 b:
这两种作用域都很常见,没啥好说的。
Block 作用域es6 加入了块语句,它也同样会生成作用域:
如图,会把里面声明的变量 a 放到 Block 作用域内,也就是块级作用域。
if、while、for 等语句都会生成 Block 作用域:
前几种作用域很常规,但下面这种作用域绝大部分前端就不知道了:
Script 作用域这段代码大家觉得会生成什么作用域:
很多同学都会说,不是 ...
无题
Nest.js 是当下最流行的 Node.js 服务端框架,它建立在 Express 之上,实现了 IOC 的架构模式,并且对很多方案都有集成,比如 websocket、graphql 等。
当然,这节不是讲 Nest.js 的原理,而是讲如何调试 Nest.js 的项目,如何调试 Nest.js 的源码。
调试 Nest.js 项目Nest.js 提供了快速创建项目的命令行工具 @nest/cli,首先全局安装它:
1npm i -g @nestjs/cli
然后用 nest new nest-test 快速创建一个 nest 的项目。
进入项目目录,执行 npm run start 就会启动服务:
然后浏览器访问 http://localhost:3000 ,可以看到 Hello World,说明服务启动成功了。
然后我们创建一个 node 调试配置,指定 npm 为 runtime:
这里 console 要设置为 integratedTerminal,这样日志会输出在 terminal,就和我们手动执行 npm run start 是一样的。
不然,日志会输 ...
无题
前端的命令行工具太多了,比如 webpack、vite、babel、tsc、eslint 等等,每天我们都会用各种命令行工具。
这些命令行工具都提供了两种入口:命令行和 api。
平时用我们会通过命令行的方式,比如 eslint xxx –fix,但是别的工具集成这些工具的时候就会使用 api 了,它更灵活。
所以,调试这些工具的时候也就有两种方式,通过命令行调试和通过 api 调试。
这节我们以 eslint 为例子来试下两种调试方式,大家可以跟着调试一下。
命令行的方式调试 ESLint 源码我们创建一个 index.js 文件
配置下 .eslintrc
123module.exports = { extends: 'standard'}
安装 eslint,然后执行 npx eslint ./index.js 会看到这样的报错:
后面三个错误都是格式错误,eslint 可以修复。
执行 npx eslint ./index.js –fix 就会自动修复错误。
我们想探究下 fix 的原理,就要调试下源码了。
用 ...
无题
有时候我们需要修改 node_modules 下的一些代码,但是 node_modules 不会提交到 git 仓库,改动保存不下来,怎么办呢?
这时候可以用 patch-package 这个工具。
比如我对 node_modules 下的 acorn 代码做了一些修改:
加了一个 a.js 的文件:
在项目目录下执行 npx patch-package acorn 之后,就会生成这样一个目录:
在 patches 目录下的 xx.patch 文件里记录着对这个包的改动。
这个 patches 目录是可以提交到 git 仓库的,然后再次把项目拉下来的时候,执行下 npx patch-package 就会应用这次改动。
可以把它配到 postintsll 里,每次安装完依赖自动跑。
这样能保证每次拉取下来的代码都包含了对 node_modules 的改动。
如何使用我们学会了,那它是怎么实现的呢?
这节我们就来调试下 patch-package 的源码。
调试 patch-package 源码首先把代码下载下来:
1git clone git@github.com:ds300/ ...
无题
Babel 是一个 JS 的编译器,用于把高版本语法的代码转成低版本的,并且添加 polyfill。
它有很多插件,插件还进一步封装成了预设(preset),开箱即用。
此外,我们还可以写 Babel 插件来完成一些特定的代码转换。
Babel 是前端领域高频用到的工具,自然有必要去深入它的原理。所以这节我们就来调试下 Babel 的源码。
Babel 也是个命令行工具,也是有命令行和 api 两种形式的入口。今天我们通过 api 的方式来调试它。
它的编译流程分为三个阶段:
parse:把源码转成 AST
traverse:对 AST 做遍历,遍历过程中做增删改
generate:生成目标代码和 sourcemap
这三个阶段分别对应 @babel/parser、@babel/traverse、@babel/generator 三个包。
它的 API 是这样用的:
123456789101112131415161718192021222324const parser = require('@babel/parser');cons ...
无题
Vite 这两年比较火,大家应该都接触过它,但很少有人调试过它的源码。而想要深入理解它的原理,调试源码是必不可少的一步。
这节我们就一起来调试下 Vite 的源码。
首先,用 create-vue 创建一个 vite + vue3 的项目:
执行 npm init vue@3,然后进入项目目录,安装依赖。
之后 npm run dev 把 vite 的开发服务跑起来:
那么问题来了,vite 跑 dev server 的过程都执行了什么逻辑?
这就要调试源码来找答案了。
创建 .vscode/launch.json 的配置文件,添加一个 npm 类型的调试配置:
指定用调试的方式跑 npm run dev:
其他的都是默认配置,但要指定下 console 为 integratedTerminal,这样会把日志打在我们平时用的那个集成终端里。不然默认是打在这里的:
可以在 vite.config.js 这里打个断点:
因为这个 defineConfig 肯定是被 vite 调用的,我们可以看下 vite 处理配置的代码。
调试的方式跑,代码会在这里断住:
往下执行一 ...
无题
不知道大家对 ts 是否熟悉,你觉得下面这段 ts 代码,res 类型是什么?
123type Test<T> = T extends number ? 1 : 2;type res = Test<any>;
结果是 1 | 2 的联合类型:
很多同学会感到迷惑,为什么是这样呢?
这就要从源码找答案了,今天我们就来调试下 ts 源码来探究上面这个类型的求值过程。
首先,我们要把 ts 源码下载下来(加个 depth=1 可以下载单 commit,速度比较快),这里我下载的是 4.6.0 版本的源码
1git clone --depth=1 git@github.com:microsoft/TypeScript.git
然后可以看到 lib 目录下有 tsc.js 和 typescript.js,这两个分别是 ts 的命令行和 api 的入口。
但是,这些是编译以后的 js 代码,源码在 src 下,是用 ts 写的。
怎么把编译后的 js 代码和 ts 源码关联起来呢? sourcemap!
编译源码:
12yarn yarn run build ...
