尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于 Rust 的 Farm TailwindCSS 插件:配置文件加载与工具类生成原理(@farmfe/plugin-tailwindcss 2.0.0-beta.1)
前端构建构建工具开发工具【免费下载链接】farmExtremely fast Vite-compatible web build tool written in Rust项目地址https://gitcode.com/gh_mirrors/fa/farm点击查看免费下载导读farmfe/plugin-tailwindcss是 Farm 官方提供的 Rust 原生 TailwindCSS 集成方案它借助 Tailwind 官方 Oxide 扫描器与 Rust 版编译内核在 Farm 构建管线内直接完成候选类扫描与 CSS 生成无需postcss.config.js。本文以该插件 CHANGELOG 记录的 2.0.0-beta.1 版本两条核心变更为主线结合仓库内 Rust 源码、Node 集成测试与官方文档完整讲解tailwindcss.config.js的 CommonJS/ESM 双格式加载、配置注入 Rust 插件的通路以及配置中的颜色主题扩展theme.extend.colors如何影响最终工具类 CSS 的生成。读完本文你将理解该插件从配置文件解析到工具类产出的完整调用链并掌握其当前版本的能力边界。版本变更概览2.0.0-beta.1 改了什么CHANGELOG.md 中 2.0.0-beta.1 版本共记录两条变更类型变更内容Minor Changes支持同时以 CommonJS 与 ESM 两种格式加载tailwindcss.config.js并在工具类utilities生成期间应用配置中的颜色主题扩展Patch Changes将可从tailwind.config.js读取的、可 JSON 序列化的 Tailwind 配置加载进 Rust TailwindCSS 插件两条变更本质上是同一能力的两个侧面Node 侧负责把配置文件无论 CJS 还是 ESM解析成纯数据对象Rust 侧负责把这个 JSON 数据应用到编译内核中。下文分别从「配置文件如何被加载」和「配置数据如何参与工具类生成」两条链路展开。一、配置文件加载tailwindcss.config.js 的 CJS/ESM 双格式支持1.1 行为验证Node 集成测试的三种文件仓库中的 Node 集成测试 tests/index.test.mjs 直接刻画了本次变更的行为约定。测试在临时目录中同时写入三个配置文件// CommonJS 格式被优先采用 writeFile(join(projectDir, tailwindcss.config.js), module.exports { theme: { extend: { colors: { brand: #123456 } } } };\n); // CommonJS 格式的兼容名应被忽略 writeFile(join(projectDir, tailwind.config.js), module.exports { theme: { extend: { colors: { ignored: #654321 } } } };\n); // TS 格式应被忽略 writeFile(join(projectDir, tailwindcss.config.ts), export default { theme: { extend: { colors: { ignored: #654321 } } } };\n);随后调用插件入口process.chdir(projectDir); const { default: tailwindcss } await import(../index.js); const [binPath, options] tailwindcss(); assert.deepEqual(options, { config: { theme: { extend: { colors: { brand: #123456 } // 只有 tailwindcss.config.js 里的配置生效 } } } });从中可以提炼出三条明确的行为约定加载位置是「当前工作目录」插件入口以process.cwd()为基准寻找配置文件测试通过process.chdir(projectDir)切换目录来验证。tailwindcss.config.js优先于tailwind.config.js当两者同时存在时只有tailwindcss.config.js的brand颜色被保留后者的ignored颜色不会进入最终配置。返回值是[binPath, options]二元组binPath指向当前平台对应的原生二进制.farm文件options则是要传给 Rust 插件构造函数的 JSON 选项对象。第二个测试用例 tests/index.test.mjs 进一步验证 ESM 格式writeFile(join(projectDir, tailwindcss.config.js), export default { theme: { extend: { colors: { accent: #abcdef } } } };\n);断言结果options.config.theme.extend.colors.accent #abcdef说明export default语法的 ESM 配置文件同样被支持。这也是 2.0.0-beta.1 相对于此前版本的关键能力提升——此前 Rust 插件只能接受通过插件参数显式传入的 JSON 配置见官网文档的 Known limitations 一节现在则可以从工程根目录的配置文件直接读取。1.2 类型定义Options 的公开形态scripts/index.d.ts 给出了插件选项的类型契约export interface TailwindCSSOptions { /** * Paths or patterns to scan for TailwindCSS candidates. */ content?: string[]; /** * JSON-serializable TailwindCSS configuration. * * When omitted, the plugin loads tailwindcss.config.js from the current * working directory if it exists. */ config?: Recordstring, unknown; } export default function tailwindcss( options?: TailwindCSSOptions ): [string, TailwindCSSOptions];注意类型注释中的关键信息当config省略时插件会从当前工作目录加载tailwindcss.config.js——这正是本次 Minor Change 声明的能力。content字段属于预留选项当前版本并不据此展开文件系统扫描详见后文已知限制。1.3 原生二进制解析与平台分发加载到的配置最终要连同二进制路径一起交给 Farm 的 Rust 插件体系因此插件入口需要先解析出当前平台的.farm原生模块。平台解析逻辑位于 scripts/index.js它按process.platform与process.arch组合分发win32支持x64、ia32、arm64优先读取本包npm/win32-*-msvc/index.farm回退到独立平台包farmfe/plugin-tailwindcss-win32-*-msvcdarwin支持x64、arm64Apple Siliconlinux支持x64、arm64且通过isMusl()检测 glibc 与 musl 运行时分别指向linux-*-gnu或linux-*-musl目录。isMusl()的判定逻辑是先尝试读取process.report.getReport().header.glibcVersionRuntime若读取失败无 glibc 版本即判定为 musl兼容性较老的 Node 版本则回退到解析/usr/bin/ldd输出。对应的 package.json 在napi.targets中声明了 9 个编译目标覆盖三大桌面操作系统的主流架构与 musl 变体发布时通过farm-plugin-tools build --platform产出各平台二进制。二、配置注入通路从 JS 选项到 Rust 编译内核2.1 选项解析与插件初始化加载到的options会被 Farm 序列化为 JSON 字符串作为 Rust 插件构造函数new的入参传入。在 src/lib.rs 中插件首先定义选项结构体#[derive(serde::Deserialize, Default, Debug)] #[serde(rename_all camelCase)] struct TailwindCSSOptions { /// Extra paths / globs to scan for candidates. #[serde(default)] content: VecString, /// Optional Tailwind config payload passed through directly to the Rust core crate. #[serde(default)] config: Optionserde_json::Value, }serde(rename_all camelCase)保证 Node 侧content、config的驼峰命名能与 Rust 字段一一对应serde_json::from_str(options).unwrap_or_default()负责反序列化——即使传入空字符串{}也能得到一个全默认的选项结构单元测试plugin_name_and_priority正是用{}构造插件来验证名称与优先级fn plugin_name_and_priority() { let config Config::default(); let plugin FarmPluginTailwindCSS::new(config, {}.to_string()); assert_eq!(plugin.name(), FarmPluginTailwindCSS); assert_eq!(plugin.priority(), 101); }插件将config选项原样保留在_options字段中直到编译阶段才真正消费。FarmPluginTailwindCSS插件在 config hook 中还有一个副作用若resolve.extensions尚未包含css会自动追加确保 CSS 文件能被正常解析fn config(self, config: mut Config) - farmfe_core::error::ResultOption() { if config.resolve.extensions.iter().all(|e| e ! css) { config.resolve.extensions.push(css.to_string()); } Ok(Some(())) }2.2 配置数据如何进入编译过程CSS 根文件的真正编译发生在freeze_module阶段。插件在此调用farmfe_ecosystem_tailwindcss_node::compile::compile并把_options.config包装成TailwindConfig传入config: self ._options .config .as_ref() .cloned() .map(TailwindConfig::new),TailwindConfig是 rust-ecosystem/tailwindcss 中定义的一个极简数据载体#[derive(Debug, Clone, Default, PartialEq)] pub struct TailwindConfig { pub data: Value, // serde_json::Value } impl TailwindConfig { pub fn new(data: Value) - Self { Self { data } } }该 crate 的模块注释明确声明了它的职责边界「本 crate 专注于编译/构建状态与外部提供的配置数据不加载 JS/TS 配置文件不实现插件兼容 API」。也就是说配置文件的读取与语法解析完全由 Node 侧完成Rust 内核只接收已经序列化为 JSON 的纯数据——这也解释了 CHANGELOG 中 Load JSON-serializable Tailwind configuration 的措辞。在farmfe_ecosystem_tailwindcss::compiler::compile中配置数据被应用到主题上pub fn compile(css: str, options: CompilerOptions) - ResultCompiler, CompileError { let ast parse(css); let features detect_features(ast); let mut theme Theme::with_defaults(); if let Some(config) options.config { apply_external_config_theme(mut theme, config.data); } let design_system DesignSystem::build(ast, theme); Ok(Compiler::new(design_system, ast, features, options)) }Theme::with_defaults()先建立 Tailwind 内置主题--color-red-500、--spacing等默认令牌随后外部配置在默认主题之上叠加。三、颜色主题扩展如何影响工具类生成3.1 apply_external_config_theme把配置颜色注册为主题变量本次变更的核心收益在于配置里声明的自定义颜色会真正进入工具类编译。实现位于 compiler.rs 的apply_external_config_themefn apply_external_config_theme(theme: mut Theme, config: Value) { let Some(theme_config) config.get(theme) else { return; }; if let Some(colors) theme_config.get(colors) { add_config_colors(theme, colors, mut Vec::new()); } if let Some(colors) theme_config .get(extend) .and_then(|extend| extend.get(colors)) { add_config_colors(theme, colors, mut Vec::new()); } }它会同时处理两种来源theme.colors——顶层颜色定义theme.extend.colors——扩展颜色定义即测试中断言的extend.colors.brand。add_config_colors递归展开颜色对象把键路径拼接成 CSS 自定义属性名并注册进主题fn add_config_colors(theme: mut Theme, colors: Value, path: mut VecString) { match colors { Value::String(value) { if path.is_empty() { return; } let key format!(--color-{}, path.join(-)); theme.add(key, value, ThemeOptions::NONE); } Value::Object(map) { for (name, value) in map { let path_len path.len(); if name ! DEFAULT { path.push(name.to_string()); } add_config_colors(theme, value, path); path.truncate(path_len); } } _ {} } }细节值得注意嵌套对象如colors.primary.light会生成--color-primary-light这样带连字符的令牌DEFAULT键被特殊跳过避免污染路径。例如配置{ theme: { extend: { colors: { brand: #123456 } } } }最终注册的就是--color-brand: #123456。3.2 生成的工具类如何消费自定义令牌自定义颜色并不是直接硬编码进 CSS而是通过var()引用主题令牌。Compiler::build的注释与实现说明了这条链路compiler.rsDesignSystem::compile_candidates(candidates)把候选类如bg-brand编译成 CSS AST 节点collect_var_references递归扫描生成的节点收集所有var(--*)引用theme_root_rule_for(referenced)只为被引用到的令牌生成:root { … }声明块并插入到输出最前面。let referenced collect_var_references(candidate_nodes); if let Some(root) self.design_system.theme_root_rule_for(referenced) { utilities.insert(0, root); }这样bg-brand会产出background-color: var(--color-brand)而--color-brand: #123456则由配置驱动的:root规则提供运行时值。只有被实际引用的令牌才会物化避免无谓的 CSS 体积膨胀。3.3 完整生成流程tailwind 标记的内联替换Compiler::build的完整处理顺序是compiler.rs剥离utility/custom-variant/theme/source规则——它们已被设计系统消费不应出现在输出中用substitute_at_apply对用户 AST 中的apply进行替换失败时回退到原 AST保证该阶段不会 panic收集候选工具类节点前置:root令牌声明块inline_tailwind_markers递归替换所有tailwind与import tailwindcss标记为生成的工具类节点optimize_astto_css输出最终 CSS。其中is_tailwind_marker识别四类注入标记fn is_tailwind_marker(name: str, params: str) - bool { match name { tailwind true, import params.contains(tailwindcss), _ false, } }inline_tailwind_markers会递归进入嵌套的layer、media等 at-rule 与普通规则因此用户在media内部书写的标记也能被正确处理。四、两阶段工作流程扫描与延迟编译官方文档 website/docs/plugins/official-plugins/tailwindcss.mdx 概括了插件两阶段的工作原理源码 src/lib.rs 给出了精确实现。4.1 阶段一transform候选类收集 CSS 根文件登记transformhook 对每个经过编译图传递的模块做分流第一类非 CSS 候选源文件。插件用正则CANDIDATE_EXT_RE判定文件是否值得扫描static ref CANDIDATE_EXT_RE: Regex Regex::new( r\.(js|jsx|ts|tsx|vue|svelte|html|mdx|md|astro|php|blade\.php|twig|erb|hbs|liquid|pug|slim|haml)(\?|$) ).unwrap();同时要求路径不包含node_modulesis_candidate_source_file。命中的文件用tailwindcss-oxide的Scanner扫描fn scan_candidates(self, content: str, extension: str) - VecString { let mut scanner Scanner::new(vec![]); scanner.scan_content(vec![ChangedContent::Content( content.to_string(), extension.to_string(), )]) }扫描出的候选类如bg-red-500、hover:bg-blue-500去重后存入candidates: ArcMutexHashSetString同时把文件路径记入candidate_sources。源代码本身不做修改直接返回None放行。第二类CSS 根文件。判定条件有两个扩展名匹配.css(?|$)且路径不含node_modulesis_css_root_file。若内容包含 Tailwind 指令则把原始内容存入pending_css映射表等待第二阶段编译同时 CSS 内部的字符串字面量 /apply值也会用扫描器提取候选类if !has_tailwind_directive(param.content) { return Ok(None); } self.pending_css.lock().unwrap().insert(resolved_path.to_string(), param.content.clone());has_tailwind_directive的检测逻辑覆盖三种写法并刻意规避误判src/lib.rsfn has_tailwind_directive(content: str) - bool { if content.contains(tailwind) { return true; } if content.contains(apply ) { return true; } // 注意尾随空格避开 applyXyz content.contains(import \tailwindcss\) || content.contains(import tailwindcss) }单元测试has_tailwind_directive_detects_all_forms验证了全部正反用例tailwind utilities;、.btn { apply px-4; }、单双引号两种import tailwindcss均命中普通 CSS、import ./theme.css、applyXyz均不命中。4.2 阶段二freeze_module用完整候选集编译 CSS 根为什么编译要推迟到freeze_module源码注释给出了答案必须在所有候选源文件都完成 transform 之后编译否则候选集不完整tailwind utilities会被展开为空。这正是pending_css延迟编译设计存在的意义。freeze_module中的核心逻辑src/lib.rslet original { ... guard.remove(resolved_path) }; // 取出即删除保证每个模块只编译一次 let Some(original) original else { return Ok(None); }; let compiler_result compile::compile( original, CompileOptions { base, // CSS 文件所在目录用于解析相对路径 from: if sourcemap_enabled { Some(resolved_path.clone()) } else { None }, should_rewrite_urls: true, // 重写 url() 引用 config: self._options.config.as_ref().cloned().map(TailwindConfig::new), on_dependency: Box::new(|_dep| { /* 依赖通过模块图追踪 */ }), ..Default::default() }, );编译失败时打印[farmfe/plugin-tailwindcss] Failed to compile TailwindCSS for ...错误并返回Ok(None)放行原模块若编译结果没有任何输出特性!compiler.features.has_any_output_feature()也直接放行——例如纯import无指令的 CSS 就不值得重写。Features位标志定义于 compiler.rs包括AT_APPLY、JS_PLUGIN_COMPAT、THEME_FUNCTION、UTILITIES四类pub const AT_APPLY: Self Self(1 0); pub const JS_PLUGIN_COMPAT: Self Self(1 1); pub const THEME_FUNCTION: Self Self(1 2); pub const UTILITIES: Self Self(1 3);随后执行真正产出let css compiler.build(candidates); // 用完整候选集构建最终 CSS let parsed match parse_css_stylesheet(module_id_str, Arc::new(css.clone())) { ... }; param.module.meta.as_css_mut().set_ast(parsed.ast); param.module.content Arc::new(css);生成后的 CSS 会重新解析为样式表 AST 并回写模块内容保证模块 AST 与新内容一致。4.3 增量构建watch 依赖注册插件还通过context.add_watch_files为 CSS 根模块注册了 watch 边——每个贡献过候选类的源文件都被关联起来for source in sources { let _ context.add_watch_files( param.module.id.clone(), vec![ModuleId::new(source, , context.config.root)], ); }这意味着任意候选源文件如某个.tsx组件发生变更时对应的 CSS 根模块会在后续重建中被标记失效并重新编译从而实现开发模式下样式与源码的同步热更新。从源码结构看这是 Farm 模块图 watch 机制与 Tailwind 候选扫描的深度集成点。五、候选扫描的边界与取舍5.1 扫描范围的精确表达插件把「哪些文件扫描候选、哪些文件触发编译」固化为四条规则Rust 单元测试逐一验证src/lib.rs规则函数命中示例不命中示例CSS 根文件is_css_root_file/src/app.css、/src/styles.css?v1/node_modules/lib/style.css、/src/app.js候选源文件is_candidate_source_file/src/app.tsx、/src/app.vue?lang.ts/node_modules/pkg/index.js、/src/styles.css扩展名提取get_extensionapp.css?v1→css—注意get_extension会先剥离 query 字符串?v1再取扩展名保证带查询参数的模块 id 也能正确归类。候选扫描还有一条防御性策略scan_candidates_ignores_urls单元测试确保 URL 之类的字符串不会产生 URL-like 伪候选。5.2 为什么跳过 node_modulesis_candidate_source_file与is_css_root_file都要求路径不包含node_modulesRust 集成测试tailwindcss_skips_node_modules专门覆盖了这一行为即使node_modules内的 CSS 写了tailwind base;也不会被处理。这一取舍与官方文档 tailwindcss.mdx 的说明一致扫描范围是通过 Farm 编译图传递的模块并排除 node_modules而非传统 Tailwind CLI 式的文件系统 glob 扫描。5.3 测试夹具三种典型 CSS 输入tests/fixtures 目录下的三个夹具分别对应三种输入形态Rust 集成测试 tests/mod.rs 据此验证插件的分流逻辑basic/index.css纯普通 CSS无 Tailwind 指令 →transform返回None不处理at-apply/index.css包含.btn { apply text-red-500; }→ 指令被检测到进入处理路径编译产物含apply或展开后的 padding 等声明且模块类型保持Cssimport-resolve/index.css仅import ./links.css无 Tailwind 指令 → 不处理交由其他 CSS 插件解析。这些测试共同印证插件只对自己负责的「含 Tailwind 指令的 CSS 根文件」动手其余模块一律放行保持管线职责清晰。六、调试与观测手段farmfe_ecosystem_tailwindcss_node提供与上游tailwindcss/node对齐的调试开关。env.rs 中resolve_debug完整复刻了上游DEBUG环境变量的解析约定DEBUG 取值结果未设置falsetrue/1/*truefalse/0false逗号分隔列表含tailwindcss或tailwindcss:*true逗号分隔列表含-tailwindcssfalse显式关闭debug()函数在调用时读取std::env::var(DEBUG)因此同一进程内多次调用会反映最新的环境状态。需要观察编译过程时可以设置DEBUGtailwindcss运行构建命令来启用诊断输出。七、安装与使用Rust 插件 vs JS 插件7.1 安装与注册按官方文档 tailwindcss.mdx 的指引安装需要同时引入插件包与tailwindcssnpm install farmfe/plugin-tailwindcss tailwindcss # 或 yarn add / pnpm add 等价命令在farm.config.ts中注册import { defineConfig } from farmfe/core; import tailwindcss from farmfe/plugin-tailwindcss; export default defineConfig({ plugins: [ tailwindcss({ config: { theme: { extend: {} } } }) ] });在 CSS 入口引入 Tailwindimport tailwindcss;7.2 配置优先级与序列化约束结合 2.0.0-beta.1 的新行为配置来源可归纳为两级插件参数显式传入config优先使用且必须是纯数据对象JSON 可序列化工作目录下的tailwindcss.config.js当config省略时自动加载支持 CJSmodule.exports与 ESMexport default。官方文档 tailwindcss.mdx 的 note 强调Rust 包装层会先把选项序列化再传给底层处理器因此config必须传纯数据不能包含 JavaScript 函数。同理从tailwindcss.config.js读取时也要求文件内容最终可被 JSON 序列化。7.3 与 JS 插件的选择同一仓库还提供 JavaScript 版插件farmfe/js-plugin-tailwindcss文档。官方文档给出的选择建议是需要 Rust 原生集成、追求编译管线内直接处理时用farmfe/plugin-tailwindcss需要 JS 插件管线或filters过滤能力时用farmfe/js-plugin-tailwindcss。后者在 features/css.md 中展示了基于filters.resolvedPaths限定扫描目录的用法可作为需要精细控制扫描范围场景的备选。同时注意两者都面向TailwindCSS v4若需 TailwindCSS v3官方建议改用farmfe/js-plugin-postcss搭配require(tailwindcss)。八、当前版本的已知限制结合源码与 官方文档 的 Known limitations2.0.0-beta.1 存在以下明确边界content选项暂不生效类型上已预留但当前构建不会据此展开文件系统 glob 扫描候选类只来自通过 Farm 编译图的模块并排除 node_modules配置必须 JSON 可序列化Rust 内核只接收serde_json::Value数据JS 配置文件中的函数、类等运行时行为无法传入不支持 JS/TS 配置中的动态逻辑farmfe_ecosystem_tailwindcss的 crate 文档明确声明不加载 JS/TS 配置文件、不实现插件兼容 API配置读取完全依赖 Node 侧入口完成。这些限制决定了当前版本适用的场景Tailwind v4 纯数据配置 以编译图为扫描边界的常规前端工程。结语farmfe/plugin-tailwindcss2.0.0-beta.1 的变更补全了「配置文件 → 插件选项 → Rust 内核」的最后一块拼图Node 侧入口现在能以 CJS/ESM 双格式读取tailwindcss.config.jsRust 侧apply_external_config_theme则把配置中的颜色令牌含theme.extend.colors注册进设计系统并通过var(--color-*)引用机制物化进工具类输出。配合两阶段transform 扫描 freeze_module 延迟编译架构与 watch 依赖注册插件在保持 Rust 原生性能的同时向用户提供了接近官方 Tailwind 工作流的配置体验。想要深入源码的读者可以从 rust-plugins/tailwindcss/src/lib.rs 的transform/freeze_module两个入口出发沿farmfe_ecosystem_tailwindcss_node::compile→farmfe_ecosystem_tailwindcss::compiler::compile一路追踪到 compiler.rs 的Compiler::build即可完整复现本文所述的生成链路。赞分享前端构建构建工具开发工具【免费下载链接】farmExtremely fast Vite-compatible web build tool written in Rust项目地址https://gitcode.com/gh_mirrors/fa/farm点击查看免费下载相关推荐基于 farmfe/plugin-tools 的 Farm Rust 插件构建与发布实战指南基于 farmfe/plugin tools 的 Farm Rust 插件构建与发布实战指南 本指南围绕 Farm 仓库中的 packages/plugin前端构建构建工具开发工具SDRPlusPlus 搭建 GSM-R 解码链路5 步搞定铁路列调语音接收SDRPlusPlus 搭建 GSM R 解码链路5 步搞定铁路列调语音接收 凌晨三点一列货运列车正在接近。调度室的电台里全是杂音列车通话一个字也听不清。前端构建构建工具开发工具Victory Native跨端实战同一套React API在React Native绘制图表完全指南Victory Native跨端实战同一套React API在React Native绘制图表完全指南 Victory Native 是 Victory 图表前端构建构建工具开发工具上一篇搞定Linux文件系统挂载从mount到fstab的实战指南下一篇如何在本地部署DeepTutor打造完全离线的AI学习助手完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Docker私有镜像仓库:稳、快、准、可控的容器交付中枢

