Ruby on Rails 6中如何配置让app/assets下的JS经webpacker正确编译
问题根因说明
你遇到的报错本质是Rails 6.1 两套独立JS编译管线的边界错位问题:
- webpacker 负责管辖
app/javascript目录下的ES6+模块,全程做ES语法转译、依赖打包 - 传统Sprockets资产管线负责处理
app/assets目录下的静态资源,默认只会原样输出文件内容,不做ES模块语法解析、也不会自动关联yarn安装的node依赖
你把带import语法的JS放在了app/assets/javascripts路径下,又通过普通的javascript_include_tag直接引入了未经过任何转译的源文件;浏览器拿到带原生ES import的脚本时,因为没有给脚本加上type="module"属性,自然就会抛出Cannot use import statement outside a module的语法错误。
另外注意你描述的自定义文件存放路径有个笔误,app/asets/javascripts少了字符,实际Rails默认的静态JS目录是app/assets/javascripts,路径拼接错误也会间接导致资产加载异常。
下面提供两种可落地的解决方式,优先推荐更贴合项目默认架构的方案。
方案一:完全迁移到webpacker体系(官方推荐,零额外依赖)
这是Rails 6.1 之后默认的JS开发规范,完全不需要改动原有Sprockets的配置逻辑:
- 把你原本写在
app/assets/javascripts/test.js的代码,移动到webpacker管辖的目录下,比如新建app/javascript/custom/test.js,文件内容修改为:import jQuery from "jquery"; function test() { console.log(jQuery.fn.jquery) } // 关键:模块作用域下定义的函数不会自动暴露到全局,需要手动挂到window才能被onclick调用 window.test = test; - 在webpacker入口文件
app/javascript/packs/application.js中引入jQuery和自定义JS:import jQuery from "jquery"; // 把jQuery挂载到全局,避免后续业务代码调用缺依赖 window.$ = jQuery; window.jQuery = jQuery; // 引入自定义的test.js import "../custom/test.js"; - 视图层替换原有的引入方式,不需要单独引入test.js,直接加载打包好的入口文件即可:
<%= javascript_pack_tag 'application' %> <button onclick="test()">Button</button>
这套流程下所有带ES import的文件都会被webpack自动转译、打包,从根源上避免原生语法不被浏览器识别的问题。
方案二:改造Sprockets配置支持ES语法解析(仅适用于存量项目)
如果因为历史代码兼容问题,必须保留app/assets/javascripts的目录结构,才需要对Sprockets做针对性改造:
- 首先在
Gemfile中添加ES转译依赖,执行bundle install安装:gem 'babel-transpiler' - 修改Sprockets初始化配置
config/initializers/assets.rb,增加两个核心规则:# 允许Sprockets从node_modules目录查找yarn安装的依赖 Rails.application.config.assets.paths << Rails.root.join('node_modules') # 给JS文件接入babel转译能力,支持ES6+语法 Rails.application.config.assets.configure do |env| env.register_mime_type 'text/javascript', extensions: ['.js'] env.register_transformer 'text/javascript', 'application/javascript', Babel::Transpiler end - 注意:Sprockets的依赖加载逻辑和webpack的ES模块解析逻辑不互通,你原来写的
import jQuery from "jquery"无法被正确识别,需要替换成Sprockets支持的require引入方式:// app/assets/javascripts/test.js 内容 //= require jquery/dist/jquery.min function test() { console.log(jQuery.fn.jquery) } // 同样需要将test方法暴露到全局,才能被按钮的onclick事件调用 window.test = test; - 视图层的引入方式不需要修改,Sprockets在输出JS时会自动完成ES语法转译,不再抛出import相关的语法错误。
避坑提醒:很多人解决完import报错后还是会遇到按钮点击无响应的问题,核心原因就是在模块作用域内定义的函数不会自动绑定到全局window对象上,一定要手动把需要在视图层直接调用的方法挂到window上。
内容的提问来源于stack exchange,提问作者fermi
相关产品推荐
相关产品推荐

