假设我想在我的客户端 *.cljs 和我的服务器端 *.clj 之间分解一些通用代码,例如各种数据结构和通用操作,我可以这样做吗?这样做有意义吗?
5 回答
我专门编写了cljx Leiningen 插件来处理 Clojure 数据可视化库的 Clojure/ClojureScript 代码共享。95% 的非主机互操作代码看起来都一样,而 cljx 允许您通过使用 core.logic 指定重写规则来自动重写最后 5%。不过,大多数时候,它只是简单的符号替换。clojure.lang.IFn
例如,在 Clojure 中只是IFn
在 ClojureScript 中。
当为特定平台生成代码时,您还可以使用元数据来注释要包含或排除的表单。
更新:从 clojure 1.7 开始,请查看Clojure 阅读器条件或 cljc。我使用 cljc 非常成功地在服务器和浏览器之间共享大量代码。
好问题!最近我也一直在思考这个问题,并编写了一些应用程序进行实验。
这是我的列表,列出了您可能想要分享的类型以及每种类型的优缺点:
- 我的大多数客户端 cljs 文件都包含操作 dom 的代码。因此,与服务器共享任何内容都是没有意义的
- 大多数服务器端的东西都处理文件系统和数据库调用。我想您可能想从客户端调用数据库(特别是如果您使用的是支持 javascript 调用的 no-sql 数据库之一)。但是,即便如此,我觉得您应该选择从客户端调用 db 或从服务器调用 db,因此,共享 db 代码也没有多大意义。
- 共享绝对有价值的一个领域是能够在客户端和服务器之间共享和传递 clojure 数据结构(列表、向量、集合等的嵌套组合)。无需转换为 json(或 xml)并返回。例如,能够来回传递 dom 的打嗝式表示非常方便。在 gwt 中,我使用 gilead 在客户端和服务器之间共享模型。但是,在 clojure 中,您可以简单地传递数据结构,因此实际上不需要像在 gwt 中那样共享类定义。
- 我觉得我需要更多尝试的一个领域是在客户端和服务器之间共享状态。在我看来,有一些策略:在客户端存储状态(单页 ajax 类型应用程序)或在服务器上存储状态(如旧版 jsp 应用程序)或两者的组合。也许负责更新状态(原子、引用、代理或其他)的代码可以共享,然后状态可以通过请求和响应来回传递,以保持两层同步?到目前为止,简单地使用 REST 最佳实践编写服务器,然后将状态存储在客户端上似乎工作得很好。但是我可以看到在客户端和服务器之间共享状态可能会带来什么好处。
- 我还不需要共享常量和/或属性,但这可能是重用的好东西。如果您将所有应用程序的全局常量放在一个 clj 文件中,然后在编译 clojurescript 时编写一个脚本将其复制到 cljs 中,那应该可以正常工作,并且可能会节省一些代码重复。
希望这些想法有用,我对其他人迄今为止发现的内容非常感兴趣!
Leiningen的新lein-cljsbuild插件内置了对共享纯 Clojure 代码的支持。
快速编写了一些代码,将我的服务器 clojure 代码的子集复制到我的 clojurescript 代码中,在构建之前重命名为 .cljs:
(ns clj-cljs.build
(use
[clojure.java.io]
)
(require
[cljs.closure :as cljsc]
)
)
(defn list-files [path]
(.listFiles (as-file path))
)
(defn copy-file* [from to]
;(println " coping " from " to " to)
(make-parents to)
(copy from to)
)
(defn rename [to-path common-path f]
(str to-path common-path (.replaceAll (.getName f) ".clj" ".cljs"))
)
(defn clj-cljs* [files common-path to-path]
(doseq [i (filter #(.endsWith (.getName %) ".clj") files)]
(copy-file* i (file (rename to-path common-path i)))
)
(doseq [i (filter #(.isDirectory %) files)]
(clj-cljs* (list-files i) (str common-path (.getName i) "/") to-path)
)
)
(defn build [{:keys [common-path clj-path cljs-path js-path module-name]}]
(clj-cljs* (list-files (str clj-path common-path)) common-path cljs-path)
(cljsc/build
cljs-path
{
:output-dir js-path
:output-to (str js-path module-name ".js")
}
)
)
(defn build-default []
(build
{
:clj-path "/home/user/projects/example/code/src/main/clojure/"
:cljs-path "/home/user/projects/example/code/src/main/cljs/"
:js-path "/home/user/projects/example/code/public/js/cljs/"
:common-path "example/common/" ; the root of your common server-client code
:module-name "example"
}
)
)
这个问题早于cljc
,但由于我偶然发现它,我想我会提到 Clojure reader conditionals。