非Rails环境下Sinatra+Sprockets应用使用dart-sass实现SCSS嵌入外部数据的方案咨询
非Rails环境下Sinatra+Sprockets应用使用dart-sass实现SCSS嵌入外部数据的方案咨询
我来给你几个适配你这个非Rails Sinatra+Sprockets场景的解决方案,都是针对你只有两个这类ERB-SCSS文件的情况,轻量处理就能搞定,不用套Rails的asset pipeline:
方案1:预编译ERB生成纯SCSS(最贴近原有代码,改动最小)
这个方案直接复用你现有的.scss.erb代码,先通过Ruby脚本把ERB渲染成纯SCSS,再交给dart-sass处理。适合你不想大改代码的场景。
步骤:
- 写一个简单的Ruby预编译脚本,处理你的ERB-SCSS文件
- 把脚本加到启动/构建流程里(开发环境可以加自动监听)
- 配置Sprockets用dart-sass处理生成的纯SCSS
预编译脚本示例(precompile_erb_scss.rb):
require 'erb' require 'pathname' # 项目根目录 root = Pathname.new(File.expand_path('../..', __FILE__)) # 你的源ERB-SCSS文件路径 source_erb = root.join('assets/stylesheets/your-file.scss.erb') # 输出目录(要放到Sprockets能扫描到的路径下,比如新建compiled子目录) output_dir = root.join('assets/stylesheets/compiled') output_dir.mkdir unless output_dir.exist? output_scss = output_dir.join('your-file.scss') # 渲染ERB,保留Sprockets的asset helper(比如image-url)让后续dart-sass处理 erb_content = File.read(source_erb) renderer = ERB.new(erb_content, trim_mode: '-') # 绑定ERB上下文,这里可以加你需要的全局变量/方法 compiled_content = renderer.result(binding) File.write(output_scss, compiled_content) puts "✅ 预编译完成:#{source_erb} → #{output_scss}"
Sinatra配置调整:
set :root, Pathname.new(File.expand_path('../..', __FILE__)) sprockets = Sprockets::Environment.new(root) # 把编译后的目录加到Sprockets的扫描路径里 sprockets.append_path root.join('assets/stylesheets').to_s sprockets.append_path root.join('assets/stylesheets/compiled').to_s sprockets.append_path root.join('assets/images').to_s # 配置dart-sass-sprockets处理SCSS require 'dartsass-sprockets' sprockets.register_transformer 'text/scss', 'text/css', DartsassSprockets::Transformer.new( style: ENV['RACK_ENV'] == 'production' ? :compressed : :expanded, source_map: ENV['RACK_ENV'] != 'production' ) set :sprockets, sprockets
开发环境热更新:
如果需要开发时修改.scss.erb自动重新编译,可以用guard gem监听文件变化:
- 安装
guard和guard-shell - 新建
Guardfile:
guard :shell do watch(%r{assets/stylesheets/.*\.scss\.erb$}) do |m| system('ruby precompile_erb_scss.rb') end end
启动Sinatra前先跑guard即可。
方案2:给Sprockets加自定义ERB处理器(自动处理,无需预编译)
这个方案让Sprockets直接处理.scss.erb文件:先通过自定义processor跑ERB渲染,再把结果传给dart-sass编译。完全贴近你原来的sprockets-sass工作流,不用手动跑脚本。
步骤:
- 给Sprockets注册
.scss.erb的MIME类型和ERB处理器 - 配置dart-sass处理生成的SCSS
Sinatra配置示例:
set :root, Pathname.new(File.expand_path('../..', __FILE__)) sprockets = Sprockets::Environment.new(root) sprockets.append_path 'assets/stylesheets' sprockets.append_path 'assets/images' # 1. 注册.scss.erb的MIME类型 sprockets.register_mime_type 'text/x-scss-erb', extensions: ['.scss.erb'] # 2. 自定义ERB处理器:把.scss.erb转成纯SCSS sprockets.register_transformer 'text/x-scss-erb', 'text/scss' do |input| erb_content = input[:data] renderer = ERB.new(erb_content, trim_mode: '-') # 绑定Sprockets上下文,让ERB里的asset helper(比如image-url)可用 context = binding def context.image_url(path) # 用Sprockets的原生asset_path方法生成正确路径 input[:environment].asset_path(path, type: :image) end renderer.result(context) end # 3. 配置dart-sass处理SCSS转CSS require 'dartsass-sprockets' sprockets.register_transformer 'text/scss', 'text/css', DartsassSprockets::Transformer.new( style: ENV['RACK_ENV'] == 'production' ? :compressed : :expanded, source_map: ENV['RACK_ENV'] != 'production' ) set :sprockets, sprockets
这样你原来的.scss.erb文件可以直接放在Sprockets的asset路径里,Sprockets会自动先处理ERB,再用dart-sass编译成CSS,和原来的体验完全一致。
方案3:纯SCSS工作流(摆脱ERB,更适配dart-sass)
如果你想完全抛弃ERB,用dart-sass原生的方式处理数据注入,可以把JSON manifest转成SCSS变量/Map,然后用SCSS的原生循环生成规则。这个方案更符合现代SCSS的最佳实践。
步骤:
- 写Ruby脚本把JSON manifest转成SCSS Map文件
- 在你的SCSS里导入这个Map文件,用SCSS原生语法写逻辑
- 配置dart-sass处理SCSS
转JSON为SCSS Map的脚本(generate_manifest_scss.rb):
require 'json' require 'pathname' root = Pathname.new(File.expand_path('../..', __FILE__)) manifest_path = root.join('assets/images/sprites/docs.json') output_scss = root.join('assets/stylesheets/_docs-manifest.scss') manifest = JSON.parse(File.read(manifest_path)) # 把JSON转成SCSS Map scss_map = <<~SCSS $docs-manifest: ( icons-per-row: #{manifest['icons_per_row']}, items: ( #{manifest['items'].map do |item| "(type: '#{item['type']}', col: #{item['col']}, row: #{item['row']}, dark-fix: #{item['dark_icon_fix'] ? 'true' : 'false'})" end.join(', ')} ) ); SCSS File.write(output_scss, scss_map) puts "✅ 生成SCSS Manifest:#{output_scss}"
改写后的纯SCSS文件(docs.scss):
@import 'docs-manifest'; %svg-icon { display: inline-block; vertical-align: top; width: 1rem; height: 1rem; pointer-events: none; fill: currentColor; } %doc-icon { content: ''; display: block; width: 1rem; height: 1rem; background-image: image-url('sprites/docs.png'); background-size: map-get($docs-manifest, icons-per-row)rem map-get($docs-manifest, icons-per-row)rem; } @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { %doc-icon { background-image: image-url('sprites/docs@2x.png'); } } html._theme-dark { %darkIconFix { filter: invert(100%) grayscale(100%); -webkit-filter: invert(100%) grayscale(100%); } } // 用SCSS原生循环生成图标规则 @each $item in map-get($docs-manifest, items) { $type: map-get($item, type); $col: map-get($item, col); $row: map-get($item, row); $dark-fix: map-get($item, dark-fix); ._icon-#{$type}:before { background-position: -#{$col}rem -#{$row}rem; @if $dark-fix { @extend %darkIconFix !optional; } } }
配置Sprockets:
和方案1的配置一样,只要确保_docs-manifest.scss在Sprockets的扫描路径里,dart-sass会自动处理导入。
方案选择建议
- 如果你不想改原有代码,方案1最直接,复制粘贴就能用
- 如果你想保留原来的自动处理流,方案2最贴近sprockets-sass的体验
- 如果你想拥抱纯SCSS工作流,摆脱ERB依赖,方案3最符合dart-sass的设计理念,也更易维护
另外注意:不管用哪个方案,都要确保dartsass-sprockets gem的版本是最新的,非Rails环境下它已经能很好地和Sprockets集成,提供image-url这类asset helper的支持。




