在应用程序中,我需要从 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.erb
Sprockets 在预编译时生成的文件中生成列表。所以我用这种方式重命名controllers/application.js
并controllers/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?