Docker私有镜像仓库:稳、快、准、可控的容器交付中枢

1. 这不是“搭个仓库”那么简单:Docker私有镜像仓库到底在解决什么问题?“Docker私有镜像仓库搭建”这八个字,看起来像一句运维手册里的标准操作指令,但如果你真把它当成“照着文档敲几行命令就能完事”的小任务,大概率…

📅 2026/10/13 9:23:05
Qt与SQLite开发实战:轻量级桌面应用数据存储方案

Qt与SQLite开发实战:轻量级桌面应用数据存储方案

1. 为什么选择QtSQLite组合在桌面应用开发中,数据持久化是绕不开的话题。我经历过多个需要本地存储的中小型项目,从早期的文件存储到后来的各种数据库方案,最终沉淀下来的黄金组合就是QtSQLite。这个组合特别适合需要轻量级数据存储的场景&am…

📅 2026/10/13 9:17:59
Varlet 回到顶部 BackTop 组件完全指南:从基础用法到平滑滚动实现原理

Varlet 回到顶部 BackTop 组件完全指南:从基础用法到平滑滚动实现原理

UI组件前端 【免费下载链接】varlet A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop. 项目地址: https://gitcode.com/gh_mirrors/va/varlet 点击查看 免费下载 导读 本文围绕 Varlet(基于 Material Desi…

