阅读视图

发现新文章,点击刷新页面。

代码规范与提交

husky

husky 是一个用于简化Git钩子(hooks)的设置的工具,允许开发者轻松地在各种Git事件触发时运行脚本。例如,在提交之前(pre-commit)、推送之前(pre-push)、或者在提交信息被写入后(commit-msg)等。

husky的使用可以提高项目团队的工作效率,确保代码库中的代码符合特定的质量标准。它通常与lint-staged一起使用,以在提交前自动执行代码的静态检查。

使用husky包括以下简单步骤:

添加husky到项目依赖。 配置Git钩子,使用husky的配置。 当相应的Git事件被触发时,定义的脚本就会自动执行。

  • husky解决了git hook 的什么痛点? husky是如何将hooks跟package.json关联的?

git原生的钩子存储在项目的.git/hooks目录下,属于本地私有目录,不会被提交到git远程仓库。多人协作时候无法共享,可能出现配置不一样的情况。 安装husky后会修改git配置(git config),将git的原生钩子改成husky管理的脚本目录。开发者可以在packge.json和.husky进行配置

  • 在前端工作流中,husky 最常与哪些工具搭配使用?请至少列举两个,并说明 husky 在这个组合中扮演的角色。

eslint commitlint prettier。角色:通过钩子进行触发。

  • pre-commit 和 commit-msg 是两个最常用的 Git hook。请解释一下它们分别在什么时机触发,以及通常用它们来做什么?

Git 先触发 pre-commit 钩子(执行检查脚本) → 如果检查通过 → 直接使用 -m 后的内容作为提交信息,完成提交 → 如果检查失败 → 提交中断 检查代码语法和检查提交信息。

  • husky v4 与 v5+ 在安装、配置和工作原理上的主要区别

husky v4 的钩子规则(比如 “提交前要跑 ESLint”)虽然写在 package.json 里能被 Git 跟踪,但实际执行钩子的脚本文件藏在本地 .git/hooks(不被 Git 跟踪)。团队成员需要通过 npm install 手动触发脚本生成,本质是 “依赖安装步骤间接同步”,容易因为漏执行步骤导致钩子失效。

husky v5+  的钩子规则直接以  .husky 目录下的脚本文件 存在(比如 .husky/pre-commit),这些文件能被 Git 跟踪,所以 “规则可见”。团队成员拉取代码后,钩子脚本会直接同步到本地,再通过 prepare 脚本自动绑定 Git 配置,本质是 “依赖 Git 文件同步直接生效”,几乎不需要额外手动操作。 image.png

  • git commit 过程的变化:
    如果 pre-commit 钩子脚本以非零状态码退出(例如 ESLint 检查出错误,返回 1),Git 会立即中断整个提交流程,不会创建提交记录。终端会显示脚本的错误输出(如 ESLint 的报错信息),提示用户修复问题后重新提交。

  • husky 的实现方式:
    husky 本身并不改变 Git 钩子的原生机制 ——Git 规定:任何钩子脚本如果以非零状态码退出,后续提交流程都会终止。 ** husky 的作用是将用户定义的命令(如 eslint .)包装到 .husky/pre-commit 脚本中,当用户命令失败时,脚本会继承其非零退出码,从而触发 Git 的原生中断逻辑。简单说,husky 只是 “传递” 了命令的失败状态,借助 Git 本身的机制实现提交拦截。

  • 现在一些 CI/CD 工具(如 GitHub Actions, GitLab CI)也能在代码推送到远程仓库后执行检查。既然如此,为什么我们还需要在本地通过 husky 做提交前的检查?这两者是重复的吗?

反馈速度:本地检查比 CI 快 10 倍以上,如果没有本地检查,错误代码可能被提交到远程仓库,导致:CI 任务频繁失败,浪费团队共享的 CI 资源,CI/CD 的核心作用是确保合并到主分支的代码符合生产标准

  • 在一个大型项目中,pre-commit 钩子可能需要运行 lint、类型检查和单元测试,导致每次提交等待时间很长。你有什么策略或建议来优化这个体验?

配合lint-staged只对提交代码检查 ,ESLint:通过 --cache 选项启用缓存(eslint --cache),并行检查:run-p命令

lint-staged

