You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

非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处理。适合你不想大改代码的场景。

步骤:

  1. 写一个简单的Ruby预编译脚本,处理你的ERB-SCSS文件
  2. 把脚本加到启动/构建流程里(开发环境可以加自动监听)
  3. 配置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监听文件变化:

  1. 安装guardguard-shell
  2. 新建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工作流,不用手动跑脚本。

步骤:

  1. 给Sprockets注册.scss.erb的MIME类型和ERB处理器
  2. 配置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的最佳实践。

步骤:

  1. 写Ruby脚本把JSON manifest转成SCSS Map文件
  2. 在你的SCSS里导入这个Map文件,用SCSS原生语法写逻辑
  3. 配置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的支持。

火山引擎 最新活动