ES6模块: 导入后未定义的 onclick 函数

我正在测试 ES6模块,想让用户使用 onclick访问一些导入的函数:

Html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Module Test</title>
</head>
<body>
<input type="button" value="click me" onclick="hello();"/>
<script type="module">import {hello} from "./test.js";</script>
</body>
</html>

Js:

export function hello() {console.log("hello");}

当我单击该按钮时,开发人员控制台显示: ReferenceError: 未定义 hello。如何从模块导入函数,以便它们作为 onclick 函数可用?

我使用的是 Firefox 54.0,dom.moduleScripts.enabled设置为 true

31791 次浏览

模块创建一个范围以避免名称冲突。

要么将函数公开给 window对象

import {hello} from './test.js'


window.hello = hello

或者使用 addEventListener绑定处理程序

<button type="button" id="hello">Click Me</button>
<script type="module">
import {hello} from './test.js'


document.querySelector('#hello').addEventListener('click', hello)
</script>

ES6模块的模块范围:

使用 type="module"以下列方式导入脚本时:

<script type="module">import {hello} from "./test.js";</script>

您正在创建一个称为模块作用域的特定作用域。这里是模块作用域相对于其他作用域级别的位置。从全球范围来看,它们是:

  1. 全局作用域: 最外层模块之外的所有声明(即不在函数中,对于 constlet不在块中)都可以从 Javascript 执行期函式库的任何地方访问
  2. 模块作用域: 模块中的所有声明都作用于模块。当其他 javascipt 尝试访问这些声明时,它将抛出一个引用错误。
  3. 函数作用域: 函数体内部(顶层)声明的所有变量都是函数作用域
  4. 块作用域: letconst变量是块作用域

之所以会出现 reference enceError,是因为在模块中声明了 hello()函数,即 模块范围。正如我们前面看到的,模块范围内的声明仅在该模块内可用,并且您尝试在模块外部使用它。

明确地将声明放在 窗户对象上时,我们可以在模块内部进行全局声明,这样我们就可以在模块外部使用它。例如:

window.hello = hello;  // putting the hello function as a property on the window object

虽然接受的答案是正确的,但是一旦开始从多个模块导入或声明多个函数,它的伸缩性就很差。此外,正如@Quentin 所指出的,它可能造成全球名称空间污染。

我更喜欢稍微修改一下

import { doThis, doThat } from './doStuff.js'
import { foo1, foo2 } from './foo.js'


function yetAnotherFunction() { ... }


window._allTheFns = { doThis, doThat, foo1, foo2, yetAnotherFunction }


// or, if you prefer, can subdivide


window._doStuffjs = { doThis, doThat }
window._foojs = { foo1, foo2 }
  1. 使用“异常”属性名(希望如此)可以避免全局命名空间问题
  2. 不需要立即附加(或 忘记附加)一个 EventListener。
  3. 您甚至可以将侦听器放在 HTML 中,例如 <button onclick="window._foojs.foo1(this)">Click Me</button>

截至2022年,除了提供的解决方案,我不知道还有什么其他解决方案,除了您不需要使用 window,但是您可以使用 globalThis

我不知道这能不能让你好受点。

优点:

  • 这是一个更易读的刻度
  • 当 checkJS 处于 on (在 window 等中找不到属性“ ...”)时,不会出现编译器错误

我甚至使用了 LitHtml,其中有@event 符号,如下所示:

<element @click="${(e)=>console.log(e)}"></element>

但遗憾的是,没有一种干净的方法可以真正得到一个对象的引用,而这个对象是连接着侦听器的。在冒泡队列中,target、 initialTarget 和 expitOrOriginalTarget 可以是任何东西,这非常烦人,也非常令人困惑,但是如果您不需要这些东西,那么 LitHTML 就是最好的选择。