3

在应用程序中,我需要从 JS 文件(我正在使用 AudioContext API)中实例化音频文件,或多或少像这样:

playAudio(url) {
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
  let data = await fetch(url).then(response => response.arrayBuffer());
  let buffer = await this.audioContext.decodeAudioData(data)
  const source = this.audioContext.createBufferSource()
  source.buffer = buffer
  source.connect(this.audioContext.destination)
  source.start()
}

此 JS 文件是加载在使用 importmap 和 Sprockets 的新 Rails 7 应用程序中的 Stimulus 控制器。

在开发环境中,JS 可以猜测路径,因为 Sprocket 将使用其规范名称(如/assets/audio/file.wav)为资产提供服务。但是,在生产环境中,在资产预编译期间,Sprockets 在文件名后添加了一个哈希,并且该文件将只能使用/assets/audio/file-f11ef113f11ef113f113.wav.

这个文件名不能被硬编码,因为它取决于预编译(从技术上讲,我可能会用哈希对路径进行硬编码,因为文件不会经常更改,但我不想假设任何关于这个哈希的内容)。

该文件在 Sprockets 在预编译期间生成的清单中被引用到公用文件夹中的其他资产。使用Rails.application.assets_manifest.files我可以访问清单数据并安全地进行映射。

这是我写的帮手:

  def audio_assets_json
    audio_assets = Rails.application.assets_manifest.files.select do |_key, file|
      file['logical_path'].start_with?('audio/')
    end

    JSON.pretty_generate(
      audio_assets.to_h { |_k, f| [f['logical_path'], asset_url(f['logical_path'])] }
    )
  end

但是我需要从 JS 文件中访问这些数据,并且由于清单的文件名中也有一个哈希,所以我的 JS 不能简单地加载它。

我当前的解决方案是将它包含在我的应用程序布局中,并且效果很好:

    <script>
      window.assets = <%= audio_assets_json %>
      window.asset_url = function(path) {
        let result = assets[path]
        return result ? result : `/assets/${path}`
      }
    </script>

这个解决方案的问题是哈希是写在来自应用服务器的每一个 HTML 响应中,效率不高。此外,在运行时调用助手也是低效的:这是在运行时动态生成的,而这应该在部署构建期间静态完成。

我最初的想法是在.js.erbSprockets 在预编译时生成的文件中生成列表。所以我用这种方式重命名controllers/application.jscontrollers/application.js.erb调用了助手:

<% environment.context_class.instance_eval { include ApplicationHelper } %>
window.assets = <%= audio_assets_json %>

JS 是由 Sprockets 正确生成的,但不知何故importmap看不到它,并且 JS 控制台显示以下错误:

Unable to resolve specifier 'controllers/application' from http://localhost:3000/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js

我试图添加这一行config/initializers/assets.rb

Sprockets.register_mime_type 'application/javascript', extensions: ['.js.erb']

我试图添加这一行assets/manifest.js

//= link_tree ../../javascript .js.erb

但这些都没有帮助。

所以我的问题是:如何静态地使用 importmap 和 Sprockets 从 JS 引用资产 URL?

4

0 回答 0