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

Rails 7 (7.0.2.3) Importmap jQuery is not defined in view

我尽可能多地寻求有关在 Rails 7 (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。

这是第 1 天的内容,但关于这些内容的文档非常少。似乎应该有一些协调一致的努力来真正解释远离 webpack 的变化,这本身就是另一场惨败。

拜托,如果你知道这个问题的答案,post它让每个人都能受益。

谢谢。

讨论

截至目前(2022 年 4 月)有两件事需要考虑:内联脚本加载浏览器导入映射支持。两者一起可以使内联 引用通过 importmap counter-intuivite 和 error-prone.

定义的变量的脚本

内联脚本加载

首先执行内联脚本。那是在加载 importmap JS 脚本之前。请参阅 MDN 脚本 文档.

浏览器导入地图支持

Importmap 仍然很新,支持各不相同。这使事情变得复杂。

  • 对于不支持 importmap 的 firefox,使用 es-module-shim 可以加载 importmap after 'load' + 'DomContentLoaded' 事件。
  • 对于支持 importmap 的 Chrome,importmap 加载 before 'load' 和 'DomContentLoaded'.
  • 此外 DOM 可能会为 various reasons 加载两次。

结论

变量和函数提升机制负责脚本加载顺序问题,但在这种情况下,由于脚本是内联定义的,并且尚未加载 importmap 脚本,因此变量未声明,最终会导致一种 ReferenceError: ... is not defined.

解决方案

通过检查导入映射中的脚本是否已加载到内联脚本中以及 运行访问它们的代码之前,确保在访问之前定义了变量。

Most reliable is placing a variable into application.js and checking for its declaration safely in the inline script. If it exists importmap has been loaded and everything in application.js exists in the inline script context.

document.DomContentLoaded 事件或window.load 事件或两者 可与in 关键字结合使用。或者,可以在 application.js 的末尾抛出一个 custom event 以强制 运行 仅在加载 importmap 代码后才执行内联代码。

示例:

  1. importmap.rb
  2. 中固定 jquery 之后
  3. 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

注意:根据注入的 shims/polyfills,加载顺序可能仍未定义。在这种情况下,需要做更多的工作(比如抛出自定义事件)。此外,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>