关于使用Rust宏在编译时聚合CSS样式数据的技术问询
实现编译时CSS聚合与输出的方案
一、编译时全局聚合为单个字符串
你当前用phf实现了编译时哈希表,但phf是静态不可变的,无法直接动态合并多个宏生成的哈希表。可以借助编译时切片收集+全局静态变量的方式实现:
1. 使用linkme crate收集样式片段
linkme的#[distributed_slice]可以让你在多个宏展开点将样式数据注入到同一个全局切片中,编译时完成收集。先添加依赖:
[dependencies] linkme = "0.3" phf = { version = "0.11", features = ["macros"] } once_cell = "1.18" # 用于全局静态变量的懒初始化
定义全局切片和聚合逻辑:
use linkme::distributed_slice; use once_cell::sync::Lazy; use phf::Map; // 定义分布式切片,收集所有样式表的PHF映射 #[distributed_slice] pub static CSS_STYLESHEETS: [&'static Map<&'static str, &'static str>; _]; // 全局聚合后的CSS字符串,懒初始化 pub static GLOBAL_CSS: Lazy<String> = Lazy::new(|| { CSS_STYLESHEETS.iter() .flat_map(|sheet| sheet.iter()) .map(|(selector, props)| { format!("{} {{{}}}", selector, props.replace(',', "; ")) }) .collect::<Vec<_>>() .join("\n") });
2. 修改宏生成代码,注册样式到切片
调整css_stylesheet!宏,让生成的PHF映射自动加入全局切片:
macro_rules! css_stylesheet { ($($selector:ident : { $($prop:ident: $val:literal),* },)*) => { // 生成当前样式表的PHF映射 static LOCAL_STYLES: phf::Map<&'static str, &'static str> = phf_map! { $(stringify!($selector) => concat!($(stringify!($prop), ": ", $val, ","),*)),* }; // 将当前映射注册到分布式切片 #[linkme::distributed_slice(CSS_STYLESHEETS)] static _ENTRY: &'static phf::Map<&'static str, &'static str> = &LOCAL_STYLES; }; }
所有通过css_stylesheet!定义的样式会在编译时被收集,程序启动后访问*GLOBAL_CSS即可得到合并后的完整CSS字符串。
二、编译时将CSS写入文件
如果需要直接在编译阶段生成独立CSS文件,可借助build script配合宏生成的元数据实现:
1. 宏生成编译时元数据
让宏将样式信息以字符串格式输出到编译元数据中:
#[cfg(feature = "build-time-css")] use linkme::distributed_slice; #[cfg(feature = "build-time-css")] #[distributed_slice] pub static CSS_BUILD_SLICES: [&'static str; _]; macro_rules! css_stylesheet { ($($selector:ident : { $($prop:ident: $val:literal),* },)*) => { // 生成样式的字符串表示,供build script读取 #[cfg(feature = "build-time-css")] #[linkme::distributed_slice(CSS_BUILD_SLICES)] static _CSS_ENTRY: &'static str = concat!( $(stringify!($selector), " { ", $(stringify!($prop), ": ", $val, "; "),* " }\n"),* ); }; }
2. Build Script收集并写入文件
在build.rs中读取元数据,合并后写入CSS文件:
use linkme::distributed_slice; #[distributed_slice] extern "Rust" static CSS_BUILD_SLICES: [&'static str; _]; fn main() { let css_content = CSS_BUILD_SLICES.iter().map(|s| *s).collect::<String>(); std::fs::write("target/debug/generated.css", css_content).unwrap(); // 告知rustc文件变化时重新编译 println!("cargo:rerun-if-changed=src/**/*.rs"); }
在Cargo.toml中启用build script和对应feature:
[package] build = "build.rs" [features] build-time-css = []
三、替代方案参考
如果不想自行实现,可以参考现有成熟库的思路:
yew的css!宏:通过proc-macro配合静态变量实现编译时样式处理与聚合,支持内联或独立CSS输出。leptos的style!宏:内置编译时CSS语法检查,通过静态数据注入实现全局样式管理。
内容的提问来源于stack exchange,提问作者max-wickham
相关产品推荐
相关产品推荐