lint-staged可以在git staged阶段的文件上执行代码检查(Linters),包括ESLint和Stylelint等。简单说就是,当开发者运行ESlint或Stylelint命令时,可以通过设置指定只检查通过git add添加到暂存区的文件,避免每次检查都把整个项目的代码都检查一遍,从而提高效率。

  •  lint-staged 如何获取 "staged files" 列表?调用了哪些 Git 命令?

lint-staged 本质是通过 Git 命令 查询暂存区状态来获取文件列表,核心命令是:

git diff --cached --name-only --diff-filter=ACMR
  • 配置中可以使用函数 例如对不同类型的文件进行不同的规则匹配,函数效果更好,能根据文件的动态信息生成个性化的命令。

  • lint-staged 如何处理大量暂存文件?如何避免命令行长度限制? 将文件列表拆分成多个 “批次”,每个批次的文件名拼接后不超过长度限制。对每个批次分别执行命令(如 prettier --write file1.js file2.js ...),直到所有文件处理完成。

commitlint

commitlint 是一个用于检查 Git 提交信息(commit message)是否符合规范的工具

  • 除了 commitlint,你还知道什么工具可以帮助开发者更方便地编写出符合规范的 Commit Message?

例如:cz-git 用 cz 生成信息 → commit-msg 钩子触发 commitlint 检查 → 检查通过才允许提交。

  • 请深入解释 commit-msg 这个 Git Hook 的工作机制。当 husky 调用 commitlint 时,commitlint 是如何获取到你正在编辑的 Commit Message 内容的?

通过将msg放入临时文件中,然后将文件路径交给钩子脚本去进行校验。校验失败返回非0,代码非0停止终端提交。

  • commitlint 内部是如何解析一个 Commit Message 字符串并进行规则校验的?你可以猜测一下它可能用到的技术或核心逻辑吗? 将字符串转换成结构化的数据,正则,配置,插件,错误处理。

eslint

ESLint 是一个针对 JavaScript(及衍生语言如 TypeScript、JSX)的静态代码分析工具。它通过预设或自定义的规则,在代码运行前检查潜在问题,比如语法错误、未使用的变量、不合理的逻辑结构,以及代码风格不一致(如缩进、引号类型)等,帮助开发者提升代码质量、减少 bug 并保持团队代码风格统一。

  • AST 的定义及 ESLint 如何利用 AST 检查代码

解析生成 AST:通过解析器将源代码转换为 AST,使代码的语法结构可被程序 “理解”。 遍历 AST 节点:ESLint 会深度遍历 AST 的每个节点,触发配置中规则对特定节点类型的监听。 规则校验节点:每个规则本质是一个函数,会针对特定节点类型(如 CallExpression)进行检查。例如,no-console 规则会监听 CallExpression 节点,判断其是否是 console.log 之类的调用,若是则标记为错误。

  • TypeScript 为何需要 @typescript-eslint/parser?与 Espree 的本质区别

ESLint 本身无法直接处理 TypeScript 代码,必须依赖 @typescript-eslint/parser,原因是:
TypeScript 包含大量 JavaScript 不具备的语法(如类型注解 : number、接口 interface、泛型 <T> 等),而 ESLint 默认的解析器 Espree 仅能解析标准 JavaScript 语法,无法识别 TS 特有语法,会导致解析失败。

prettier

Prettier 是一款专注于代码格式化的工具,核心功能是通过自动化方式,按照预设规则统一代码的格式风格(如缩进、引号、换行、空格等)。

  • Prettier 与 ESLint/TSLint 的核心区别

Prettier:只负责代码格式(如缩进、引号、换行),不关心代码质量(如未定义变量、死循环、不合理的逻辑)。 ESLint/TSLint:主要负责代码质量检查(如语法错误、变量未使用、不符合最佳实践的逻辑),同时也包含部分格式规则(如缩进、分号)。

  • Prettier 的工作原理及与正则替换的本质区别

Prettier 的流程如下:文件遍历与过滤代码解析(生成 AST)AST 遍历与代码生成:通过AST能够理解代码,而不是直接通过正则。

  • eslint和Prettier的冲突

eslint-plugin-prettier 会把 Prettier 的格式化逻辑转换成一条 ESLint 规则(可以理解为 “将 Prettier 的规则注册到 ESLint 中”),这样 ESLint 在检查时,会把不符合 Prettier 格式的代码标记为 “ESLint 错误”。 eslint-config-prettier 则负责 “清场”,禁用 ESLint 自带的、可能与 Prettier 冲突的格式规则(比如 ESLint 的 indent 规则和 Prettier 的缩进逻辑可能不一致),避免出现 “同一处格式被两个工具判为‘错误’” 的矛盾。

