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

问题描述 投票:0回答:4

我正在测试 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 is not Defined。如何从模块导入函数,以便它们可用作 onclick 函数?

我使用的是 Firefox 54.0,并将

dom.moduleScripts.enabled
设置为
true

javascript ecmascript-6 es6-modules
4个回答
152
投票

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

您可以使用

addEventListener
来绑定处理程序。 演示

<button type="button" id="hello">Click Me</button>
<script type="module">
  import {hello} from './test.js'
  
  document.querySelector('#hello').addEventListener('click', hello)
</script>

或者您可以将函数暴露给

window
对象,但不建议这样做。

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

38
投票

ES6
模块中的模块范围:

当您使用

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

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

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

  1. 全局范围:最外层模块外部的所有声明(即不在函数中,并且对于
    const
    let
    不在块中)都可以从 Javascript 运行时环境中的任何位置访问
  2. 模块作用域:模块内的所有声明的作用域都是该模块。当其他 javascipt 尝试访问这些声明时,它将抛出引用错误。
  3. 函数作用域:函数体内(顶层)声明的所有变量都是函数作用域
  4. 块作用域:
    let
    const
    变量是块作用域的

您收到了引用错误,因为

hello()
函数是在模块中声明的,该模块是模块作用域。正如我们所看到的,模块范围内的早期声明仅在该模块内可用,并且您尝试在模块外使用它。

当我们显式将其放在window对象上时,我们可以在模块内部进行全局声明,以便我们可以在模块外部使用它。例如:

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

7
投票

虽然接受的答案是正确的,但一旦开始从多个模块导入或声明多个函数,它的扩展性就会很差。另外,正如 @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>

1
投票

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

我不知道这是否会让事情变得更好。

优点:

  • 它更具可读性
  • 当 checkJS 打开时,你不会收到编译器错误(在窗口等上找不到属性“...”)

我什至使用LitHtml,其中有@event 表示法,如下所示:

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

但遗憾的是,没有干净的方法来实际获取附加了侦听器的对象的引用。 target、originalTarget 和explicitOrOriginalTarget 可以是冒泡队列中的任何内容,这非常烦人且非常令人困惑,但如果您不需要,LitHTML 将是您的最佳选择。

© www.soinside.com 2019 - 2024. All rights reserved.