Rails 7.1.2 + StimulusJS:Google Maps加载及控制器事件触发异常
Rails 7.1 + Stimulus:Google Maps 加载间歇性异常解决方案
问题核心
Google Maps API 的google-maps-callback事件每次都触发,但 Stimulus 控制器的initMap方法仅偶尔在 Firefox 执行,Brave 中完全不执行——本质是事件触发时序与 Stimulus 控制器挂载时序不匹配:API 回调事件可能在控制器完成事件绑定之前就已经触发,导致控制器错过事件。
解决方案
1. 修改 Stimulus 控制器,兼容已加载/未加载两种状态
在控制器连接时,先检查 Google Maps 是否已加载完成,直接执行初始化;若未加载,则监听事件并确保只执行一次:
import { Controller } from "@hotwired/stimulus" export default class extends Controller { connect() { console.log('localizators controller connected'); // 优先检查 API 是否已就绪 if (window.google?.maps) { this.initMap(); } else { // 绑定单次事件监听,避免重复触发 window.addEventListener("google-maps-callback", this.initMap.bind(this), { once: true }); } } initMap() { console.log('map init'); // 在这里编写地图初始化逻辑,例如: // const mapElement = document.getElementById('map'); // this.map = new google.maps.Map(mapElement, { // center: JSON.parse(this.data.get('current-location-value')), // zoom: 12 // }); } disconnect() { // 清理事件监听,防止内存泄漏 window.removeEventListener("google-maps-callback", this.initMap.bind(this)); } }
2. 调整脚本加载时序,降低冲突概率
将 Google Maps 的脚本标签从head移到body标签的末尾,确保 DOM 元素(包括 Stimulus 控制器绑定的容器)先解析完成,再加载 API:
<!-- 在 app/views/layouts/application.html.erb 的 </body> 前添加 --> <script> window.dispatchMapsEvent = function(...args) { const event = new CustomEvent("google-maps-callback", { detail: args }); window.dispatchEvent(event); } </script> <%= javascript_include_tag "https://maps.googleapis.com/maps/api/js?key=#{Rails.application.credentials.google.api_key}&libraries=places&callback=dispatchMapsEvent", defer: true, async: true, "data-turbolinks-eval": false %>
3. 验证时序问题(可选排查步骤)
在控制台打印事件触发与控制器连接的时间戳,确认时序是否颠倒:
- 布局中的脚本:
<script> window.dispatchMapsEvent = function(...args) { const event = new CustomEvent("google-maps-callback", { detail: args }); console.log("[Google Maps] Callback triggered at:", new Date().getTime()); window.dispatchEvent(event); } window.addEventListener("google-maps-callback", function(event) { console.log("[Google Maps] API loaded at:", new Date().getTime()); }) </script>
- Stimulus 控制器:
connect() { console.log("[Stimulus] Controller connected at:", new Date().getTime()); // ... 其他逻辑 }
如果 API 加载的时间戳早于控制器连接的时间戳,就坐实时序问题。
4. Turbo 环境下的额外处理
如果使用 Turbo 进行页面导航,Google Maps API 仅会加载一次,后续页面切换时需避免重复初始化:
在initMap方法中添加判断:
initMap() { if (this.map) return; // 已初始化则直接返回 console.log('map init'); // ... 初始化逻辑 }
内容的提问来源于stack exchange,提问作者Bruno Costanzo
相关产品推荐
相关产品推荐

