Rails 7 中非模块化第三方 JS 库的正确引入方式

在 rails 7 中,当需引入无 es 模块封装、仅依赖全局变量的第三方 minified js 库(如传统 jquery 插件)时,应绕过 importmap,改用 sprockets 的 require 指令机制,将其集中托管于 `vendor/javascripts` 并自动打包进单一资产文件。

Rails 7 默认启用 Importmap 作为现代 JS 依赖管理方案,但它要求所有被 import 的脚本必须符合 ESM 规范(或至少是可执行的 IIFE)。而大量遗留库(如 Chart.js v2、Select2 v3、旧版 Lightbox 等)以立即执行函数或全局变量形式(如 window.Chart)发布,直接通过 importmap-rails 引入会导致 ReferenceError 或 undefined is not a function —— 因为 Importmap 会强制包裹脚本、隔离作用域,破坏其全局暴露逻辑。

此时,Sprockets(Rails 资产管道的经典引擎)仍是可靠选择。它不修改源码、不注入包装器,仅按顺序拼接并提供预编译能力,完美适配非模块化脚本。

✅ 推荐实践:用 Sprockets 托管与聚合第三方 JS

  1. 创建标准目录结构
    将所有第三方 minified JS 文件统一存放至 vendor/javascripts/(非 app/assets/javascripts/,避免与 Stimulus/ESM 混淆):

    mkdir -p vendor/javascripts
    cp ~/Downloads/chart.min.js vendor/javascripts/
    cp ~/Downloads/select2.min.js vendor/javascripts/
  2. 注册路径并配置清单
    在 config/initializers/assets.rb 中添加自定义路径:

    Rails.application.config.assets.paths << Rails.root.join("vendor/javascripts")

    在 app/assets/config/manifest.js 中声明主入口文件:

    //= link do_not_name_me_application.js
  3. 编写聚合入口文件
    创建 app/assets/javascripts/do_not_name_me_application.js(文件名可自定义,但需与 manifest 中一致),使用 Sprockets 指令加载全部第三方脚本:

    //= require_directory ../../../vendor/javascripts
    //= require_tree .
    
    // 可在此追加初始化代码(确保依赖已就绪)
    document.addEventListener("DOMContentLoaded", () => {
      if (typeof Chart !== "undefined") {
        console.log("Chart.js loaded globally ✅");
      }
    });
  4. 在布局中引入
    替换掉 (若你已停用 Importmap 的 application.js),改为:

    <%= javascript_include_tag "do_not_name_me_application", "data-turbo-track": "reload" %>

⚠️ 注意事项

  • 命名避坑:不要将入口文件命名为 application.js,否则可能与 Importmap 的默认入口冲突;建议采用语义化名称(如 legacy_vendor.js)。
  • 加载顺序敏感://= require_directory 按字母序加载,若存在依赖关系(如 jquery.min.js 必须在 jquery-ui.min.js 之前),请改用显式 //= require jquery.min。
  • 生产环境优化:Sprockets 默认启用压缩(config.assets.js_compressor = :terser),且支持合并 → 所有 vendor/javascripts/ 下的 JS 将被打包进

    单个 do_not_name_me_application.js,自动 Gzip + CDN 缓存友好。
  • Turbo 兼容性:该方案完全兼容 Turbo(无需额外配置),因脚本在页面初始加载时执行,全局对象自然可用。

✅ 验证是否生效

启动服务器后访问:
http://localhost:3000/assets/do_not_name_me_application.js
应返回完整拼接后的 JS 内容(含原始 minified 代码 + 初始化逻辑),无语法错误,且浏览器控制台可调用 Chart, $, jQuery 等全局变量。

此方式兼顾简洁性、可维护性与 Rails 原生资产流程,是 Rails 7 中集成传统 JS 库的标准、稳定、可扩展方案。