Rails视图中调用JavaScript函数的最佳实践及资产管道疑问
同名函数命名冲突的后果
Rails资产管道会按照application.js中//= require指令定义的顺序拼接所有引入的JS文件,最终打包为单个application.js全局加载。如果两个文件存在同名的顶层函数,后加载的文件中的函数会直接覆盖先加载的函数,运行时不会抛出冲突提示,只会出现不符合预期的逻辑,排查成本很高。
你可以通过命名空间封装的方式规避这类问题,不要把函数直接挂载到全局window上:
// 自定义全局命名空间,所有业务函数都挂在这个对象下 window.RailsDemo = window.RailsDemo || {} RailsDemo.hideModal = function ($modal) { $modal.modal('hide') }
JS事件绑定不生效的原因与最佳实践
你修改后的代码没有生效,不是因为视图没有引入对应JS文件——Rails默认布局已经引入了编译后的application.js,所有打包后的JS代码在所有页面都会全局加载,失效的核心原因有两个:
- Rails 5.2默认开启了Turbolinks(Turbo的前身),页面不会触发全量刷新,JS初始化执行时你要绑定的
.form元素可能还没有渲染到DOM上,导致绑定失败 - 普通的
.submit()绑定只能作用于绑定执行时刻已经存在的DOM元素,后续动态渲染的模态框、异步加载的内容都无法被监听到
正确实现方案
使用事件委托的写法,把事件绑定到页面永远存在的顶层元素(比如document)上,即可兼容Turbolinks和动态DOM的场景:
$(document).on('submit', '.modal .form', function() { const $targetModal = $(this).closest('.modal') $targetModal.modal('hide') })
写完后删掉视图里的onsubmit属性即可,不需要在HTML中硬编码JS调用。
可选的页面专属JS加载方案
如果不想让所有JS逻辑全局执行,可以给body元素拼接控制器和动作名作为专属类,实现页面级的JS逻辑隔离:
# app/views/layouts/application.html.haml 中修改body标签 %body{class: "#{controller.controller_name}-#{controller.action_name}"}
// 只有my_directory控制器的index页面才会执行的逻辑 if ($('body.my_directory-index').length > 0) { // 写入该页面专属的JS逻辑 }
内容的提问来源于stack exchange,提问作者waleeeee
相关产品推荐
相关产品推荐

