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

use*_*597 13 jquery ruby-on-rails import-maps ruby-on-rails-7

我已经尽可能多地寻找有关在 Rails 7 (7.0.2.3) 中安装 jQuery 的帮助。我想在我的视图中的脚本标签中使用它,但我似乎无法将其导出到全局可用的位置,呃...任何地方。

当然 importmaps 就安装和映射包而言很容易操作。太棒了。之后,整个文档分崩离析,笼罩在这种使用 js 包的新方式上。环顾四周,有很多混乱。

也就是说,我如何添加这个或类似的东西:

import jquery from "jquery"
window.jQuery = jquery;
window.$ = jquery;
Run Code Online (Sandbox Code Playgroud)

到 application.js 或任何让这些全局函数工作的地方,比如 $. 我希望 $ 在我的所有视图中可用。

至于我所做的:

./bin/importmap pin jquery --download
Run Code Online (Sandbox Code Playgroud)

给我 importmap 行:

pin "jquery" # @3.6.0
Run Code Online (Sandbox Code Playgroud)

好的。然后查看 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"
  }
}
Run Code Online (Sandbox Code Playgroud)

丁。看来它就在那里。然后在 application.html.erb 中添加一个简单的脚本标签:

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

失败。检查员说:

(index):41 Uncaught ReferenceError: $ is not defined
Run Code Online (Sandbox Code Playgroud)

这看起来确实非常基本,而且没有关于一些最常用的带有导入映射(如 jQuery 和 Bootstrap)的库的文档,这非常令人恼火。

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

如果您知道这个问题的答案,请发布出来,以便每个人都能受益。

谢谢。

cou*_*nt0 13

讨论

截至目前(2022 年 4 月),有两件事需要考虑:内联脚本加载浏览器 importmap 支持。两者一起可以使引用通过 importmap 定义的变量的内联脚本违反直觉并且容易出错。

内联脚本加载

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

浏览器导入映射支持

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

  • 对于不支持 importmap 的 Firefox,可以在“load”+“DomContentLoaded”事件之后es-module-shim加载 importmap 。
  • 对于支持 importmap 的 Chrome,导入映射会在“load”和“DomContentLoaded”之前加载。
  • 此外,由于各种原因, DOM 可能会加载两次。

结论

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

解决方案

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

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

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

例子:

  1. 固定jquery后importmap.rb
  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;
Run Code Online (Sandbox Code Playgroud)
  1. 在里面.html/.erb.html

注意:根据注入的垫片/填充,加载顺序可能仍然未定义。在这种情况下,需要做更多的工作(例如抛出自定义事件)。此外,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>
Run Code Online (Sandbox Code Playgroud)
  • 或者,通过使用资产管道或直接文件夹直接内联相应的脚本public不建议这样做有两个原因: 内联脚本将与 importmap 脚本一起加载。内联一个脚本很可能会导致内联其所有依赖项。(此处尚未加载 importmap 脚本)。

例子:

<script type="text/javascript" src="/jquery.js"></script> 
Run Code Online (Sandbox Code Playgroud)