Rails下Webpacker结合Turbolinks时JS仅离开页面才运行的异常排查
核心问题根因
- 全局事件绑定的时序错位:你在org_chart.js内部给document绑定的
turbolinks:load回调是全局永久存在的(因为document对象不会被Turbolinks替换)。但org_chart.js是页面专属的分包脚本,只有导航到org_chart页时才会被浏览器加载执行:第一次执行时,当前导航流程的turbolinks:load事件早已触发完成,回调根本不会被执行;等你下次导航离开org_chart页时,新的导航流程触发全局turbolinks:load事件,这个残留的回调才会运行,但此时页面上的#org_chart元素已经被替换掉,就会抛出找不到元素的错误。 - 模块单例缓存的残留问题:Webpack打包的JS模块是单例缓存的,org_chart.js只要加载过一次,后续哪怕再次引入对应script标签,模块代码也不会重新执行。这就导致之前绑定的全局回调一直存在,每次导航都会触发,回到org_chart页时就会重复调用渲染方法,让组织架构图叠加在已有DOM上。
- 属性误用:
javascript_pack_tag上配置的data-turbolinks-track: 'reload',作用是检测资产文件的版本变化(比如src地址变动)时强制触发全页刷新,和“离开页面就停止脚本执行”的预期完全无关,放在这里属于错误配置。
具体调整步骤
- 先移除org_chart.html.erb里的错误属性,把脚本标签调整为:
<div id="org_chart"></div> <%= javascript_pack_tag 'org_chart' %>
- 重构
javascript/packs/org_chart.js的逻辑,把永久绑定的全局事件改成和页面生命周期绑定的初始化、清理逻辑,代码如下:
// javascript/packs/org_chart.js require("orgchart") // 初始化逻辑,内部先判断当前页面是否存在目标容器 const initOrgChart = () => { const $orgChartContainer = $('#org_chart') // 没有目标容器就直接退出,避免在其他页面执行逻辑 if (!$orgChartContainer.length) return // 执行原有渲染逻辑 $orgChartContainer.orgchart({ // 保留你原来的插件配置即可 }) console.log('org chart loaded') // 绑定清理逻辑:当Turbolinks即将渲染新页面(即离开当前页)时执行 const cleanupBeforeNavigation = () => { // 销毁已有渲染实例、清空容器内容,避免下次渲染叠加 // 如果jquery-orgchart提供了destroy方法就调用,没有的话直接清空容器即可 $orgChartContainer.orgchart('destroy') $orgChartContainer.empty() // 移除清理事件的绑定,避免全局监听器残留 document.removeEventListener('turbolinks:before-render', cleanupBeforeNavigation) } document.addEventListener('turbolinks:before-render', cleanupBeforeNavigation) } // 适配不同的执行时机: // 全页刷新首次加载时,DOM已经解析完成,直接初始化 if (document.readyState !== 'loading') { initOrgChart() } else { // DOM仍在解析时,等解析完成后初始化 document.addEventListener('DOMContentLoaded', initOrgChart) } // 适配Turbolinks导航场景:每次Turbolinks把新DOM替换到页面后,触发初始化检查 // 初始化函数内部已经做了容器存在性判断,其他页面触发时会直接退出,不会执行多余逻辑 document.addEventListener('turbolinks:render', initOrgChart)
调整后对应实现的三个目标
- 导航进入org_chart页时,Turbolinks替换完目标DOM后会触发
turbolinks:render事件,初始化函数检测到#org_chart容器存在,就会正常执行渲染,不会再出现进页面空白、无日志的问题 - 导航离开org_chart页时,Turbolinks会在替换DOM前触发
turbolinks:before-render事件,对应的清理逻辑会销毁渲染实例、清空容器、移除残留的事件监听器,脚本不会再在离开页面时触发执行 - 每次进入org_chart页面前,容器都是被清空的状态,没有残留的DOM内容,渲染时不会出现组织架构图叠加的问题
内容的提问来源于stack exchange,提问作者agustaf
相关产品推荐
相关产品推荐

