作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Luke拥有计算机科学和数学硕士学位,擅长函数式编程. 在谷歌实习开启了他强大的开发生涯.
当你读到这篇介绍的时候,你的脑海里可能有两个主要的想法:
But wait! 这就是你错的地方——我会证明给你看. 如果你愿意花10分钟的时间, 我将向您展示ClojureScript如何使编写前端和React-y应用程序变得有趣, fast, 最重要的是,functional.
所以您没有太多时间来学习ClojureScript, 你只是想看看整个事情的发展. 首先,什么是ClojureScript?
来自ClojureScript网站: ClojureScript是一个针对JavaScript的Clojure编译器. 它发出的JavaScript代码与Google Closure优化编译器的高级编译模式兼容.
除此之外,ClojureScript还提供了很多功能:
有了这些,让我们用一个例子来打开这个棘手的问题:
(defn组件
[]
[:div
“你好,世界!"])
对于那些不熟悉Lisp方言或ClojureScript的人,请注意:这个示例中最重要的部分是 这是ClojureScript中React组件的最基本形式. 就是这样,一个关键字,一个字符串,还有一大堆列表. Pshaw! 你说,这与JSX或TSX中的“hello world”没有明显区别: 然而,即使从这个基本的例子中,我们也可以发现一些关键的差异: 这两个小小的差异会产生巨大的影响,不仅仅是在你如何 write 不仅在于你如何表达自己! 你会问,这是怎么回事? 让我们加入这场战斗,看看ClojureScript还为我们准备了什么…… 在本ClojureScript教程中, 我将尽量不深入挖掘Clojure(脚本)的伟大之处(这是很多东西), 但我离题了). Nonetheless, 涵盖一些基本概念将是有用的,这样就有可能掌握我们在这里可以做的事情的广度. 对于那些有经验的Clojuristas和Lispians,请随意跳转到下一节! 首先我需要介绍三个主要概念: Clojure(脚本)有一个叫做关键字的概念. 它位于常量字符串(比如Java)和键之间. They are 求值为自身的符号标识符. 例如,关键字 在Clojure中,你只需要: Also note: 在Clojure中,映射既是键到值的集合,就像Java一样 Clojure(脚本)是一种Lisp方言,这意味着它非常强调列表. 正如我之前提到的,有两件事需要注意: 要在Clojure(脚本)中构建一个列表,你可以这样做: 对于序列,这有点不同: 的将 最后是函数. Clojure(脚本)中的函数是一个 sequence 是打印出来的 没有前置 这种行为的一个必然结果是,您可以构建一个函数的定义,而无需实际执行它! 当程序被求值时,只会执行一个“裸”序列. 这将在稍后变得相关! 函数可以用几种方式定义: 现在我们已经了解了基本知识, 让我们深入一点细节,看看这里发生了什么. 在ClojureScript中,React通常使用一个库来完成 Reagent. 试剂使用Hiccup及其语法来表示HTML. From Hiccup回购’s wiki: “Hiccup将Clojure数据结构变成这样:” "变成像这样的HTML字符串:" 简单地说, 第一个元素 列表的部分成为HTML元素类型,其余部分成为该元素的内容. 您还可以选择提供一个属性映射,然后将其附加到该元素. 元素可以简单地嵌套在父元素的列表中,从而相互嵌套! 这是一个最容易理解的例子: 请注意,我们可以将任何旧函数或通用Clojure语法放入结构中,而不必显式声明嵌入方法. 毕竟,这只是一个列表! 更好的是,这个在运行时的值是多少? 一个关键词和内容的列表! 没有有趣的类型,没有神奇的隐藏方法. 这只是一份简单的清单. 你可以随心所欲地拼接和摆弄这个列表——你看到的就是你得到的. Hiccup做布局,Reagent做逻辑和事件处理, 我们最终得到了一个功能齐全的React环境. 好了,让我们用一些成分把这些联系起来. React(和Reagent)的神奇之处在于,您可以将视图和布局逻辑划分为模块, 然后您可以在整个应用程序中重用它们. 假设我们创建了一个简单的组件,它显示一个按钮和一些简单的逻辑: 关于命名的快速说明:Clojure中的模块通常是命名空间的,所以 这个简单的组件为我们提供了一个可选的礼貌小部件. 现在我们把它嵌入到 在这里,我们通过将小部件作为列表的第一项调用来将其嵌入到应用程序组件中——就像HTML关键字一样! 然后,我们可以将任何子元素或参数作为同一列表的其他元素提供给组件. 这里我们简单地通过 如果我们现在评估我们的应用函数,我们将得到以下结果: Note how DOM树中的组件只有在更新后才会被评估(并在幕后转换为真正的React对象), 它能让事情保持漂亮和简洁,并减少你在任何时候都必须处理的复杂性. 有关这一主题的更多细节可供感兴趣的人查阅 在试剂文档中. Furthermore, 注意DOM只是一个列表的列表, 组件就是返回列表的列表的函数. 为什么这对学习ClojureScript如此重要? Because 你可以对函数或列表做的任何事情,你也可以对组件做. 从这里开始,通过使用ClojureScript等Lisp方言可以获得复合回报:组件和HTML元素成为可以像操作任何其他普通数据一样操作的一等对象! 让我再说一遍: 组件和HTML元素是Clojure语言中支持的第一类对象! 没错,你没听错. 这几乎就像lisp被设计用来处理列表一样(提示:它们确实是).) 这包括: 处理普通的旧数据类型(如列表和映射)要比处理类简单得多, 从长远来看,最终会变得更强大! 好吧,让我们回顾一下. 到目前为止,我们的ClojureScript教程展示了什么? 这两点非常适合Clojure和函数式编程的精神——代码是 data 被操纵,复杂性是通过连接不太复杂的部分而建立起来的. 我们将程序(本例中的网页)表示为数据(列表), functions, 并保持这种状态,直到最后一刻,当试剂接管并将其转化为React代码. 这使得我们的代码可重用, 最重要的是,非常容易阅读和理解,几乎没有魔法. 现在我们知道了如何制作具有一些基本功能的应用程序, 所以让我们继续讨论如何让它看起来更好. 有几种方法可以解决这个问题, 最简单的是使用样式表并在组件中引用它们的类: 正如您所期望的那样,它将向我们呈现一个美丽的红色“Hello, world”!” text. However, 为什么要在组件中配置视图和逻辑代码时遇到这么多麻烦呢, 但是,然后将样式分离到样式表中—现在不仅要在两个不同的地方查找, 但你也在处理两种不同的语言! 为什么不把我们的CSS写成 组件中的代码 (在这里看到一个主题?). 这将给我们带来很多好处: 我个人最喜欢的代码内css风格是with Clojure样式表(cljss). 嵌入的CSS如下所示: class还可以为您做许多其他的事情(组合样式), animations, 元素覆盖, etc.),我在这里就不详述了. 我建议你自己去看看! 最后,需要胶水把所有这些粘在一起. 幸运的是,除了一个项目文件和一个 You need: 您可以在本ClojureScript教程中找到完整的代码示例 可以在GitHub上找到. 所以这就是它(目前). Hopefully, 我至少激起了你一点点的好奇心, 无论是检查Lisp方言(Clojure(脚本)或其他),还是尝试制作自己的Reagent应用程序! 我保证你不会后悔的.:div
, the []
, and the ()
. :div
关键字是否代表 []
向量和an很像吗 ArrayList
在Java,以及 ()
序列是不是很像 LinkedList
. 我将在这篇文章的后面更详细地讨论这个问题!
函数组件(){
return (
构建块
Keywords
:cat
总是指 :cat
再也没有别的了. 就像在Java中你可能会说:MY_KEY = " MY_KEY ";
// ...
myMap.把(MY_KEY,东西);
// ...
myMap.得到(MY_KEY);
(关联my-map:my-key thing)
(my-map :my-key) ; equivalent to (:my-key my-map) ...又好又灵活!
HashMap
)和访问其内容的功能. Neat!Lists
[]
向量和an很像吗 ArrayList
.()
序列是不是很像 LinkedList
.[1 2 3 4]
“你好”“世界”)
["my" "list" "contains" 10 "things"] ; you can mix and match types
; in Clojure lists!
'(1 2 3 4)
”(“你好”“世界”)
'
在下一节中解释.Functions
'
. 该列表的第一个元素是函数本身,接下来的所有元素都是 arguments. 例如:(+ 1 2 3 4) ; -> 10
(str "hello" " " "world") ; -> "hello world"
(println "hi!") ; prints "hi!对着控制台说
(run-my-function) ; runs the function named `run-my-function`
(+ 1 1) ; -> 2
'(+ 1 1); -> a list of a function and two numbers
; A normal function definition, assigning the function
; to the symbol `my-function`
(defn函数
[arg1 arg2]
(+ arg1 arg2))
; An anonymous function that does the same thing as the above
(fn [arg1 arg2] (+ arg1 arg2))
; Another, more concise variation of the above
#(+ %1 %2)
更仔细的检查
[:a {:href "http://github ..com GitHub“}]
GitHub
[:div
[:h1 "这是标题"]
[:p "下一个元素是1 + 1"]
[:p (+ 1 1)]]
[:div
[:h1 "这是标题"]
[:p "下一个元素是1 + 1"]
[:p 2]]
一个更复杂的例子
; widget.cljs
(defn组件
[polite?]
[:div
[:p (str "礼貌时请勿按按钮"? ", please."))]
[:input {:输入“按钮”
:重视“推我”
:on-click #(js/alert) "What did I told you .?")}]])
widget.cljs
可以在名称空间下导入 widget
. 这意味着顶层 component
函数将被访问 部件/组件
. 我喜欢每个模块只有一个顶层组件, 但这是一种风格偏好—您可能更喜欢这样命名组件函数 polite-component
or widget组件
.(when polite? ", please.")
evaluates to ", please."
when polite? == true
and to nil
when it is false
.app.cljs
:(defn app
[]
[:div
[:h1 "欢迎来到我的app"]
[小部件/组件正确]])
true
,所以在我们的小部件中 polite? == true
,因此我们得到了礼貌的版本.[:div
[:h1 "欢迎来到我的app"]
[部件/组件 true]] ; <- 部件/组件 would look more like a
; function reference, but I have kept it
; clean for legibility.
部件/组件
没有被评估过! (See 职能组 如果你感到困惑.)列表一直向下
(def["绿色" "鸡蛋"和"火腿"])
(defn li-shout
[x]
[:li (string/大写x)]
(concat [:ol] (map li-shout words)
; becomes
[:ol
(李:“绿色”)
(李:“鸡蛋”)
[:li "AND"]
[李:“火腿”]]
; in widget.cljs
(defn greeting-component
[name]
[:div
[:p (str "Hiya " name ")!")]])
; ...
(def shouty-greeting-component
#(widget/greeting-component (string/大写%)))
(defn app
[]
[:div
[:h1 "My App"]
[shouty-greeting-component "Luke"]]) ; <- will show Hiya LUKE!
(def default-btn-attrs
{:输入“按钮”
:值“我是一个按钮”
:类“my-button-class”})
(defn two-button-component
[]
[:div
[:input (assoc default-btn-attrs .)
:on-click #(println "I do one thing"))]
[:input (assoc default-btn-attrs .)
:on-click #(println "I do a different thing"))]])
一种模式出现
越来越时尚
.my-class {
color: red;
}
[:div {:class "my-class"}
“你好,世界!"]
;; -- STYLES ------------------------------------------------------------
(defstyles component-style []
{:颜色“红色”
宽度:“100%”})
;; -- VIEW --------------------------------------------------------------
(defn组件
[]
[:div {:class (component style)}]
“你好,世界!"])
defstyles
创建一个函数,它将为我们生成一个唯一的类名(这对任何导入我们组件的人来说都很好).)组装一个ClojureScript应用
index.html
在这里,样板文件是最少的.
project.clj
. 这是任何Clojure项目的主要内容,它定义了您的依赖项甚至直接从GitHub-和其他构建属性(类似于 build.gradle
or package.json
.)index.html
作为Reagent应用程序的绑定点.
React是JavaScript的前端开发框架.
ClojureScript是一个针对JavaScript的Clojure编译器. 它发出的JavaScript代码与Google Closure优化编译器的高级编译模式兼容. 它继承了Clojure的大部分属性, 一种支持交互式开发的动态编程语言.
Lisp是一组具有共同特征的语言(如Clojure)的名称, Common Lisp, Scheme etc.),所以“Lisp”本身并不是真正写出来的. (这就像问“蛋糕的原料是什么?“大多数蛋糕都遵循类似的主题,但会有不同的成分.)
Yes! Lisp优先考虑“代码即数据”的原则.这可以从名称本身的派生- list Processor中看出. Lisp方言中的一切都是数据——甚至函数调用也是一个参数列表! Lisp是一种奇妙的编程语言风格——有许多不同的风格可以满足任何需求.
Lisp方言通常调用函数式和命令式编程语言的一部分. Clojure和ClojureScript是多范式的——它们严重依赖于函数式编程, 并且允许你在需要的时候分支成命令式. 这可以让你体验到两全其美!
Google Closure Library是一个广泛且完善的JavaScript跨浏览器库, 包含各种UI工具, DOM操作, 服务器通信, testing, etc. 它旨在与Google闭包编译器一起使用.
谷歌闭包编译器通过操作将JavaScript编译成“更好的JavaScript”, optimizing, 最小化以确保它下载和运行得更快. 此外,它还对代码运行一套检查,以尽量减少运行时出现的问题.
世界级的文章,每周发一次.
世界级的文章,每周发一次.