Rails 7 (7.0.2.3) Importmap视图中未定义jQuery

9

关于在Rails7(7.0.2.3)中安装jQuery的问题,我查找了尽可能多的帮助。我想在视图中的脚本标签中使用它,但是我似乎无法将它导出到全局可用的任何地方。

当然,importmaps很容易操作,可以安装和映射包。不过,在此之后整个文档就变得支离破碎,一种普遍的雾气笼罩着这种新的使用JS包的方式。看看周围,有很多混淆。

话虽如此,我该如何添加这个或类似的内容:

import jquery from "jquery"
window.jQuery = jquery;
window.$ = jquery;

将全局函数放到application.js或其他地方,以使它们能够工作,例如$. 我希望$在所有视图中都可用。

至于我所做的:

./bin/importmap pin jquery --download

请给我提供 importmap 的代码行:

pin "jquery" # @3.6.0

好的。现在看一下importmap JSON:

{
  "imports": {
    "application": "/assets/application-37a24e4747cc3cde854cbbd628efbdf8f909f7b031a9ec5d22c5052b06207eb8.js",
    "@hotwired/turbo-rails": "/assets/turbo.min-96cbf52c71021ba210235aaeec4720012d2c1df7d2dab3770cfa49eea3bb09da.js",
    "@hotwired/stimulus": "/assets/stimulus.min-900648768bd96f3faeba359cf33c1bd01ca424ca4d2d05f36a5d8345112ae93c.js",
    "@hotwired/stimulus-loading": "/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js",
    "jquery": "/assets/jquery-498b35766beec7b412bab57a5acbe41761daa65aa7090857db4e973fa88a5623.js",
    "controllers/application": "/assets/controllers/application-368d98631bccbf2349e0d4f8269afb3fe9625118341966de054759d96ea86c7e.js",
    "controllers/hello_controller": "/assets/controllers/hello_controller-549135e8e7c683a538c3d6d517339ba470fcfb79d62f738a0a089ba41851a554.js",
    "controllers": "/assets/controllers/index-7a8fc081f7e391bd7b6fba95a75e36f88ba813da2c4c8787adad248afb9a0a06.js"
  }
}

听起来似乎已经在这里了。然后在application.html.erb文件中插入一个简单的脚本标签:

<script type="text/javascript" charset="utf-8">
        $(document).ready(function (){
            console.log('jQuery working.');
        })
</script>

失败。检查员说:

(index):41 Uncaught ReferenceError: $ is not defined

这似乎非常基础,没有一些最常用的库(如 jQuery 和 Bootstrap)在 importmaps 中的文档,真是非常令人恼火。

这是第1天的内容,但是这些东西的文档非常稀少。看起来应该有一些协调的努力来解释一下从 webpack 转变过来的变化,而 webpack 本身也是另一个灾难。

如果您知道答案,请发布它,以使每个人受益。

谢谢。


你真的需要使用 script 标签吗? - mechnicov
请访问 https://github.com/rails/importmap-rails#usage 了解如何使用 importmap-rails。 - Alex
+1对于“这真的看起来非常基础,没有一些最常用的库的文档,比如jQuery和Bootstrap,这真的很恼人。” - undefined
1个回答

11

讨论

截至目前(2022年4月),有两件事需要考虑:内联脚本加载浏览器导入映射支持。这两者结合起来,可以使引用通过importmap定义的变量的内联脚本变得难以理解和容易出错。

内联脚本加载

内联脚本会先执行,即在导入 importmap 的JS脚本之前。请参阅MDN script 文档

浏览器导入映射支持

导入映射(importmap)仍然很新,支持程度不同,这使得情况变得更加复杂。

  • 对于没有导入映射支持的Firefox,将使用es-module-shim,它可以在'load' + 'DomContentLoaded'事件之后加载 importmap。
  • 对于支持导入映射的Chrome,importmap会在'load'和'DomContentLoaded'之前加载。
  • 此外,DOM可能会因为各种原因而被加载两次。

结论

变量和函数提升机制解决了脚本加载顺序问题,但在这种情况下,由于脚本是内联定义的,并且importmap脚本尚未被加载,变量是未声明的,并且肯定会导致ReferenceError:...未定义

解决方案

通过检查内联脚本中是否已加载来确保在访问之前定义变量,并且在代码访问它们之前检查从importmap中的脚本是否已经加载。

最可靠的方法是将变量放入application.js并在内联脚本中 安全地 检查其声明。如果存在,则表示已加载importmap,并且在内联脚本上下文中存在于application.js中的所有内容。

可以使用document.DomContentLoaded事件或window.load事件或两者都in关键字结合使用来实现此目的。或者可以在application.js的末尾抛出自定义事件,以强制仅在导入映射代码加载后才运行内联代码。

例如:

  1. importmap.rb中固定jquery
  2. app/javascript/application.js
// jquery does not export 'default' but defines window.$ and
// window.jQuery when loaded:
// - import 'jquery'; will not work
// - namespace does not matter here (jq)
// - no need to redefine it again w/ window.$ = jq.$
import * as jq from 'jquery';

// Define a variable to check in inlined HTML script
window.importmapScriptsLoaded = true;
  1. .html / .erb.html

注:根据注入的 shim/polyfill,加载顺序可能仍然未定义。在这种情况下,需要进行更多的工作(如抛出自定义事件)。此外,DOM 可能会被加载两次,因此在代码不得重复执行时必须考虑这一点。

<h1 id="hello">Hello</h1>

<script type="text/javascript">

// Guard against double DOM loads
var codeExecuted = false;

document.addEventListener('DOMContentLoaded', function(e) {

  // Check if importmap stuff exisits without throwing an error.
  // Then run main code w/ guard against multiple executions.
  if ("importmapScriptsLoaded" in window) { 

    if (!codeExecuted) {
      // Main code here
      console.log($('#hello'));

      // Don't forget to bump guard for one-time only JS execution !!
      codeExecuted = true; 
    };  
  };

});
</script>
  • 或者将相应的脚本直接内嵌,可以使用asset pipeline或直接使用public文件夹。 但这种方法不被推荐,原因如下: 内嵌的脚本会与importmap脚本同时加载。很可能内嵌一个脚本会导致它的所有依赖项也要内嵌(此时importmap脚本还未加载)。

示例:

<script type="text/javascript" src="/jquery.js"></script> 

写得真好。非常感谢@count0的帮助。这更详细地解释了如何加载东西以及如何确保在执行之前加载jQuery等。太棒了! - user1572597
@user1572597 没问题!顺便提一下:我不得不自己举办活动,并编写逻辑来检查 importmapSctiptsLoaded 是否在 DOM 加载、页面加载和我的事件触发时都能够始终正常工作于 Chrome 和 Firefox。 - count0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接