news 2026/9/11 10:47:29

深入理解 ESLint 扩展体系:插件、共享配置、自定义格式化器与解析器完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入理解 ESLint 扩展体系:插件、共享配置、自定义格式化器与解析器完全指南

深入理解 ESLint 扩展体系:插件、共享配置、自定义格式化器与解析器完全指南

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

ESLint 是一个高度可插拔(pluggable)且可配置的 JavaScript 静态检查工具,其核心能力不仅限于内置规则,更在于一套完整的扩展机制:插件(Plugins)、共享配置(Shareable Configs)、自定义格式化器(Custom Formatters)与自定义解析器(Custom Parsers)。本文以仓库 docs/src/extend/ways-to-extend.md 为骨架,逐层拆解这四类扩展方式的定义、接口、配置方法与底层实现,帮助你在真实项目中按需扩展 ESLint 的功能边界。

扩展方式总览:一个高度可插拔的体系

ESLint 的设计哲学是"内核尽量精简、能力向外扩展"。核心eslint包只内置 JavaScript 解析器与一组核心规则,而任何超出内核范围的场景——无论是新的语法、新的文件类型、新的输出格式,还是一整套团队规范——都可以通过扩展机制补齐。官方文档将扩展方式归纳为四条主线:

扩展方式解决什么问题交付形态
插件(Plugins)为项目添加自定义规则、自定义处理器,甚至内置共享配置npm 模块(eslint-plugin-*
共享配置(Shareable Configs)将整套规则与配置项打包复用npm 模块(eslint-config-*
自定义格式化器(Custom Formatters)按自定义格式输出 lint 结果本地文件或 npm 模块(eslint-formatter-*
自定义解析器(Custom Parsers)支持新语言特性或自定义语法,产出 AST本地文件或 npm 模块

这四条主线并非彼此孤立:插件是扩展能力的"容器",可以同时承载规则、处理器、配置与语言(language);共享配置既可以独立发布,也可以作为插件的一部分随插件分发;自定义解析器同样可以被包含在插件中。理解它们的接口与协作关系,是掌握 ESLint 扩展体系的关键。

插件(Plugins):扩展能力的核心载体

插件是什么

插件让你能够向项目添加自己的 ESLint 自定义规则(custom rules)和自定义处理器(custom processors),并以 npm 模块的形式发布。当项目需要的 ESLint 配置超出了核心eslint包的范围时,插件就派上了用场——例如前端框架 React、Vue 所引入的一些特性,就需要核心规则之外的专门规则来约束。一个典型的例子是eslint-plugin-react:它内置了针对 React 项目的规则,包括强制一致地使用组件生命周期方法、在渲染动态列表时要求提供key属性等。

插件通常还会搭配一份 ESLint 配置一起使用,把插件中的一组特性应用到项目上——这份配置本身也可以直接放在插件里(Configs in Plugins)。

插件对象的结构

一个插件本质上就是一个 JavaScript 对象,它向 ESLint 暴露若干特定属性。根据 docs/src/extend/plugins.md 的定义,插件可以包含:

  • meta:插件的元信息;
  • configs:一个包含具名配置的对象;
  • rules:自定义规则定义的集合;
  • processors:具名处理器的集合。

推荐的插件入口文件结构如下(同时兼容 ESM 与 CommonJS):

const plugin = { meta: {}, configs: {}, rules: {}, processors: {}, }; // for ESM export default plugin; // OR for CommonJS module.exports = plugin;

为了便于调试与配置缓存,建议在插件根部的meta对象中提供nameversionnamespace

const plugin = { // preferred location of name and version meta: { name: "eslint-plugin-example", version: "1.2.3", namespace: "example", }, rules: { // add rules here }, }; // for ESM export default plugin; // OR for CommonJS module.exports = plugin;

meta.name应与插件 npm 包名一致,meta.version应与包版本一致,meta.namespace则是用户访问该插件规则、处理器、语言与配置时使用的前缀(通常是包名去掉eslint-plugin-后的部分)。最稳妥的做法是从package.json中读取这两个值,避免手工维护时产生不一致。nameversion也可以直接放在插件对象的根部(向后兼容的旧格式),但meta是推荐位置。

在配置文件中使用插件

在 flat config 格式下,使用插件需要先在plugins键中注册命名空间,再通过命名空间/规则名的形式启用规则(详见 docs/src/use/configure/plugins.md):

// eslint.config.js import { defineConfig } from "eslint/config"; import example from "eslint-plugin-example"; export default defineConfig([ { files: ["**/*.js"], // any patterns you want to apply the config to plugins: { example, }, rules: { "example/dollar-sign": "error", }, }, ]);

需要注意命名空间规范:不以@开头的命名空间不能包含/;以@开头的可以包含/(例如eslint/plugin不合法,而@eslint/plugin合法)。插件也可以从本地文件直接加载(本地插件),甚至可以在配置中内联定义一个"虚拟插件"(Virtual Plugin),把单个规则文件直接包成插件使用。

插件内的配置:Configs in Plugins

插件可以在configs键下打包一份或多份具名配置,例如把一组自定义规则和推荐的选项绑定在一起。插件内的配置同样遵循 flat config 的写法:在plugins对象中注册自身,并启用以命名空间为前缀的规则:

const plugin = { meta: { name: "eslint-plugin-example", version: "1.2.3", }, configs: {}, rules: { "dollar-sign": { create(context) { // rule implementation ... }, }, }, }; // assign configs here so we can reference `plugin` Object.assign(plugin.configs, { recommended: [ { plugins: { example: plugin, }, rules: { "example/dollar-sign": "error", }, languageOptions: { globals: { myGlobal: "readonly", }, parserOptions: { ecmaFeatures: { jsx: true, }, }, }, }, ], }); // for ESM export default plugin;

使用方通过extends键引用这份具名配置:

// eslint.config.js import { defineConfig } from "eslint/config"; import example from "eslint-plugin-example"; export default defineConfig([ { files: ["**/*.js"], plugins: { example, }, extends: ["example/recommended"], }, ]);

需要强调:插件不能强制用户采用某份配置,用户必须在自己的配置文件中显式引入。另外,为了兼容 ESLint v9.0.0 之前的 eslintrc 配置体系,插件可以用"legacy-"前缀导出旧格式配置;对于已存在"recommended"命名的老插件,也可以增加"flat/recommended"条目。defineConfig()会先查看recommended键,若格式不符再查找flat/recommended键,从而提供平滑升级路径。

插件的测试、检查与发布

  • 测试:ESLint 提供了RuleTester工具(见 docs/src/integrate/nodejs-api.md 与实现 lib/rule-tester/rule-tester.js),可以方便地为插件中的每条规则编写单元测试。
  • 检查:插件自身也应该被 lint。官方建议使用eslinteslint-plugin-eslint-plugineslint-plugin-nrecommended配置来检查插件代码。
  • 发布:发布到 npm 时,务必在package.json中将eslint声明为peerDependencies(例如"eslint": ">=10.0.0"),并添加eslinteslintplugineslint-plugin关键词便于检索。

插件中的自定义规则与处理器

插件承载的两种核心扩展物是规则与处理器:

  • 自定义规则(Custom Rules):详见 docs/src/extend/custom-rules.md。一条规则导出metacreate(context)两个关键部分:meta声明规则的类型(problem/suggestion/layout)、是否可修复(fixable)、是否提供建议(hasSuggestions)、消息模板(messages)、选项 schema(schema)与默认选项(defaultOptions)等;create()返回一组 visitor 方法,ESLint 在遍历 AST 时按节点类型、选择器(见 docs/src/extend/selectors.md)或代码路径事件(见 docs/src/extend/code-path-analysis.md)调用它们。报告问题统一使用context.report(),并推荐以messageId引用meta.messages中集中管理的消息模板。
  • 自定义处理器(Custom Processors):详见 docs/src/extend/custom-processors.md。处理器通过preprocess(text, filename)把非 JS 文件拆分成若干待 lint 的代码块(每个代码块含textfilename两个属性),再通过postprocess(messages, filename)把多维消息数组合并回一维并校正行号、列号到原始文件坐标。例如@eslint/markdown就提供了从 Markdown 文件中提取并 lint 内嵌 JavaScript 的处理器。

共享配置(Shareable Configs):打包整套规范

定义与适用场景

ESLint 共享配置是预定义好的 ESLint 配置,将规则与其他配置项打包进一个 npm 包中供项目直接复用。凡是能写进配置文件的内容,都能写进共享配置——规则、languageOptions(包括 parser options)、pluginsignores等一应俱全。共享配置可以独立发布,也可以作为插件的一部分随插件分发。

一个经典的例子是eslint-config-airbnb:它包含大量规则,并额外指定了一些 parser options,设计目的是强制在项目中应用 Airbnb JavaScript 风格指南。通过引入这份共享配置,项目无需逐条手工配置规则即可自动落实整套风格规范。

创建与发布

共享配置本质上就是导出一个配置对象或配置数组的 npm 包(详见 docs/src/extend/shareable-configs.md)。包名建议遵循eslint-config-*前缀(scoped 包则使用@scope/eslint-config@scope/eslint-config-myconfig格式),以便识别。从包的main入口(默认index.js)导出配置即可:

// index.js export default [ { languageOptions: { globals: { MyGlobal: true, }, }, rules: { semi: [2, "always"], }, }, ];

因为index.js只是普通 JavaScript,你可以从文件中读取设置,甚至动态生成配置。发布时建议在package.json中添加eslinteslintconfig关键词,并用peerDependencies声明对 ESLint 的依赖(推荐使用">="区间语法,如"eslint": ">=10.0.0")。如果共享配置依赖某个插件或自定义解析器,应把这些包放入dependencies

使用与覆盖

eslint.config.js中导入共享配置并通过extends加入导出数组即可:

// eslint.config.js import { defineConfig } from "eslint/config"; import myconfig from "eslint-config-myconfig"; export default defineConfig([ { files: ["**/*.js"], extends: [myconfig], }, ]);

注意:共享配置不能与 CLI 的--config标志一起使用。若要在共享配置基础上覆盖某些设置,只需在extends之后直接书写同名配置项,后续的值会覆盖共享配置:

// eslint.config.js import { defineConfig } from "eslint/config"; import myconfig from "eslint-config-myconfig"; export default defineConfig([ { files: ["**/*.js"], extends: [myconfig], // anything from here will override myconfig rules: { "no-unused-vars": "warn", }, }, ]);

同一个包还可以导出多份配置:除main指定的默认配置外,新增文件(如my-special-config.js)后,通过eslint-config-myconfig/my-special-config.js路径直接导入并使用。官方强烈建议始终提供默认导出,以避免使用方困惑。

自定义格式化器(Custom Formatters):掌控输出形态

格式化器是什么

自定义格式化器接收 ESLint 的 lint 结果,并以你定义的格式输出——可以是特定文件格式、特定的展示风格,也可以是针对某类工具优化的格式。只有当内置格式化器(见 docs/src/use/formatters/index.md)无法满足需求时才需要自行实现。例如eslint-formatter-gitlab自定义格式化器可以把 ESLint 结果转换为 GitLab 代码质量报告。

从源码可以看到,ESLint 内核自带四种格式化器(lib/cli-engine/formatters/ 目录):stylish(人类可读输出,也是默认格式化器)、jsonjson-with-metadatahtml,其名称与描述记录在 lib/cli-engine/formatters/formatters-meta.json 中。

函数签名与基本用法

每个格式化器都是一个函数:接收results对象与context两个参数,返回一个字符串(详见 docs/src/extend/custom-formatters.md)。内置 JSON 格式化器(实现见 lib/cli-engine/formatters/json.js)本质上只有一行:

//my-awesome-formatter.js module.exports = function (results, context) { return JSON.stringify(results, null, 2); };

格式化器也可以是 async 函数(自 ESLint v8.4.0 起支持):

//my-awesome-formatter.js module.exports = async function (results) { const formatted = await asyncTask(); return formatted; };

使用-f(或--format)命令行标志运行自定义格式化器。本地自定义格式化器的路径必须以.开头:

eslint -f ./my-awesome-formatter.js src/

results参数的结构

results是一个LintResult对象数组(类型定义见 docs/src/integrate/nodejs-api.md),每个元素对应一个文件的 lint 结果:

[ { filePath: "/path/to/a/file.js", messages: [ { ruleId: "curly", severity: 2, message: "Expected { after 'if' condition.", line: 2, column: 1, }, { ruleId: "no-process-exit", severity: 2, message: "Don't use process.exit(); throw an error instead.", line: 3, column: 1, }, ], errorCount: 2, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0, source: "var err = doStuff();\nif (err) console.log('failed tests: ' + err);\nprocess.exit(1);\n", }, { filePath: "/path/to/Gruntfile.js", messages: [], errorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0, }, ];

每条message包含ruleIdseverity(0/1/2)、messagelinecolumn等定位信息;文件级汇总则通过errorCountwarningCountfixableErrorCountfixableWarningCount体现。

context参数的结构

context是格式化器函数的第二个参数,包含以下属性:

  • color(可选):设置--color时为true--no-color时为false,两者均未设置时该属性被省略;
  • cwd:当前工作目录,来自ESLint类的cwd构造选项;
  • maxWarningsExceeded(可选):设置了--max-warnings且警告数超出上限时存在,含maxWarningsfoundWarnings两个属性;
  • rulesMeta:各规则的meta属性值,可用于在输出中附带规则文档链接等信息。

例如运行了no-extra-semi规则时,context形如:

{ cwd: "/path/to/cwd", maxWarningsExceeded: { maxWarnings: 5, foundWarnings: 6 }, rulesMeta: { "no-extra-semi": { type: "suggestion", docs: { description: "disallow unnecessary semicolons", recommended: true, url: "https://eslint.org/docs/rules/no-extra-semi" }, fixable: "code", schema: [], messages: { unexpected: "Unnecessary semicolon." } } }, }

若希望输出点击即可在终端打开文件,现代终端普遍支持file:line:column格式,按此格式输出即可。

向格式化器传递额外参数

格式化器函数只接收resultscontext,没有额外的形参。但有两种途径向自定义格式化器注入附加数据:

通过环境变量——格式化器可以读取环境变量来改变行为。例如用FORMATTER_SKIP_WARNINGS决定是否输出警告:

module.exports = function (results) { var skipWarnings = process.env.FORMATTER_SKIP_WARNINGS === "true"; // ... 组装 errors / warnings,按 skipWarnings 决定是否包含警告 };

运行时:

FORMATTER_SKIP_WARNINGS=true eslint -f ./my-awesome-formatter.js src/

通过管道组合——若自定义格式化器的模式仍不够灵活,最稳妥的方案是使用内置 JSON 格式化器,把 JSON 输出通过管道交给第二个程序处理:

eslint -f json src/ | your-program-that-reads-JSON --option

打包与发布

自定义格式化器可以发布为 npm 包,包名遵循eslint-formatter-*格式(如eslint-formatter-awesome)。使用已发布的格式化器时无需输入eslint-formatter-前缀(ESLint 知道在格式化器名不以.开头时去查找eslint-formatter-前缀的包):

eslint -f awesome src/

package.jsonmain入口必须是实现格式化器的 JavaScript 文件,并建议添加eslinteslint-formattereslintformatter关键词方便检索。

完整示例

一个只输出错误与警告总数的汇总格式化器:

module.exports = function (results, context) { // accumulate the errors and warnings var summary = results.reduce( function (seq, current) { seq.errors += current.errorCount; seq.warnings += current.warningCount; return seq; }, { errors: 0, warnings: 0 }, ); if (summary.errors > 0 || summary.warnings > 0) { return ( "Errors: " + summary.errors + ", Warnings: " + summary.warnings + "\n" ); } return ""; };

运行eslint -f ./my-awesome-formatter.js src/会输出类似Errors: 2, Warnings: 4。更复杂的详细格式化器则可以逐条输出错误/警告、规则 ID、规则文档链接与file:line:column定位(示例见 docs/src/extend/custom-formatters.md 的 Detailed Formatter 一节)。

自定义解析器(Custom Parsers):扩展语法边界

解析器在 ESLint 中的角色

自定义解析器用于扩展 ESLint,使其能够 lint 新的非标准 JavaScript 语言特性或代码中的自定义语法。解析器负责把代码转换为抽象语法树(AST),ESLint 再基于 AST 进行分析与检查。ESLint 自带内置的 JavaScript 解析器 Espree,自定义解析器则让你可以 lint 其他语言,或扩展内置解析器的能力。例如@typescript-eslint/parser就是通过自定义解析器让 ESLint 支持 lint TypeScript 代码。自定义解析器同样可以被包含在插件中。

两种方法:parseparseForESLint

一个自定义解析器是带有parse()parseForESLint()方法的 JavaScript 对象(详见 docs/src/extend/custom-parsers.md)。parse只返回 AST;parseForESLint()除 AST 外还能返回附加信息,让解析器更深地定制 ESLint 行为。两个方法都应为实例自身属性,第一个参数是源码,第二个参数是可选配置对象(即配置文件中的parserOptions):

// customParser.js const espree = require("espree"); // Logs the duration it takes to parse each file. function parse(code, options) { const label = `Parsing file "${options.filePath}"`; console.time(label); const ast = espree.parse(code, options); console.timeEnd(label); return ast; // Only the AST is returned. } module.exports = { parse };

parseForESLint()返回的对象包含必选属性ast与可选属性servicesscopeManagervisitorKeys

  • ast:解析出的 AST 对象;
  • services:解析器提供的服务(如节点的类型检查器),规则通过context.sourceCode.parserServices访问,默认为空对象;
  • scopeManager:可替换的ScopeManager对象,用于对实验/增强语法做定制化作用域分析,默认由eslint-scope创建(接口见 docs/src/extend/scope-manager-interface.md);
  • visitorKeys:定制 AST 遍历方式的对象,键为 AST 节点类型,值为需要遍历的属性名数组,默认来自eslint-visitor-keys

解析器同样建议在根部meta中提供nameversion(与 npm 包名、包版本保持一致),便于调试与缓存。

AST 规范要点

自定义解析器产出的 AST 基于 ESTree 规范,但需要额外的位置信息(详见 docs/src/extend/custom-parsers.md):

  • 所有节点必须带range属性(两个 0 基索引组成的数组,code.slice(node.range[0], node.range[1])必须是该节点的文本);loc不能为null(ESTree 中它可空,但 ESLint 强制要求)。所有节点的parent属性必须可写——规则访问 AST 前,ESLint 会在遍历中为每个节点设置parent
  • Program节点必须带tokenscomments属性,均为Token接口数组(含typelocrangevalue),并分别按range[0]排序;所有 token 与 comment 的 range 不得互相重叠。
  • Literal节点必须带raw属性,即该字面量的源码文本。

打包、安装与配置

发布自定义解析器时,在package.json中把main字段指向导出解析器的文件即可。安装后在 flat config 中用languageOptions.parser指定:

// eslint.config.js const { defineConfig } = require("eslint/config"); const myparser = require("eslint-parser-myparser"); module.exports = defineConfig([ { languageOptions: { parser: myparser, }, // ... rest of configuration }, ]);

在旧版 eslintrc 配置中则把parser写成字符串:

// .eslintrc.js module.exports = { parser: "eslint-parser-myparser", // ... rest of configuration };

一个提供parserServices.foo()服务的简单自定义解析器示例:

// awesome-custom-parser.js var espree = require("espree"); function parseForESLint(code, options) { return { ast: espree.parse(code, options), services: { foo: function () { console.log("foo"); }, }, scopeManager: null, visitorKeys: null, }; } module.exports = { parseForESLint };

扩展要素的协同:一条完整的数据流

把四类扩展组合起来,可以看到 ESLint 的完整工作链路:

  1. 输入侧:自定义解析器把代码解析为 AST(内置 JavaScript 语言实现见 lib/languages/js/index.js);对于非 JS 文件,插件提供的处理器负责从 HTML、Markdown 等文件中提取出代码块再交给 ESLint(处理器配置见 docs/src/use/configure/plugins.md 的 "Specify a Processor" 一节)。
  2. 检查侧:插件中的自定义规则随插件注册,与核心规则一起在 AST 遍历与代码路径分析中执行;插件内附的配置(Configs in Plugins)或独立发布的共享配置决定启用哪些规则及严重级别。
  3. 输出侧:自定义格式化器接收LintResult数组,按目标格式输出给终端、CI 或第三方工具。

此外,插件还可以提供"语言"(language)扩展——通过配置中的language键指定namespace/language-name,让 ESLint 直接 lint JavaScript 之外的语言(如@eslint/json提供的json/jsonc语言)。

扩展性能的可观测性:内置规则耗时统计

针对自定义规则的性能分析,ESLint 提供了内置的耗时追踪机制:设置TIMING环境变量后,lint 结束时将显示耗时最长的十条规则及其相对占比(时间 = 规则创建 + 规则执行)。其实现位于 lib/linter/timing.js:模块在加载时读取process.env.TIMING决定是否启用统计,并在process.on("exit")时输出排序后的规则耗时表格。

$ TIMING=1 eslint lib Rule | Time (ms) | Relative :-----------------------|----------:|--------: no-multi-spaces | 52.472 | 6.1% camelcase | 48.684 | 5.7% no-irregular-whitespace | 43.847 | 5.1% valid-jsdoc | 40.346 | 4.7% handle-callback-err | 39.153 | 4.6% space-infix-ops | 35.444 | 4.1% no-undefined | 25.693 | 3.0% no-shadow | 22.759 | 2.7% no-empty-class | 21.976 | 2.6% semi | 19.359 | 2.3%

从 lib/linter/timing.js 的实现可以看出:列表长度默认下限为 10;若TIMING为字符串"all"则输出全部规则;若被解析为大于 10 的整数则输出对应条数,否则回退到默认 10 条。

要单独测试某条规则,可组合--no-config-lookup--rule选项:

$ TIMING=1 eslint --no-config-lookup --rule "quotes: [2, 'double']" lib Rule | Time (ms) | Relative :------|----------:|--------: quotes | 18.066 | 100.0%

如需更细粒度的耗时信息(按文件、按规则),可以使用stats选项,详见 docs/src/extend/stats.md。

总结与进阶路线

ESLint 的扩展体系可以概括为:插件是承载自定义规则与处理器的容器,共享配置让整套规范可打包复用,自定义解析器拓宽了可分析的语法范围,自定义格式化器则把结果导出为任意需要的形态。四者分别对应 lint 流水线的注册、配置、解析与输出环节,既可独立使用,也可组合嵌套(配置入插件、解析器入插件)。

深入每个扩展点的实现细节,推荐按以下顺序阅读本仓库的对应文档:

  • 自定义规则:docs/src/extend/custom-rules.md(含context对象、context.report()、fix 与 suggestions、选项 schema、defaultOptions、作用域访问等完整参考)
  • 自定义处理器:docs/src/extend/custom-processors.md(含preprocess/postprocess接口与 autofix 支持)
  • 插件创建:docs/src/extend/plugins.md(含插件结构、meta、规则/处理器/配置打包与 legacy 兼容)
  • 共享配置:docs/src/extend/shareable-configs.md
  • 自定义格式化器:docs/src/extend/custom-formatters.md
  • 自定义解析器:docs/src/extend/custom-parsers.md

同时可结合仓库源码印证:内置格式化器位于 lib/cli-engine/formatters/,内置规则位于 lib/rules/(如 lib/rules/quotes.js、lib/rules/yoda.js、lib/rules/no-shadow.js 分别是选项 schema、多选项规则与作用域分析的典型实现),规则的测试工具位于 lib/rule-tester/rule-tester.js。在动手编写自己的第一个自定义规则或格式化器之前,先通读这些实现,会让你的扩展代码与 ESLint 内核的协作更加顺畅。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 10:46:18

把OpenAI Codex搬进手机群聊:用Grix实现随时随地AI编程协作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:43:21

智慧农业执行器控制实战:从开关量到继电器与PLC的可靠方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:43:18

基于高阶统计量与改进小波块阈值的地震信号去噪方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:42:04

单片机毕设项目:基于 STM32 或 51 单片机的按键阈值设置超声波测距仪设计 基于 STM32 或 51 单片机的环境温度补偿距离检测系统设计(022907)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华