标签

Prettier

Prettier 是一款流行的代码格式化工具。 它支持的语言相当多。 它很纯粹,就一个代码格式化工具,并不会做代码质量的检查。 Prettier 会强制使用统一的代码风格,原理就是解析语言生成AST 抽象语法树,然后用自己的一套风格写回到文件。

Prettier
查看更多相关内容
前端5月28日 07:28
Prettier 与其他代码格式化工具有什么区别?如何选择?## Prettier 和 ESLint 有什么本质区别? Prettier 是代码**格式化工具**,ESLint 是代码**质量检查工具**,二者不是替代关系而是互补关系。 核心区别在于工作原理:Prettier 将代码解析为 AST(抽象语法树),然后按照自己的规则重新输出,保证同样的输入永远得到同样的输出;ESLint 则基于规则引擎逐行扫描代码,检测潜在的错误和反模式。 实际项目中标准做法是两者结合:用 `eslint-config-prettier` 关闭 ESLint 中与格式化重叠的规则,让 Prettier 完全负责格式化(缩进、换行、引号风格),ESLint 专注代码质量(未使用变量、潜在 bug、最佳实践)。 ```json // .eslintrc.json { "extends": ["eslint:recommended", "prettier"], "plugins": ["prettier"] } ``` ## Prettier 相比 Beautify、Standard.js 的优势在哪? **vs Beautify**: Beautify 基于正则匹配做格式化,不具备 AST 解析能力,对复杂语法结构(如嵌套的三元表达式、链式调用)的格式化效果差,且输出不确定——同一份代码多次格式化可能产生不同结果。Prettier 基于 AST 重新打印代码,输出完全确定性,这是团队协作的基础。 **vs Standard.js**: Standard.js 是"零配置"的代名词,但它不允许任何自定义——分号必须有或必须没有,没有中间地带。Prettier 同样开箱即用,但保留了少量关键配置(单引号/双引号、分号、行宽等),适合需要一定灵活性的团队。 | 维度 | Prettier | Beautify | Standard.js | |------|----------|----------|-------------| | 解析方式 | AST | 正则 | AST | | 输出确定性 | 完全确定 | 不确定 | 完全确定 | | 可配置性 | 少量关键选项 | 丰富 | 几乎为零 | | 多语言支持 | JS/TS/CSS/HTML/JSON/MD | JS/CSS/HTML | JS/TS | ## Biome 等新一代工具会取代 Prettier 吗? 2026 年 Biome 成为最值得关注的替代方案。它用 Rust 编写,将格式化和 lint 合并为一个工具,在大型 monorepo 中性能优势显著:10,000+ 文件的项目,格式化+检查不到 200ms,而 ESLint+Prettier 组合需要近 12 秒。 但 Prettier 短期内不会被取代,原因有三: 1. **生态成熟度**: Prettier 拥有大量编辑器插件、预提交钩子、CI 集成方案,Biome 生态仍在追赶 2. **插件体系**: Prettier 支持插件格式化额外语言(如 Java、Ruby、PHP),Biome 目前语言覆盖有限 3. **迁移成本**: 已有项目的 `.prettierrc` 配置和格式化基线,切换工具意味着大量 diff **选择建议**: 新项目可以尝试 Biome,享受性能提升和简化配置;已有项目不必急于迁移,等 Biome 生态更成熟再说。 ## Prettier 的 AST 重打印机制是什么意思? 这是理解 Prettier 行为的关键。Prettier 的工作流程: 1. **解析(Parse)**: 将源代码解析为 AST 2. **遍历(Traverse)**: 遍历 AST 节点 3. **打印(Print)**: 根据行宽限制和自身规则重新输出代码 这意味着 Prettier 不是"调整"你的代码,而是"重新生成"你的代码。你写的空行、多余括号、手动对齐——大部分都会被丢弃重写。 这也是为什么 Prettier 配置选项少:它不是逐条规则控制,而是整体重打印,只暴露行宽、缩进等顶层参数。这种设计牺牲了灵活性,换来了确定性。 ## 实际项目中怎么配置 Prettier + ESLint? 完整的工程化配置分三步: **第一步:安装依赖** ```bash npm install -D prettier eslint eslint-config-prettier eslint-plugin-prettier ``` **第二步:配置文件** ```json // .prettierrc { "semi": true, "singleQuote": true, "printWidth": 80, "trailingComma": "es5" } ``` ```json // .eslintrc.json { "extends": ["eslint:recommended", "plugin:prettier/recommended"], "env": { "es2024": true, "node": true } } ``` `plugin:prettier/recommended` 做了三件事:加载 `eslint-plugin-prettier`(把 Prettier 规则作为 ESLint 规则运行)、加载 `eslint-config-prettier`(关闭 ESLint 格式化相关规则)、设置 `prettier/prettier` 为 error 级别。 **第三步:编辑器集成** ```json // .vscode/settings.json { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } } ``` 保存时先 Prettier 格式化,再 ESLint 自动修复,分工明确不冲突。 **第四步:Git 钩子自动化** ```bash npm install -D husky lint-staged npx husky init echo "npx lint-staged" > .husky/pre-commit ``` ```json // package.json { "lint-staged": { "*.{js,ts}": ["eslint --fix", "prettier --write"], "*.{css,html,json,md}": ["prettier --write"] } } ``` 提交时自动格式化和检查,不合格的代码进不了仓库。 ## Prettier 有哪些已知局限? **配置不够灵活**: 行宽以内无法手动换行,`printWidth: 80` 时超过 80 字符的链式调用会被强制换行,即使你手动排列得更易读。这是"确定性"的代价——不允许个人偏好覆盖工具判断。 **大项目性能瓶颈**: Prettier 是单线程的,超大型项目全量格式化耗时较长。应对方式是用 `lint-staged` 只格式化变更文件,或引入缓存。 **版本升级可能产生 diff**: Prettier 的格式化结果在不同大版本间可能有差异,团队必须锁定版本号,升级时全量格式化会产生大量无意义 diff。 ## 面试追问:什么时候不该用 Prettier? 三种场景下 Prettier 不是最佳选择: 1. **遗留大型项目**: 全量格式化会产生数千行 diff,干扰 code review,建议渐进式引入(只格式化新文件或变更文件) 2. **需要精细控制格式的场景**: 如代码生成器输出、教学材料中特意安排的缩进,Prettier 的重打印会破坏这些刻意格式 3. **纯 Python 项目**: Python 有 Black,设计理念与 Prettier 一致但针对 Python 语法优化,混用 Prettier 反而增加复杂度
前端5月28日 07:26
如何在 CI/CD 中集成 Prettier 做代码格式检查?## 为什么要用 Prettier 拦截代码格式问题 代码格式不一致是团队协作中最容易引发无意义争论的问题。Prettier 通过"零配置强制统一"的思路消除了这类争议,但仅靠开发者自觉运行 Prettier 并不可靠——有人会忘记格式化,有人会选择性忽略。把 Prettier 检查嵌入 CI/CD 流水线,是保证代码库格式一致性的最后防线。 推荐的两层防护策略:本地 Git Hook 做即时拦截 + CI 流水线做兜底检查。前者让开发者在提交前就能发现问题,后者防止绕过 Hook 的代码进入主分支。 ## 本地拦截:Git Hooks 配置 ### Husky + lint-staged 方案 这是目前最主流的方案,lint-staged 的核心优势是只格式化本次提交涉及的文件,不会全量扫描,提交速度有保障。 1. **安装依赖** ```bash npm install --save-dev husky lint-staged prettier npx husky install npm pkg set scripts.prepare="husky install" ``` 2. **配置 lint-staged** 在 `package.json` 中添加: ```json { "lint-staged": { "*.{js,jsx,ts,tsx}": [ "prettier --write" ], "*.{json,css,scss,md}": [ "prettier --write" ] } } ``` 分开配置的好处是后续可以为 JS/TS 文件加入 ESLint 检查,而不影响纯样式或文档文件。 3. **创建 pre-commit Hook** ```bash npx husky add .husky/pre-commit "npx lint-staged" ``` ### 常见坑:Husky 不生效 - **未执行 `husky install`**:克隆仓库后需要手动运行一次 `npm prepare` - **corehooks 被覆盖**:某些工具(如 Gerrit)会修改 Git hooks 路径,检查 `git config core.hooksPath` - **lint-staged 卡住**:文件路径含空格或中文时需要用引号包裹 glob 模式 ## CI 流水线集成 ### GitHub Actions 创建 `.github/workflows/prettier.yml`: ```yaml name: Prettier Check on: push: branches: [main] pull_request: branches: [main] jobs: prettier: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx prettier --check "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" ``` **关键细节**: - 用 `npm ci` 而非 `npm install`,前者严格按照 lock 文件安装,CI 环境更稳定 - `--check` 模式只检查不修改,符合 CI "只读" 原则 - 缩小 glob 范围到 `src/` 目录,避免扫描 node_modules 或构建产物 如果希望 PR 中直接看到哪些文件格式不对,可以用 `--list-different` 替代 `--check`,它会列出有问题的文件名,输出更直观: ```yaml - run: npx prettier --list-different "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" ``` ### GitLab CI 在 `.gitlab-ci.yml` 中添加: ```yaml prettier: stage: test image: node:20-alpine script: - npm ci - npx prettier --check "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" only: - merge_requests - main cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ ``` 用 `node:20-alpine` 镜像比完整 Node 镜像小 5 倍以上,流水线启动更快。加上 `cache` 配置避免每次都全量安装依赖。 如果想在 GitLab 的 Code Quality 报告中展示 Prettier 错误,可以使用 `@studiometa/prettier-formatter-gitlab` 将输出转为 GitLab 可识别的格式。 ### Jenkins 在 `Jenkinsfile` 中添加阶段: ```groovy stage('Format Check') { steps { sh 'npm ci' sh 'npx prettier --check "src/**/*.{js,jsx,ts,tsx}"' } } ``` ### Bitbucket Pipelines 在 `bitbucket-pipelines.yml` 中添加: ```yaml pipelines: pull-requests: '**': - step: name: Prettier Check image: node:20-alpine script: - npm ci - npx prettier --check "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" ``` ## Prettier 配置文件示例 CI 检查的准确性依赖项目中有明确的 Prettier 配置。创建 `.prettierrc`: ```json { "semi": true, "singleQuote": true, "trailingComma": "es5", "printWidth": 100, "tabWidth": 2 } ``` 同时创建 `.prettierignore` 排除不需要检查的内容: ``` node_modules dist build coverage *.min.js package-lock.json ``` **配置必须在本地和 CI 之间保持一致**——这也是为什么 Prettier 要作为 devDependencies 安装而非全局安装,`npm ci` 会确保 CI 环境拿到和本地完全相同的版本。 ## Prettier 与 ESLint 的协作 Prettier 只管格式,ESLint 管代码质量,两者配合才是完整方案。核心原则是**用 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的规则**: ```bash npm install --save-dev eslint-config-prettier ``` 在 `.eslintrc.js` 中: ```js module.exports = { extends: [ 'eslint:recommended', 'prettier' // 必须放在最后,覆盖前面的格式相关规则 ] } ``` CI 中可以合并为一条检查: ```yaml script: - npm ci - npx eslint "src/**/*.{js,ts}" - npx prettier --check "src/**/*.{js,ts,json,css,md}" ``` ## Monorepo 场景的优化策略 在 Turborepo 或 Nx 管理的 monorepo 中,全量 `npm ci` + 全局 Prettier check 会非常慢。两个优化方向: 1. **用 Turborepo 的 filter 定位变更包**: ```bash npx turbo run format:check --filter=...[HEAD^] ``` 只检查本次提交影响到的包。 2. **用 changesets 圈定范围**:在 CI 中先用 `git diff` 找出变更的包目录,再对对应目录执行 Prettier check。 3. **Prettier 的 `--cache` 选项**(Prettier 3.1+ 支持):只检查未缓存的文件,对大型仓库效果显著: ```bash npx prettier --check --cache "src/**/*.{js,ts}" ``` 缓存默认写入 `node_modules/.cache/prettier`,CI 中记得把这个目录加入缓存配置。 ## CI 检查失败怎么办 当 CI 报告格式不一致时,最快的修复方式是在本地执行: ```bash npx prettier --write "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}" ``` 如果频繁出现格式不一致,排查以下几点: - **Prettier 版本不一致**:检查 `package.json` 中的版本,确保本地和 CI 的 `npm ci` 安装的是同一个版本 - **编辑器格式化插件冲突**:VS Code 中可能同时有多个格式化扩展在生效,在设置中指定 Prettier 为默认格式化器 - **.editorconfig 与 .prettierrc 冲突**:两者同时存在时 Prettier 优先,但建议统一到 .prettierrc 中管理 ## 核心要点总结 - 本地 Git Hook 做即时反馈,CI 流水线做兜底保障,两者缺一不可 - `--check` 模式用于 CI,`--write` 模式用于本地修复,不要在 CI 中用 `--write` - 用 `npm ci` 代替 `npm install` 保证依赖版本一致性 - Prettier 与 ESLint 配合时,eslint-config-prettier 必须放在 extends 最后 - Monorepo 项目用 filter 或 `--cache` 缩小检查范围,避免全量扫描拖慢流水线
前端5月28日 07:26
Prettier 命令行工具有哪些常用命令和选项?## Prettier 命令行工具有哪些常用命令和选项? Prettier 的命令行工具是日常开发中格式化代码的核心手段,掌握常用命令和关键选项不仅能提升开发效率,也是前端工程化面试中的高频考点。 ### 核心命令 **格式化文件:`--write`** `--write` 是最常用的选项,直接修改文件为格式化后的内容: ```bash # 格式化单个文件 npx prettier --write src/index.ts # 格式化整个项目 npx prettier --write . ``` 面试追问:`--write` 会先写入临时文件再原子替换原文件,避免写入中断导致文件损坏。 **检查格式:`--check`** `--check` 只检查文件是否符合 Prettier 格式,不修改文件。文件不合规时退出码为 1,因此广泛用于 CI 流水线: ```bash npx prettier --check "src/**/*.{js,ts}" # 在 CI 中使用 npx prettier --check . || echo "存在未格式化的文件" ``` **列出差异文件:`--list-different`** `--list-different`(简写 `-l`)只输出格式不一致的文件路径,不输出格式化内容,适合脚本处理: ```bash npx prettier --list-different "src/**/*.js" ``` 与 `--check` 的区别:`--check` 会输出详细的人类可读信息,`--list-different` 只输出文件路径,更便于后续管道处理。 **查看差异:`--diff`** `--diff` 输出格式化前后的 diff 对比,方便在不修改文件的前提下预览变更: ```bash npx prettier --diff src/app.ts ``` ### 配置与忽略 **指定配置文件:`--config`** 默认 Prettier 会沿目录向上查找 `.prettierrc` 等配置文件,使用 `--config` 可指定自定义配置: ```bash npx prettier --config .prettierrc.staging.json --write src/ ``` **查找配置路径:`--find-config-path`** 输出给定文件实际使用的配置文件路径,用于排查配置生效问题: ```bash npx prettier --find-config-path src/index.ts # 输出: /project/.prettierrc ``` **忽略文件:`--ignore-path`** 默认使用 `.prettierignore`,可通过 `--ignore-path` 指定自定义忽略文件: ```bash npx prettier --ignore-path .gitignore --write . ``` 将 `.gitignore` 复用为忽略规则是一个实用技巧。 **忽略未知文件类型:`--ignore-unknown`** 格式化整个项目时,遇到 Prettier 不支持的文件类型默认会报错,加上此选项会自动跳过: ```bash npx prettier --write --ignore-unknown . ``` ### 缓存与性能 **启用缓存:`--cache`** 大型项目格式化耗时较长,`--cache` 通过缓存未变更文件的格式化结果显著提升速度: ```bash npx prettier --write --cache "src/**/*.ts" ``` **缓存位置:`--cache-location`** 指定缓存文件的存储路径: ```bash npx prettier --write --cache --cache-location .prettiercache src/ ``` **缓存策略:`--cache-strategy`** 支持两种策略: - `metadata`(默认):根据文件修改时间判断,速度快但不够精确 - `content`:根据文件内容哈希判断,更精确但稍慢 ```bash npx prettier --write --cache --cache-strategy content src/ ``` ### 输出控制 **输出到标准输出** 不加 `--write` 时,Prettier 将格式化结果输出到 stdout,不修改原文件: ```bash npx prettier src/index.ts ``` **指定输出目录:`--out-dir`** 将格式化结果写入指定目录而非原文件,适合生成格式化副本: ```bash npx prettier "src/**/*.js" --out-dir formatted/ ``` **标准输入:`--stdin-filepath`** 从标准输入读取代码时,Prettier 无法判断文件类型,通过此选项指定虚拟路径: ```bash echo "const x=1" | npx prettier --stdin-filepath index.ts ``` 这在编辑器集成和管道场景中非常关键。 ### 与工程化工具集成 **在 package.json 中配置脚本** ```json { "scripts": { "format": "prettier --write "src/**/*.{js,ts,json,css,md}"", "format:check": "prettier --check "src/**/*.{js,ts,json,css,md}"", "format:all": "prettier --write --ignore-unknown ." } } ``` **配合 lint-staged 只格式化暂存文件** ```json { "lint-staged": { "*.{js,ts,css,md}": "prettier --write" } } ``` 这样配合 husky 的 pre-commit 钩子,每次提交只格式化本次变更的文件,避免全量格式化带来的提交噪音。 **在 CI 中强制格式检查** ```yaml - name: Check formatting run: npx prettier --check . ``` `--check` 在文件不合规时返回退出码 1,CI 流水线会因此失败,确保仓库中不会混入未格式化的代码。 ### 调试命令 **调试检查:`--debug-check`** 格式化文件并检查格式化是否改变了 AST,用于排查 Prettier 自身的 bug。不能与 `--write` 同时使用: ```bash npx prettier --debug-check src/index.ts ``` **查看帮助与版本** ```bash npx prettier --help npx prettier --version ``` Prettier 命令行工具在日常开发中主要用于格式化和检查,在工程化体系中则通过 `--check` 与 CI 集成、通过 `--list-different` 与 lint-staged 配合,理解每个命令的应用场景比记住参数更重要。
前端5月28日 07:25
如何在 Monorepo 项目中配置和使用 Prettier?## 核心答案 在 Monorepo 中配置 Prettier,关键是**统一配置 + 分包覆盖 + 工具链集成**三步走:根目录放一份基础 `.prettierrc` 作为全局基准,通过共享配置包 `@org/prettier-config` 让各子项目继承,再用 `overrides` 按包定制差异规则,最后配合 Husky + lint-staged 在提交时自动格式化、Turborepo/Nx 在 CI 层做缓存检查。 ## 根目录统一配置 最简单的方式是在 monorepo 根目录创建 `.prettierrc`: ```json { "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 80 } ``` Prettier 会从文件所在目录向上查找配置,子包如果自己没有 `.prettierrc`,就自动继承根目录的规则。这意味着只要根目录配置到位,大部分子包无需额外配置。 需要注意的是,如果子包自己也有 `.prettierrc`,它会**完全覆盖**根配置而不是合并。所以除非有必要,不要在子包里单独放配置文件。 ## 共享配置包 当团队规模较大或 monorepo 包含多个独立发布的库时,推荐把 Prettier 配置抽成 npm 包: ```javascript // packages/prettier-config/index.js module.exports = { semi: true, singleQuote: true, tabWidth: 2, trailingComma: "es5", printWidth: 80, bracketSpacing: true, arrowParens: "always", }; ``` ```json // packages/prettier-config/package.json { "name": "@my-org/prettier-config", "version": "1.0.0", "main": "index.js" } ``` 在各子包中引用: ```json { "prettier": "@my-org/prettier-config" } ``` 共享配置包的优势在于版本可控——改一处发布新版本,所有依赖它的子包 `npm update` 即可同步。对于使用 pnpm workspace 的项目,直接用 `workspace:*` 协议引用,无需发布到外部 registry。 ## 分包差异化配置(overrides) 有些子包需要不同的格式化规则,比如 UI 库希望更宽的 `printWidth`,而后端服务保持 80 列。用 `overrides` 字段实现: ```json { "semi": true, "singleQuote": true, "printWidth": 80, "overrides": [ { "files": "packages/ui/**/*", "options": { "printWidth": 100 } }, { "files": "packages/server/**/*", "options": { "printWidth": 80 } }, { "files": "packages/docs/**/*.md", "options": { "proseWrap": "always", "printWidth": 90 } } ] } ``` `overrides` 是在根配置基础上**增量覆盖**,不会丢失未显式指定的规则。这比在每个子包单独放 `.prettierrc` 更容易维护。 ## .prettierignore 配置 很多教程忽略了 `.prettierignore`,但它在 monorepo 中非常关键。典型的忽略规则: ``` node_modules dist build coverage .next .turbo *.min.js *.min.css pnpm-lock.yaml package-lock.json ``` 不配 `.prettierignore` 会导致 `prettier --write` 扫描 `node_modules` 和构建产物,既浪费时间又可能报错。尤其在 monorepo 中,子包的 `dist` 目录层级深,手动排除不现实,需要用通配符一次搞定。 ## 与 ESLint 的冲突解决 Prettier 和 ESLint 同时存在时,格式化规则会冲突。比如 ESLint 要求尾逗号,Prettier 又删掉尾逗号,来回打架。解决方案分两步: **第一步**:安装 `eslint-config-prettier`,它关闭所有与 Prettier 冲突的 ESLint 规则: ```bash pnpm add -wD eslint-config-prettier ``` ```javascript // .eslintrc.js module.exports = { extends: [ "eslint:recommended", // 其他配置... "prettier" // 必须放最后,覆盖前面的格式化规则 ] }; ``` **第二步**(可选):如果想在 ESLint 中实时报告格式问题,安装 `eslint-plugin-prettier`: ```bash pnpm add -wD eslint-plugin-prettier ``` ```javascript module.exports = { plugins: ["prettier"], rules: { "prettier/prettier": "error" } }; ``` 不过在 monorepo 中,更推荐的做法是**分离职责**:ESLint 只管代码质量,Prettier 只管格式,不要把 Prettier 嵌入 ESLint。这样运行更快,调试也更清晰。 ## Husky + lint-staged 自动格式化 提交时自动格式化是 monorepo 的标配实践: ```bash pnpm add -wD husky lint-staged pnpm exec husky init ``` ```json // package.json { "lint-staged": { "*.{js,jsx,ts,tsx}": ["prettier --write"], "*.{json,css,md}": ["prettier --write"] } } ``` ```bash # .husky/pre-commit pnpm exec lint-staged ``` 这样每次 `git commit` 只会格式化**暂存区的文件**,而不是整个项目。对于 monorepo 来说,增量处理比全量扫描快得多。 如果使用 pnpm workspace,可以把 lint-staged 配置放在根目录,它会自动根据修改文件的路径匹配对应规则。 ## Turborepo 集成 Turborepo 的缓存机制能避免重复格式化检查: ```json // turbo.json { "pipeline": { "format": { "outputs": [] }, "format:check": { "outputs": [] } } } ``` ```json // 根 package.json { "scripts": { "format": "prettier --write .", "format:check": "prettier --check ." } } ``` `outputs` 设为空数组是因为格式化不产生构建产物,Turborepo 只需要根据输入文件的变化判断是否需要重新执行。 实际项目中,`format:check` 通常放在 CI 里,而 `format` 在本地开发时使用。Turborepo 会缓存未变更文件的结果,二次运行几乎零耗时。 ## Nx 集成 Nx 对 Prettier 有专门的 executor 支持: ```json { "targets": { "format": { "executor": "@nx/vite:format", "options": { "write": true } } } } ``` Nx 的优势在于**受影响项目检测**——只格式化当前提交影响到的子包: ```bash nx format:check --projects=tag:scope:ui nx format:write --projects=tag:scope:ui ``` 这在大型 monorepo 中比 `prettier --write .` 高效很多。 ## Lerna 集成 Lerna 的 `--scope` 选项可以针对特定子包执行格式化: ```bash lerna exec --scope @my-org/ui -- prettier --write "src/**/*.js" lerna exec --scope @my-org/core -- prettier --check "src/**/*.{ts,tsx}" ``` Lerna 7 之后去除了内置的 `lerna run` 对 Prettier 的特殊处理,推荐直接在子包的 `package.json` 里加 `format` 脚本,然后用 `lerna run format` 批量执行。 ## 性能优化 **增量格式化**——只处理 Git 暂存区中的变更文件: ```bash git diff --name-only --diff-filter=ACM HEAD | grep -E '\.(js|ts|tsx)$' | xargs prettier --write ``` **并行处理**——多核同时跑,适合项目文件数过万的场景: ```bash find . -name "*.ts" -not -path "*/node_modules/*" | parallel -j 4 prettier --write ``` **缓存机制**——Prettier 3.0 原生支持缓存: ```bash prettier --write --cache --cache-strategy content "src/**/*.ts" ``` `--cache-strategy content` 基于文件内容哈希判断是否需要重新格式化,比默认的 `metadata` 策略更准确。首次运行生成缓存,后续未修改的文件直接跳过。 **真实场景**:在一个 200+ 子包的 monorepo 中,全量 `prettier --check .` 需要 45 秒。加上缓存后,二次运行降至 3 秒以内。配合 lint-staged 只处理暂存文件,提交时的格式化检查几乎无感。 ## CI/CD 集成 在 GitHub Actions 中配置格式化检查: ```yaml name: Format Check on: [push, pull_request] jobs: format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm run format:check ``` 关键点:CI 中必须用 `--check`(只检查不修改),而不是 `--write`。如果格式不合格,CI 直接报错,开发者本地 `format` 后重新提交。 如果用 Turborepo,可以配合缓存进一步加速: ```yaml - uses: actions/cache@v4 with: path: .turbo key: turbo-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - run: npx turbo format:check ``` ## 常见问题排查 **问题1:子包格式化规则不生效** 检查子包目录下是否有自己的 `.prettierrc`。如果存在,它会完全覆盖根配置。删除子包的 `.prettierrc`,改用根目录的 `overrides` 来定制规则。 **问题2:Prettier 和 ESLint 反复修改同一行** 确认 `eslint-config-prettier` 放在了 `extends` 数组的**最后一位**。如果放在前面,后续配置会重新开启被关闭的规则。 **问题3:CI 中格式检查通过但本地不通过(或反过来)** 通常是 Prettier 版本不一致导致的。在 monorepo 根目录统一安装 Prettier,子包不要单独安装。用 `pnpm ls prettier` 检查是否有多个版本。 **问题4:格式化速度过慢** 按优先级排查:1) 检查 `.prettierignore` 是否正确排除了 `node_modules` 和构建产物;2) 启用 `--cache`;3) 用 lint-staged 只处理变更文件;4) 考虑并行处理。 ## 追问 **为什么推荐共享配置包而不是根目录 .prettierrc?** 根目录配置对纯内部 monorepo 足够。但如果某些子包会独立发布到 npm,它们脱离 monorepo 上下文后就失去了根配置。共享配置包作为 npm 依赖,无论在不在 monorepo 中都能生效。 **Prettier 3.0 有哪些影响 monorepo 的变化?** 最大的变化是原生缓存支持(`--cache`)和 ESM 配置文件支持(`prettier.config.mjs`)。缓存对大型 monorepo 的性能提升显著。ESM 配置则允许在配置文件中动态导入其他模块,比如根据环境变量切换规则。
前端5月28日 07:24
Prettier 与 ESLint 有什么区别?如何协作使用?## Prettier 与 ESLint 有什么区别?如何协作使用? 前端项目中,Prettier 和 ESLint 是最常搭配使用的两个工具,但它们的职责完全不同。理解各自的定位,才能正确配置和协作使用。 ### Prettier 和 ESLint 各自负责什么 **Prettier 是代码格式化工具**,只关心代码长什么样: - 统一缩进、引号、分号、换行等风格 - 解析代码生成 AST 后重新输出,确保格式完全一致 - 配置项很少(约20个),设计理念是"别吵了,就用这个" - 支持 JS/TS/CSS/HTML/JSON/Markdown 等多种语言 **ESLint 是代码质量检查工具**,关心代码有没有问题: - 检测未使用变量、潜在错误、不安全的写法 - 执行团队约定的编码规范(如禁用 var、要求 ===) - 拥有数千条可配置规则和丰富的插件生态 - 仅针对 JavaScript/TypeScript 核心区别一句话: **Prettier 管"好不好看",ESLint 管"对不对"**。 ### 为什么不能只用一个 ESLint 虽然也有格式化规则(如缩进、引号),但能力有限且配置复杂。Prettier 的格式化效果更一致、覆盖语言更多,且几乎不需要团队争论配置。 反过来,Prettier 完全不做代码质量检查,漏掉未使用变量、错误逻辑等问题会埋下隐患。 两者结合是当前前端工程的标准做法。 ### 协作配置(ESLint Flat Config) 从 ESLint v9 开始,官方推荐使用 Flat Config(`eslint.config.js`)替代旧版 `.eslintrc`。新配置方式如下: **安装依赖:** ```bash npm install --save-dev eslint prettier eslint-config-prettier ``` **配置 eslint.config.js:** ```javascript import js from "@eslint/js"; import prettierConfig from "eslint-config-prettier"; export default [ js.configs.recommended, prettierConfig, // 必须放在最后,关闭与 Prettier 冲突的规则 { rules: { "no-unused-vars": "warn", "prefer-const": "error", }, }, ]; ``` **配置 .prettierrc:** ```json { "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" } ``` 关键点: `eslint-config-prettier` 必须放在配置数组最后,它会关闭所有与 Prettier 冲突的 ESLint 格式化规则,让 Prettier 独占格式化职责。 ### 旧版配置方式(.eslintrc) 如果项目仍在使用旧版配置,这样设置: ```javascript // .eslintrc.js module.exports = { extends: [ "eslint:recommended", "prettier", // 放在最后 ], }; ``` ### eslint-plugin-prettier 还需要吗 `eslint-plugin-prettier` 的作用是把 Prettier 的格式化结果作为 ESLint 规则来报告。Prettier 官方现在不再推荐这种方式,原因是: - 它让 ESLint 承担了格式化职责,导致运行变慢 - 格式化问题被混在 ESLint 报错中,难以区分 - 推荐做法是让两者各自独立运行 ### 编辑器集成 在 VS Code 中配置自动格式化,保存时同时生效: ```json // .vscode/settings.json { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } } ``` 保存时先由 Prettier 格式化,再由 ESLint 修复代码质量问题,顺序正确无冲突。 ### Git 提交时自动检查 配合 Husky 和 lint-staged,在提交代码时自动运行检查: ```bash npm install --save-dev husky lint-staged npx husky init ``` ```json // package.json { "lint-staged": { "*.{js,ts}": ["eslint --fix", "prettier --write"], "*.{css,html,md,json}": ["prettier --write"] } } ``` ```bash # .husky/pre-commit npx lint-staged ``` 这样只有暂存的文件会被检查,既保证代码质量又不影响提交效率。 ### 常见冲突与排查 | 问题 | 原因 | 解决方式 | |------|------|----------| | ESLint 报缩进/引号错误 | 格式化规则与 Prettier 冲突 | 确认 `eslint-config-prettier` 在 extends 最后 | | Prettier 格式化后 ESLint 仍报错 | 质量规则报错,非格式问题 | 检查具体规则,质量规则应保留 | | 保存时格式化不生效 | 编辑器未配置或扩展未安装 | 检查 VS Code 扩展和 settings.json | 可以用以下命令快速排查冲突规则: ```bash npx eslint-config-prettier path/to/.eslintrc.js ``` ### 执行顺序总结 实际运行时的正确顺序: 1. Prettier 先格式化代码(处理风格) 2. ESLint 再检查代码质量(处理逻辑) 3. 两者通过 `eslint-config-prettier` 隔离职责,互不干扰 掌握 Prettier 和 ESLint 的职责边界、正确配置方式以及常见冲突排查,是前端工程化基础设施的基本要求。
前端5月27日 22:07
Prettier 支持哪些配置文件格式?## 答案 Prettier 支持以下配置文件格式,按查找优先级从高到低排列: | 优先级 | 文件名 | 格式 | |--------|--------|------| | 1 | `package.json` 中 `prettier` 字段 | JSON | | 2 | `.prettierrc` | JSON/YAML | | 3 | `.prettierrc.json` | JSON | | 4 | `.prettierrc.json5` | JSON5 | | 5 | `.prettierrc.yml` / `.prettierrc.yaml` | YAML | | 6 | `.prettierrc.toml` | TOML | | 7 | `.prettierrc.js` / `.prettierrc.mjs` | JS(ESM) | | 8 | `.prettierrc.cjs` | JS(CJS) | | 9 | `prettier.config.js` / `prettier.config.mjs` | JS(ESM) | | 10 | `prettier.config.cjs` | JS(CJS) | Prettier 3.5+ 还支持 `.prettierrc.ts` / `prettier.config.ts`(TypeScript 格式),适合 TS 项目直接复用类型。 **选哪个?** 小项目用 `.prettierrc.json`,简洁无歧义,编辑器能做 JSON Schema 校验;需要注释或动态逻辑时用 `.prettierrc.js`;TS 项目可考虑 `.prettierrc.ts`。 ```json // .prettierrc.json — 最常用 { "printWidth": 80, "tabWidth": 2, "semi": true, "singleQuote": true, "trailingComma": "es5", "endOfLine": "lf" } ``` ```javascript // .prettierrc.js — 需要注释时 module.exports = { printWidth: 80, // 每行最大字符数 tabWidth: 2, // 缩进空格数 semi: true, // 行尾分号 singleQuote: true, // 单引号 trailingComma: "es5", endOfLine: "lf", }; ``` ## 配置解析机制 Prettier 从**被格式化文件所在目录**开始向上搜索配置文件,直到项目根目录,不支持全局配置,确保不同机器行为一致。如果同时存在 `.editorconfig`,Prettier 会将其作为基础配置,但 `.prettierrc` 中的选项优先。 ## overrides 按文件类型定制 ```json { "semi": true, "overrides": [ { "files": ["*.md", "*.json"], "options": { "tabWidth": 4 } } ] } ``` ## .prettierignore 类似 `.gitignore`,排除不需要格式化的文件: ``` node_modules dist *.min.js ``` ## 追问 **Q: .prettierrc 和 .prettierrc.json 有什么区别?** 没有本质区别。`.prettierrc` 优先级更高,Prettier 会先尝试按 JSON 解析,失败则尝试 YAML。`.prettierrc.json` 只按 JSON 解析,语义更明确,编辑器也能做 schema 校验。 **Q: Prettier 配置和 ESLint 冲突怎么办?** 安装 `eslint-config-prettier` 关闭 ESLint 中与 Prettier 冲突的规则即可。不要两边重复定义同一规则。 **Q: 为什么不推荐在 package.json 中配置?** `package.json` 的 `prettier` 字段容易被忽略,且该文件本身体积大、职责多,配置混在一起不利于维护和代码审查。
前端5月27日 22:04
Prettier 插件有哪些?如何开发自定义插件?## 直接回答 Prettier 支持两类插件:**语言解析器插件**(为新语言提供格式化能力)和**格式化增强插件**(扩展现有语言的格式化行为)。开发自定义插件需实现三个核心部分:`languages`(语言定义)、`parsers`(解析器,文本→AST)、`printers`(打印器,AST→格式化文本)。 ## Prettier 插件分类 **语言解析器插件** — 让 Prettier 支持新语言: - `@prettier/plugin-php`、`@prettier/plugin-ruby`、`@prettier/plugin-pug` **格式化增强插件** — 对已有语言做额外处理: - `prettier-plugin-organize-imports`:自动排序 import - `prettier-plugin-tailwindcss`:Tailwind 类名排序 - `prettier-plugin-sort-json`:JSON 键排序 安装后在 `.prettierrc` 的 `plugins` 数组中声明即可。Prettier 也会自动加载 `node_modules` 下匹配 `prettier-plugin-*` 或 `@scope/prettier-plugin-*` 的包。 ## 如何开发自定义插件 一个完整的插件至少导出 `languages`、`parsers`、`printers`: ```javascript // my-prettier-plugin/index.js module.exports = { languages: [ { name: "MyLang", parsers: ["mylang-parse"], extensions: [".mylang"], }, ], parsers: { "mylang-parse": { parse: (text) => { // 将源码文本解析为 AST // 可借助第三方解析器(如 babel, tree-sitter 等) return customParse(text); }, astFormat: "mylang-ast", locStart: (node) => node.start, locEnd: (node) => node.end, }, }, printers: { "mylang-ast": { print: (path, options, print) => { // 将 AST 节点转换为 Prettier Doc 对象 // Doc 是 Prettier 的中间表示,支持换行、缩进等 const node = path.getValue(); // 用 concat/line/hardline 等构建输出 }, }, }, }; ``` **开发要点:** - `parse` 函数的输入是源码字符串,返回 AST 对象 - `locStart`/`locEnd` 告诉 Prettier 每个 AST 节点的位置,用于错误定位 - `print` 函数返回 Doc 对象,用 `concat`、`hardline`、`indent` 等 API 拼接格式化输出 - 如果只想对已有语言做预处理,只需在 parser 中实现 `preprocess` 函数,无需写完整 parser/printer **调试方式:** 运行 `prettier --plugin ./my-prettier-plugin --debug-print-doc file.mylang` 可查看生成的 Doc 结构。 ## 面试追问方向 1. **如果只想在格式化前对代码做预处理(如删除注释),怎么实现?** — 在对应 parser 中实现 `preprocess` 函数即可,不需要自定义整个 parser/printer 链路。 2. **Prettier 的 Doc 是什么?** — Prettier 内部的中间表示,类似虚拟 DOM。`print` 返回 Doc 而非字符串,Prettier 再根据行宽等配置将 Doc "渲染"为最终输出,这样能自动处理换行和缩进。 3. **插件和 Prettier 内置语言支持的区别?** — 内置语言是 Prettier 核心代码的一部分,性能和兼容性更优;插件是独立模块,更新节奏自由但需自行维护与 Prettier 版本的兼容。
前端5月27日 22:04
使用 Prettier 时常见的问题有哪些?如何解决?## Prettier 配置不生效怎么办? 修改 `.prettierrc` 后格式化无变化,是最常见的坑。排查顺序:先用 `prettier --find-config-path <file>` 确认实际加载的配置文件,可能被上层目录的配置覆盖;再检查 JSON 语法是否合法(多余逗号是最常见的错误);最后重启编辑器,VS Code 会缓存配置。 多配置文件共存时,Prettier 按 `.prettierrc` > `.prettierrc.json` > `.prettierrc.yml` > `prettier.config.js` 的顺序查找,找到一个就停止。用 `--config` 显式指定可以彻底消除歧义。 > **追问**: `.prettierignore` 和 `.gitignore` 的区别是什么? — `.prettierignore` 只控制 Prettier 忽略哪些文件,不影响 Git;`.gitignore` 不被 Prettier 自动读取,除非用 `--ignore-path .gitignore` 显式指定。 ## 如何忽略特定文件的格式化? 三种方式,适用场景不同: - **项目级忽略**: `.prettierignore` 文件,语法与 `.gitignore` 一致,适合忽略 `dist/`、`vendor/` 等目录 - **行内忽略**: `// prettier-ignore` 注释,只忽略紧跟的下一行 ```javascript // prettier-ignore const matrix = [ [1, 2, 3], [4, 5, 6], ]; ``` - **部分规则覆盖**: `overrides` 字段,为特定文件类型设置不同规则 ```json { "overrides": [ { "files": "*.md", "options": { "proseWrap": "always" } } ] } ``` > **追问**: `// prettier-ignore` 能忽略整个文件吗? — 不能,它只作用于紧跟的下一个语法节点。要忽略整个文件,用 `.prettierignore`。 ## Prettier 和 ESLint 冲突如何解决? 这是面试高频题。冲突根源:ESLint 有格式规则(如缩进、引号),Prettier 也有自己的规则,两者同时作用就会打架。 标准解法分两步: 1. 安装 `eslint-config-prettier`,它会关闭所有与 Prettier 冲突的 ESLint 规则 2. (可选) 安装 `eslint-plugin-prettier`,将 Prettier 格式问题作为 ESLint 错误报告 ```javascript // .eslintrc.js module.exports = { extends: [ 'eslint:recommended', 'plugin:prettier/recommended' // 必须放最后,否则会被前面的配置覆盖 ] }; ``` `plugin:prettier/recommended` 已经包含了 `eslint-config-prettier`,不需要重复引入。`extends` 的顺序很关键——后面的配置会覆盖前面的,所以 Prettier 相关配置必须放末尾。 > **追问**: `eslint-plugin-prettier` 和 `prettier-eslint` 有什么区别? — `eslint-plugin-prettier` 让 ESLint 调用 Prettier;`prettier-eslint` 让 Prettier 的输出再过一遍 ESLint fix。前者是主流方案。 ## 大项目格式化太慢怎么办? Prettier 3.x 原生支持 `--cache`,只格式化变更的文件: ```bash prettier --write --cache . ``` 配合 `lint-staged` 只处理暂存文件更高效: ```json { "lint-staged": { "*.{js,ts}": ["prettier --write"] } } ``` 升级版本也有明显收益。Prettier 3 基于 ECMAScript 2024 重写了解析器,格式化速度比 2.x 快 2-3 倍。 > **追问**: `--cache` 的缓存存在哪里? — 默认存在 `node_modules/.cache/prettier`,可以通过 `--cache-location` 自定义路径。 ## VS Code 中 Prettier 保存不格式化? 检查清单: 1. 安装了 Prettier 扩展 2. `settings.json` 中设置 `"editor.formatOnSave": true` 3. `"editor.defaultFormatter"` 设为 `"esbenp.prettier-vscode"` 4. 如果项目有多个 formatter,需要在 `"[javascript]"` 等语言设置中指定 ```json { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } } ``` 多编辑器协作时,加 `.editorconfig` 统一基础缩进和换行符,避免 Tab/Space 之争。 ## 团队版本不一致怎么处理? 锁定版本是最直接的办法: ```json // package.json { "devDependencies": { "prettier": "3.3.3" // 不用 ^ 前缀 } } ``` 团队用 `npm ci` 安装依赖,它会严格按 `package-lock.json` 安装,忽略 `node_modules` 中已有的版本。CI 环境同理,`npm ci` 比 `npm install` 更可靠。 升级 Prettier 大版本时,格式化结果可能变化。正确做法:单独开分支升级,确认无异常再合入主干,不要在功能分支中顺手升级。
前端5月27日 22:03
Prettier 的 .prettierignore 怎么配置?有哪些常用规则和踩坑点?## .prettierignore 文件怎么写? `.prettierignore` 放在项目根目录,语法和 `.gitignore` 完全一致。写好它才能放心跑 `prettier --write .`,否则会格式化不该动的文件。 ``` node_modules dist build coverage *.min.js *.min.css package-lock.json pnpm-lock.yaml ``` ## Prettier 默认会忽略哪些文件? 不需要手动写,Prettier 自动忽略: - 版本控制目录:`.git`、`.svn`、`.hg` - `node_modules`(除非显式加 `--with-node-modules`) 另外,Prettier **自动读取同目录下的 `.gitignore`**,所以 `.gitignore` 里已经排除的文件不需要在 `.prettierignore` 里重复写。 ## 四种忽略模式怎么用? **1. 目录忽略** ``` node_modules **/dist ``` **2. 扩展名忽略** ``` *.min.js *.min.css *.d.ts ``` **3. 路径通配符** ``` src/**/*.generated.ts !src/vendor/*.js ``` `!` 是否定模式,表示"前面忽略的里面,这条除外"。 **4. 行内忽略(prettier-ignore 注释)** 不想格式化某一段代码,在上方加注释: ```js // prettier-ignore const matrix = [ [1, 000, 000], [0, 1, 000], [0, 000, 1] ]; ``` HTML 里用 `<!-- prettier-ignore -->`,Markdown 里用 `<!-- prettier-ignore -->`,CSS 里用 `/* prettier-ignore */`。每种文件类型都有对应的注释语法。 ## CLI 相关的忽略参数有哪些? - `--ignore-path .prettierignore.custom`:指定自定义 ignore 文件路径 - `--with-node-modules`:取消默认对 node_modules 的忽略 - `--list-different`(别名 `-l`):只列出不符合格式的文件名,CI 场景常用 ```bash # 用自定义 ignore 文件格式化 prettier --write --ignore-path .prettierignore.custom "src/**/*.js" # CI 中检查是否有未格式化的文件 prettier --check "src/**/*.js" ``` ## 常见踩坑 **ignore 规则写了不生效?** 检查文件是否放在项目根目录,语法是否和 `.gitignore` 一致。 **想格式化被忽略的文件?** 直接传绝对路径给 prettier,ignore 规则不拦截绝对路径。 **`.prettierignore` 和 `.gitignore` 规则冲突怎么办?** `.prettierignore` 优先级更高。如果 `.gitignore` 排除了某个文件,但 `.prettierignore` 没有排除,Prettier 仍会格式化它。