是的,可以提供对评估代码使用的环境/预编译库的访问。
首先,您必须确保库中的函数在 JavaScript 运行时可用。换句话说,避免:advanced
优化,因为这将消除编译时 (DCE) 未调用的函数。自托管 ClojureScript 兼容:simple
.
其次,您需要使分析元数据可供将在浏览器中运行的自托管编译器使用(可以使用cljs.js/load-analysis-cache!
或可选参数cljs.js/empty-state
).
下面是一个说明如何执行此操作的最小项目(也位于https://github.com/mfikes/ambient https://github.com/mfikes/ambient):
项目代码
src/main/core.cljs
:
(ns main.core
(:require-macros [main.core :refer [analyzer-state]])
(:require [cljs.js]
[library.core]))
(def state (cljs.js/empty-state))
(defn evaluate [source cb]
(cljs.js/eval-str state source nil {:eval cljs.js/js-eval :context :expr} cb))
(defn load-library-analysis-cache! []
(cljs.js/load-analysis-cache! state 'library.core (analyzer-state 'library.core))
nil)
src/main.core.clj
:
(ns main.core
(:require [cljs.env :as env]))
(defmacro analyzer-state [[_ ns-sym]]
`'~(get-in @env/*compiler* [:cljs.analyzer/namespaces ns-sym]))
src/library/core.cljs
:
(ns library.core)
(defn my-inc [x]
(inc x))
Usage
我们有一个main.core
命名空间提供了一个evaluate
函数,此示例将展示如何在环境/预编译中调用函数library.core
命名空间。
首先,通过以下方式启动浏览器 REPL
clj -m cljs.main
在 REPL 中,通过评估来加载我们的主命名空间
(require 'main.core)
测试我们是否可以评估一些代码:
(main.core/evaluate "(+ 2 3)" prn)
这应该打印
{:ns cljs.user, :value 5}
现在,自从main.core
必需的library.core
,我们可以调用该命名空间中的函数。在 REPL 上对此进行评估将产生11
:
(library.core/my-inc 10)
现在,让我们尝试使用自托管 ClojureScript 中的这个“环境”函数:
(main.core/evaluate "(library.core/my-inc 10)" prn)
你会看到以下内容
WARNING: No such namespace: library.core, could not locate library/core.cljs, library/core.cljc, or JavaScript source providing "library.core" at line 1
WARNING: Use of undeclared Var library.core/my-inc at line 1
{:ns cljs.user, :value 11}
简而言之,正在发生的事情是,尽管library.core.my_inc
在 JavaScript 环境中可用,并且确实可以被调用,产生正确的答案,您会收到来自自托管编译器的警告,表明它对此命名空间一无所知。
这是因为编译器分析元数据不在main.core/state
原子。 (自托管编译器有自己的分析状态,在 JavaScript 环境中保存在该原子中,它与 JVM 编译器分析状态分开,在 Java 环境中通过 Clojure 保存。)
注意:如果我们有源library.core
由自托管编译器编译(也许通过使用main.core/evaluate
评估"(require 'library.core)"
,以及正确定义cljs.js/*load-fn*
可以检索此源代码,事情会很好,并且编译器分析元数据would be in main.core/state.
但这个例子是关于调用环境/预编译函数library.core
.
我们可以通过使用来解决这个问题cljs.js/load-analysis-cache!
加载与关联的分析缓存library.core
命名空间。
此示例代码通过使用从基于 JVM 的编译器抢夺分析缓存的宏,将此分析缓存直接嵌入到代码中。您可以通过任何您想要的机制将此分析缓存传输到浏览器;这只是说明了一种简单地将其直接嵌入到运输代码中的方法(它只是数据)。
继续评估以下内容,只是为了看看该命名空间的分析缓存是什么样的:
(main.core/analyzer-state 'library.core)
如果你打电话
(main.core/load-library-analysis-cache!)
该分析缓存将被加载以供自承载编译器使用。
现在如果你评估
(main.core/evaluate "(library.core/my-inc 10)" prn)
您不会看到任何警告,并且将打印以下内容:
{:ns cljs.user, :value 11}
此外,由于自托管编译器现在具有以下分析元数据libraray.core
,它可以正确警告数量错误,例如
(main.core/evaluate "(library.core/my-inc 10 12)" prn)
将导致打印:
WARNING: Wrong number of args (2) passed to library.core/my-inc at line 1
上面说明了当命名空间不存在分析器缓存时会发生什么以及如何修复它cljs.js/load-analysis-cache!
。如果你知道你总是想在启动时加载缓存,你可以简单地使用一个可选参数cljs.js/empty-state
在初始化时加载此缓存:
(defn init-state [state]
(assoc-in state [:cljs.analyzer/namespaces 'library.core]
(analyzer-state 'library.core)))
(def state (cljs.js/empty-state init-state))
其他的项目
一些(更详细的)项目使库函数可用于浏览器中自托管的 ClojureScript:
- 巴朗迈斯特 https://github.com/ctford/klangmeister
- 动力龟 https://github.com/timothypratley/power-turtle
- 生活演示 https://github.com/quoll/life-demo