📅 2026/10/13 9:17:59
MORE NEWS

更多资讯

📰

基于STM32单片机直流无刷电机PID控制温度PWM调速编码器蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S457

STM32-S457-无刷电机测速编码器温度超速高温报警PID控制PWM10档加减速正反转电源OLED屏声光提醒按键(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、OLED屏、(无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选)、电压转…

📰

基于STM32单片机激光测距仪TOF激光传感器VL53L1X倒车雷达蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S450

STM32-S450-电磁波检测舵机保护报警频率变化预警阈值保护阈值超阈值报警OLED屏声光提醒按键(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、OLED屏、(无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选)、激光测距传感器…

📰

敏捷开发全流程会议实战指南:从Sprint规划到回顾会的高效落地

敏捷开发这几年几乎成了软件团队的标配词汇,但真正把“敏捷”落实到日常工作中的,往往都绕不开一件事:开会。从需求进待办列表到版本发布,串起整个迭代的其实是一套固定的会议节奏——Sprint规划、每日站会、Sprint评审、Sprint回…

📰

基于STM32单片机电磁波检测电磁波传感器电磁辐射蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S445

STM32-S445-电磁波检测报警频率变化预警阈值超阈值报警OLED屏声光提醒按键(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、OLED屏、(无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选)、电磁波检测模块、舵机控制电路、…

📰

RHCSA实战:LAMP环境部署phpBB论坛全指南

1. 项目背景与目标解析作为Linux系统管理员认证(RHCSA)的实践考核项目,搭建一个完整可用的论坛系统是检验学员综合能力的经典考题。这个项目看似简单,实则涵盖了Linux系统管理的多个核心技能点:从基础环境配置、服务部署到安全加固&#xff0…

📰

Prometheus+Grafana搭建MySQL监控体系:从指标采集到告警实战

1. 为什么选择PrometheusGrafana来盯MySQL1.1 生产环境里MySQL监控到底缺什么先聊一个很现实的问题:你手上的MySQL实例,真的被“监控”了吗?很多团队最开始的监控方式就是看几眼命令行状态、翻翻慢日志,或者跑到服务器上敲一句sho…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