Clojure的数据结构都是持久化的,但在你的示例中,似乎想要产生副作用(即直接操作DOM来改变它)。
这是一种相当过程化/命令式的方法,因此可能值得退后一步,以更功能化的方式重新制定问题。
我的个人哲学是将“视图视为数据”,并使用Clojure的持久化数据结构对其进行建模,直到最后需要呈现为止。
你是否熟悉Hiccup?
其思想是使用简单的向量和映射表示HTML或SVG DOM:
[:div {:with "attribute"} "and" [:span "children"]]
您可以通过组合普通的Clojure函数来构建它。在Clojure中,您可以使用原始的Hiccup库将其呈现为HTML,但至少有两个ClojureScript库可直接呈现到(可能已存在的)DOM结构中。
Crate是Hiccup的近似移植,而
Singult具有一些额外的语义,例如D3.js启发的数据绑定(Singult实际上是用CoffeeScript编写的,因此可从纯JavaScript中使用,并且比Crate更快)。
我的
C2库基于Singult构建了数据绑定语义,以使DOM与底层数据保持同步。考虑以下TODO列表的模板:
(bind! "#main"
[:section#main {:style {:display (when (zero? (core/todo-count)) "none")}}
[:input#toggle-all {:type "checkbox"
:properties {:checked (every? :completed? @core/!todos)}}]
[:label {:for "toggle-all"} "Mark all as complete"]
[:ul#todo-list (unify (case @core/!filter
:active (remove :completed? @core/!todos)
:completed (filter :completed? @core/!todos)
@core/!todos)
todo*)]])
(摘自C2 TodoMVC 实现)
类似“全选”复选框是否被选中的事项直接从底层数据(存储在原子中)派生。
每当数据发生变化时,模板将重新运行,dom 会自动更新。
基本思想是在应用程序数据到 Hiccup 数据结构的正向映射上建立,然后让库来处理DOM同步(添加 / 移除子元素、属性等)。
如果不必关心DOM的状态(已经添加过了吗?我需要切换一些类吗?),那么很多偶然的复杂性都会消失。