2024 Clojure 状况调查! 中分享您的想法。

欢迎!请在 关于 页面上了解有关如何使用 Clojure 的更多信息。

0 投票
ClojureScript

ES6 有用于“默认”导入的特殊语法,而在使用导入的 ES6 代码时,CLJS 中没有等效的语法。

`
import * as name from "module-name";
(:require ["module-name" :as name])

import { export } from "module-name";
(:require ["module-name" :refer (export)])

import { export as alias } from "module-name";
(:require ["module-name" :refer (export) :rename {export alias}])

import defaultExport from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
;; 无法轻松访问 defaultExport
`

我建议在 {{ns :require}} 中添加 {{:default}}。

(:require ["module-name" :as mod :default foo])

这使得从 CLJS 使用重写的 ES6 代码变得更加方便。如果 "module-name" 拥有默认导出,您目前必须在每个地方都写 {{mod/default}},因为没有简单的方式来给默认设置别名。

`
(:require ["material-ui/RaisedButton" :as RaisedButton])
;; :as 是不正确的,用户现在必须使用
RaisedButton/default

(:require ["material-ui/RaisedButton" :default RaisedButton])
;; 将允许直接使用
RaisedButton
`

在内部,Closure 编译器(以及通过 babel 重写的 ES6 代码)会将默认导出重写为 {{.default}} 属性,所以 {{:default}} 实际上只是一个方便的方式来访问它。

已经可以工作的长版本是

(:require ["material-ui/RaisedButton" :refer (default) :rename {default RaisedButton}])

当 ES6 更广泛使用时,我们应该有一个方便的方式来正确引用“默认”导出。

(链接:1) https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Statements/import

10 个答案

0 投票

评论者:deraen

首先关于实现的考虑。这个实现将最低层({{parse-require-spec}})中的 {{:default name}} 重写到 {{:refer [default] :rename {default name}}},所以我认为其他函数不需要更改。

我还未更新 require 规范验证错误。

0 投票

评论者:dnolen

在我看来,ES6 默认导入最理想的语言结构是以下内容

(:require ["material-ui/RaisedButton" :refer [RaisedButton]])

我不明白为什么不直接使其起作用呢?

0 投票

评论者:thheller

我不理解这个建议。默认导出没有名称,{{.default}} 属性仅由转译器在互操作性中使用的。在实际的 JavaScript 中使用的是实际引用。您将如何将 {{RaisedButton}} 映射回 {{default}}?

{{["material-ui/RaisedButton" :default foo]}} 是有效的。JavaScript 模块实际上也可以具有一个 export { RaisedButton },除了 export default ...,这可能导致“猜”错名称时的冲突。默认导出并不是模块本身的别名,它是分开的。

我创建了一个参考翻译表(链接:1)并添加了 {{:default}} 别名是可靠地映射所有 ES6 导入/导出功能的唯一方法。我看不出来可以如何使 {{:refer}} 表作,这将会完全不清楚也不可靠。

import defaultExport, * as name from "module-name";

(链接:1) https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages

0 投票

评论者:dnolen

请在这个讨论中不要涉及争论,让我们专注于技术细节。我只是不想在 ns 形式中包含另一个指令,并且我更倾向于一种避免这种情况的方法。在我看来,对这种替代方法的探索还不够。

这个工单主要关注的是便利性,我认为我们需要在这件事上多思考一下,然后再做决定。

0 投票

评论者:jcr

我们为什么不能使用元数据来处理这个问题?即

(:require ["material-ui/RaisedButton" :refer [^:default RaisedButton]]) (:require ["module-name" :as mod :refer [^:default foo]]) (:require ["module-name" :refer [^:default foo, bar, baz]])

这不需要额外的 ns 指令也不造成任何歧义。

因为这个没有被用于宏(即:我们有 :require-macros,而不是普通的 :require + ^:macros 或 :refer (link: ^:macro foo)),我猜想可能有一些我不太了解的细微差别,阻止了我去实现它。有没有什么情况是这样子的?

0 投票
_评论者:thheller_

对我来说,{{^:default}} 的提示似乎从实现的角度来看相当复杂。

Java/JVM 和 Clojure 中没有默认导入/导出的等效功能。即使在 CommonJS 中也没有。这完全是关于 ES6 的。因此,将其附加到“其他”方法上在我看来是不正确的。

在我看来,之所以应该单独处理,是因为使用 {{.default}} 属性并不严格正确,它存在的全部原因完全是为了与 CommonJS 兼容。在严格的 ES6 中,它们不是由 {{:as}} 创建的“Object”的一部分。


;; a.js
export let a = 1;
export let b = 2;
export default 3;

;; b.js
import x, * as y from "./a.js"

// 这些是存在的
y.a
y.b
// 这在 ES6 中是不有效的,也不存在
y.default
// 而必须使用 x
x


鉴于 CLJS 不会发出 ES6,我们目前当然依赖于 {{.default}} 属性的存在。然而,{{webpack}} 已经改变了处理默认导出的方式,我也期望 Closure Compiler 在未来做类似的事情。
0 投票

评论者:thheller

实际上我发现我错了。重新阅读规范,{{.default}} 实际上被定义为一个 getter,所以它是可访问的。那么之前的帖子可以忽略。

不过,这并没有改变我对 {{:default}} 作为更简单的别名方法的观点。

0 投票

评论者:pesterhazy

我遇到了 ES6 默认导出,这在当今很常见。人们常常在代码中遇到 default,例如: https://github.com/pesterhazy/cljs-spa-example/issues/13。我想报告我的发现。

如问题描述中解释的,你可以使用 :refer:rename 的组合来消除对代码中 :default 的引用,将其移动到 NS 声明中。这不是最优雅的解决方案,但可以完成任务,并且与全局导出一起工作: https://github.com/pesterhazy/cljs-spa-example/pull/14/files

注意,根据我的理解,因为 default 在 ES3 中是一个严格的关键字,你需要设置以下编译器选项才能使其生效

:language-out :ecmascript5

否则 default 将被重命名为 default$

0 投票
参考: https://clojure.atlassian.net/browse/CLJS-2376 (由thheller报告)
0 投票

此问题的“官方”修复方案在以下文档中记录: https://clojure.atlassian.net/browse/CLJS-3235

...