Chrome Devtool

Elements 元素面板 使用元素面板可以自由操作 DOM 和 CSS 来迭代布局和设计页面 操作 DOM:复制,隐藏,展示,调整位置,设置断点

WebAssembly

JS在引擎中的过程

词法分析:分词生成token

一个个字幕扫描,与定义好的JavaScript关键字进行比较。token是不可分割的最小单元。

    [
      { type: 'Const', value: 'const', start: 0, end: 5 },
      { type: 'Identifier', value: 'a', start: 6, end: 7 },
      { type: 'Assign', value: '=', start: 8, end: 9 },
      { type: 'Number', value: '1', start: 10, end: 11, raw: '1' },
      { type: 'Semicolon', value: ';', start: 11, end: 12 },
    ]

语法分析:通过Parser生成AST(规定结构的对象)

image.png

生成中间字节码

由解释器生成可运行的字节码。(为了在跨平台的基础上做到提升效率,保护代码安全)

热点代码编译成机器码

被即时编译器转换成机器码。只有热点代码会编译成机器码,为了平衡启动速度和执行效率。热点代码(例如多次调用的函数,多次循环体等),引擎通过性能计数器监控代码执行频率,当达到阈值时触发编译。

可以看到,代码中的热点代码会被进行编译成机器码,下一次执行就能直接执行机器码来提高效率。但是我们JS中是动态类型的语言,一个变量可以赋值给不同的类型。就行这次翻译的机器码限定类型为Object类型,下次执行到发现是Array类型,那么又要重新进行编译。

asm.js出现

asm.js是一个Javascript的严格子集,合理合法的asm.js代码一定是合理合法的JavaScript代码,但是反之就不成立。同WebAssembly一样,asm.js不是用来给各位用手一行一行撸的代码,asm.js是一个编译目标

C / C++ 编译成 JS 有两个最大的困难。

  • C / C++ 是静态类型语言,而 JS 是动态类型语言。
  • C / C++ 是手动内存管理,而 JS 依靠垃圾回收机制。

asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。 除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。

asm.js强制静态类型,举个例子。

function asmJs() { 
'use asm'; 
let myInt = 0 | 0; 
let myDouble = +1.1; 
}

WebAssembly

如果你对 JS 比较了解,可能知道还有一种叫做 WebAssembly 的技术,也能将 C / C++ 转成 JS 引擎可以运行的代码。那么它与 asm.js 有何区别呢?

回答是,两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。从长远来看,WebAssembly 的前景更光明。

优点:

  1. 🚀 性能接近原生 (Near-Native Performance)

这是 Wasm 最广为人知的优点。通过预编译的二进制格式和高效的 JIT 编译到原生机器码,Wasm 在执行 CPU 密集型任务时远超传统 JavaScript。

  • 应用场景:3D 游戏、视频/音频处理、科学计算、加密、物理模拟。
  1. 🌐 语言生态的极大扩展 (Language Ecosystem Expansion)

Wasm 允许开发者使用他们最擅长的语言来为 Web 编写高性能模块,而不仅仅局限于 JavaScript。

  • 支持的语言:C, C++, Rust 是“一等公民”,Go, C#, Swift, Python, Java 等也通过不同工具链得到了支持。
  • 核心价值:这意味着可以将数十年积累下来的、经过实战检验的非 JS 库和应用移植到 Web 上。

缺点:

  1. 🌉 与 JavaScript 的交互开销 (JavaScript Interop Overhead)

这是目前 Wasm 最大的性能陷阱。频繁地在 JS 和 Wasm 之间来回调用函数或传递数据是非常昂贵的。

  • 问题根源:数据需要在线性内存和 JS 对象之间来回复制和转换。

  • 负面影响:如果使用不当(例如,用 Wasm 频繁操作 DOM),性能可能比纯 JS 实现还要差得多。

  1. 🕸️ 无法直接访问 Web API (No Direct Web API Access)

Wasm 被关在沙箱里,它不能像 JS 那样直接调用 document.getElementById() 或 fetch()。所有这些操作都必须通过 JS 作为“中间人”来完成,这增加了复杂性和“胶水代码”的数量。

❌