【问题标题】:Accessing exported functions from html file从 html 文件访问导出的函数
【发布时间】:2018-05-04 13:33:46
【问题描述】:

我正在使用Chrome v65 玩一些ES6 功能

我有以下文件:

base.js:

export function consoleLogger(message) {
    console.log(message);
}

ma​​in.js

import { consoleLogger } from './base.js';
consoleLogger('Some message');

utils.js

function sayMyName(name) {
    console.log(name);
}

imports.html

<!DOCTYPE html>
<html>
<head>
    <script src='./main.js' type='module'></script>
    <script src='./utils.js'></script>
</head>
<body>
    <script>
        sayMyName('Cool name');
    </script>
</body>
</html>

像这样使用一切似乎都很好,在控制台中我得到了

酷名 utils.js:2

一些消息base.js:2

但是,让我们想象一下我需要一些额外数据才能为consoleLogger 方法编写消息的场景。然后我想在 ma​​in.js

中有这样的东西
function logToConsole(msg) {
    consoleLogger(msg);
}

在 imports.html

中
<script>
    sayMyName('Cool name');
    logToConsole('Send from log to console');
</script>

然后对于我在控制台中的html 文件中的logToConsole('Send from log to console'); 调用,我得到:

未捕获的 ReferenceError:未定义 logToConsole 在imports.html:10

所以从base.js导入consoleLogger并直接在main.js中调用没有问题,包含另一个.js文件(utils.js)并从那里调用方法没有问题,但是如果我尝试调用在main.js 中声明的方法,该方法在内部调用导入的方法,我从上面得到了错误。看来main.js 中的方法是否引用导入的方法似乎并不重要。我只是评论了所有内容,只留下了一个简单的方法

ma​​in.js

import { consoleLogger } from './base.js';

/*function logToConsole(msg) {
    consoleLogger(msg);
}

consoleLogger('Some message');*/

function randomStuff() {
    console.log('random stuff');
}

在控制台中我收到了这个错误:

imports.html:11 Uncaught ReferenceError: randomStuff is not defined 在imports.html:11

有人可以解释一下这种行为的原因吗?

【问题讨论】:

标签: javascript import ecmascript-6


【解决方案1】:

ES 模块(和一般的 JS 模块)的目的之一是防止全局范围的污染。

模块导出不应泄漏到全局范围。模块的使用通常假设所有第一方代码都驻留在模块中。 logToConsole('Send from log to console') 进入主模块。

如果需要与全局范围进行互操作,则应将变量显式公开为模块内的全局变量:

window.logToConsole = function (msg) {
    consoleLogger(msg);
}

正如另一个答案已经提到的那样,仍然可能存在竞争条件,因为模块是异步加载的。脚本应该推迟到文档准备好 - jQuery ready 事件或本地对应物:

<script>
document.addEventListener('DOMContentLoaded', () => {
    logToConsole('Send from log to console');
});
<script>

【讨论】:

  • 对我来说很有意义,但使用这种技术我仍然遇到未定义的错误。
  • @estus 这很有意义。有点跑题了,但是你想使用模块来封装逻辑,但是在某些时候你需要提供一些入口点。就像我认为记录器是一个很好的例子,你可以隐藏很多实现逻辑,但仍然可以调用一些Log 方法。那么这样做的模式是什么?
  • 我添加了说明。在您的情况下,main.js 是入口点,因此您可能希望在整个应用程序中坚持使用模块。是的,在某些情况下,您可能希望将某些内容暴露给全局范围,以便在常规脚本中访问。如果是这种情况,那么你应该像答案中所示那样做,我猜。如果竞争条件是无法容忍的(例如,您需要在 的开头有 logToConsole),那么这段代码可能应该转到常规脚本。由于原生模块的实现还不成熟,它们的灵活性远不如 Webpack 等捆绑的模块化环境。
【解决方案2】:

那是因为这条线 -

<script src='./main.js' type='module'></script>

带有type="module" 的脚本会延迟执行直到它们的依赖得到满足。同时。其他脚本只会执行。

这意味着你的

<script>
    sayMyName('Cool name');
    logToConsole('Send from log to console');
</script>

将在“模块脚本”main.js 解析和评估之前执行。

【讨论】:

    【解决方案3】:

    浏览器将imports.html 中的logToConsole 解释为window.logToConsole,即它希望该函数存在于全局命名空间中。

    默认情况下,es6 模块中的对象在添加到 HTML 页面时不会放置在全局命名空间中。

    您可以通过在 HTML 中显式导入 logToConsole 来访问它:

    <script type="module">
        import { logToConsole } from './main.js';
    
        sayMyName('Cool name');
        logToConsole('Got it');
    </script>
    

    【讨论】:

    • 您知道这是访问此类方法的常用方法吗?我试图弄清楚处理这个问题的标准方法是什么。
    • 不知道,我自己也是刚开始学es6。我建议 1) 避免在 HTML 中使用 JS 和 2) 尝试在 es6 导入的上下文中处理所有内容。这样可以保持一切干净,并允许您以支持旧浏览器的方式轻松打包 Web 的 JS(使用 webpack 之类的东西)。
    【解决方案4】:

    你应该像这样在 main.js 中导出函数:

    export function logToConsole(msg) {

    然后您可以使用

    导入它

    &lt;script type="module"&gt; 内嵌脚本,其中包含导入

    见https://jakearchibald.com/2017/es-modules-in-browsers/

    看起来像这样:

    <script type="module">
        import { logToConsole } from "./main.js";
        sayMyName('Cool name');
        logToConsole('Send from log to console');
    </script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-17
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      • 2012-03-24
      • 2021-09-16
      相关资源
      最近更新 更多