如何在Rails 6应用中加载自定义Gem内的JS文件
嘿,我完全懂你在从Rails 5切换到Rails 6时遇到的这个麻烦——之前用Sprockets能轻松加载自定义Gem里的JS,换成Webpacker之后就卡壳了,还不想靠yarn重新装一遍资源,就想直接用Gem里的原文件对吧?下面给你几个简洁的解决方案,不用搞一堆复杂的补丁或者额外文件:
方案1:把Gem的资源目录添加到Webpack的解析路径
Webpack需要明确知道去哪找这些Gem里的文件,所以我们可以修改config/webpack/environment.js,把Gem的JS资源目录添加到Webpack的解析路径中:
首先用bundle show [你的Gem名称]命令拿到Gem的安装绝对路径,比如我的Gem叫custom_ui_gem,执行后得到路径/Users/me/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/custom_ui_gem-1.2.3/app/assets/javascripts。
然后修改environment.js:
const { environment } = require('@rails/webpacker') const path = require('path') // 添加Gem的JS资源目录到Webpack的解析列表 environment.resolvedModules.prepend( path.resolve(__dirname, '../../../../.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/custom_ui_gem-1.2.3/app/assets/javascripts') ) // 同时保留默认的node_modules路径 environment.resolvedModules.prepend('node_modules') module.exports = environment
接下来调整app/javascript/packs/application.js的代码——注意普通JS文件不需要加.start(),只有Rails官方的模块(比如ujs、turbolinks)才需要调用初始化方法:
require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") // 直接加载Gem里的JS文件 require('jquery-3.3.1.min') require('popper.min') require('bootstrap') require('mdb') require('wysiwyg') require('addons/pickr.min') require('modules/buttons') require('modules/cards') require('modules/waves')
方案2:用Webpack别名简化路径
如果觉得上面的绝对路径太长不好维护,可以给Gem的资源目录设置别名,修改environment.js:
const { environment } = require('@rails/webpacker') const path = require('path') environment.config.set('resolve.alias', { 'gem-assets': path.resolve(__dirname, '../../../../.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/custom_ui_gem-1.2.3/app/assets/javascripts') }) module.exports = environment
之后在application.js里就能用别名来引用文件了:
require('gem-assets/jquery-3.3.1.min') require('gem-assets/addons/pickr.min') // 其他文件同理
方案3:临时方案——复制Gem资源到项目目录
如果不想改Webpack配置,也可以把Gem里的JS文件直接复制到项目的app/javascript/vendor目录下:
# 先创建vendor目录(如果没有的话) mkdir -p app/javascript/vendor # 复制Gem里的所有JS文件 cp -r $(bundle show custom_ui_gem)/app/assets/javascripts/* app/javascript/vendor/
然后在application.js里引用:
require('vendor/jquery-3.3.1.min') require('vendor/addons/pickr.min')
⚠️ 这个方法的缺点是Gem更新后需要手动重新复制资源,适合临时应急用。
额外注意事项
- 如果遇到jQuery全局未定义的问题,可以在
environment.js里配置Webpack的ProvidePlugin,让全局自动注入jQuery:const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }) ) - 确认Gem里的文件路径完全正确,比如
addons/pickr.min.js(注意是否有.js后缀,Webpack可能需要你补全,或者在配置里添加.js到扩展名解析列表)。
内容的提问来源于stack exchange,提问作者Rockwell Rice

