ClojureScript 简单介绍

ffc8 8年前

ClojureScript 简单介绍

这篇文章经过 Thomas Greco Jérémy Heleine 的同行评议。感谢所有 SitePoint 的评议者们,是你们使 SitePoint 的内容如此的棒!

从几个月前起,越来越多的开发者秉承着“抱紧 JavaScript 的大腿”的理念,然而,被编译为 JavaScript 的编程语言与日俱增,其中包含有 Dart、TypeScript、CoffeeScript、ClojureScript 等。

在这篇文章中我们会讨论一下 ClojureScript,一种目标为 JavaScript 的新 Clojure 编译器。我们会探讨一下使用 ClojureScript 的好处,以及怎么样快速的使用 npm 和“你们最爱的”Node.js 库搭建起它的环境。

为什么使用ClojureScript?

在网上有很多的文章解释了 ClojureScript 的好处,总的来说有以下几点:

  • 简洁性: 从语法角度来说,ClojureScript 是一种基于 Lisp 的语言,这使得它的语法十分简洁。它的简洁性使得我们甚至能在本篇文章中完全地解释它。除此之外,ClojureScript 还提供了帮助我们更方便的异步编程的工具。

  • 安全性: 这意味着更少的漏洞!ClojureScript 和其他的函数编程语言都有很多的特性来帮助我们减少和减轻一些常见的漏洞。

  • 高性能: ClojureScript 使用了Google的Closure编译器(译者注:需KX上网),这使得ClojureScript能支持无用代码清除以及其他的一些特点。

  • 实时编程: ClojureScript 生态圈提供了很多的工具来“实时编程”。这意味着当代码一改变,在你的项目上就会马上反应出来。在这篇文章中我们会看下Figwheel使读者能更好的理解这个概念。

  • 代码复用性: ClojureScript 能在全局中运行,很多人称之“同态”。这代表你能在服务器端和客户端运行相同的代码,这在 Node.js 的生态系统中已经是个流行的模式了。除此之外,ClojureScript 还能使用 Node.js 和 Java 生态圈当中的库。

安装 Clojure(Script) 工具链

在这篇文章中,我们将在 Mac OSX 环境中安装工具链。以防万一,在这里你能找到 ClojureScript wik i中在其他环境下的安装指南。首先我们将会需要一些系统依赖项,其中一个就是 OSX 下流行的包管理器,Homebrew

安装最新版本的 Java

ClojureScript 需要最新版本的 Java (在这篇文章完成的时候为 Java8)。如果在运行 lein 这个命令的时候你遇到了一个看起来像这样的错误:

Exception in thread "main" java.util.regex.PatternSyntaxException:       Unknown inline modifier near index 2 (?U)^[\p{Alpha}_$]^, compiling:(cljs/util.clj:158:33)

那么你就需要去安装最新的 Java 了。

首先,让我们在命令行界面中运行如下命令:

brew tap caskroom/cask  brew install brew-cask

如果终端出现了“already installed”这个错误,根据你终端中的引导将链接取消。一旦完成,再次安装。如下命令可以完成这个动作:

brew unlink brew-cask  brew install brew-cask

此时,执行我们需要的最后一个命令:

brew cask install java

安装Leiningen

Leiningen 是一个 Clojure 项目的构建工具,我们将用它来执行 ClojureScript 代码并安装依赖项。这一步假设你已经安装了 Homebrew

brew install leiningen

如果这步失败了,也许你需要手动安装

使用Repl

既然现在我们已经安装了 Leningen,我们就可以开始熟悉 ClojureScript 的语法了。

执行

lein rep

命令,你应该会得到类似于这样的输出:

$ lein repl  nREPL server started on port 58371 on host 127.0.0.1 - nrepl://127.0.0.1:58371  REPL-y 0.3.7, nREPL 0.2.10  Clojure 1.7.0  Java HotSpot(TM) 64-Bit Server VM 1.6.0_65-b14-466.1-11M4716      Docs: (doc function-name-here)            (find-doc "part-of-name-here")    Source: (source function-name-here)   Javadoc: (javadoc java-object-or-class-here)      Exit: Control+D or (exit) or (quit)   Results: Stored in vars *1, *2, *3, an exception in *e    user=>

我们现在已经在 ClojureScript Repl 中了,这让我们能快速执行 ClojureScript 并且查看结果。如果要离开 repl 你可以按下 Control+D。

执行这步之后,我们现在已经准备好在 ClojureScript 的语法海洋遨游并做点好玩的事啦!

ClojureScript语法

ClojureScript 是一种函数语言,这代表它使用函数以及有限的额外语言结构。在接下来的模块中我将会讲述这种语言的一些特性。

原生类型

ClojureScript 支持下列的原生类型:

  • 数字

    user=> 1.23      1.23
  • 字符串

    user=> "foo"      "foo"
  • 矢量(数组)

    user=> [:bar 3.14 "hello"]      [:bar 3.14 "hello"]
  • 地图(混合数组)

    user=> {:msg "hello" :pi 3.14 :primes [2 3 5 7 11 13]}      {:msg "hello", :pi 3.14, :primes [2 3 5 7 11 13]}
  • 关键字(用于访问地图)

    user=> :foo      :foo
  • 集合(唯一数组)

    user=> #{:bar 3.14 "hello"}      #{"hello" 3.14 :bar}

全都是函数

函数是 ClojureScript 的基础组件。你甚至是通过内置的 defn 函数来定义自己的函数。

下面是一个函数定义的例子。我们要定义一个叫 myfunction 的函数。这个函数有一个参数,叫 argument1,然后简单的返回这个参数。这个函数没啥用,但是可以很好的为我们示范语法。

user=> (defn myfunction [argument1] argment1)

估计你会觉得这个语法有些怪异,下面是 Javascript 的等价代码:

function myfunction(argument1){      return argument1;}

通过圆括号把函数名和参数包含起来的方式来调用函数。

user=> (myfunction "hello world")  "hello world"

在非函数式编程语言里,可以使用特殊的“操作符”或者关键字。例如 Javascript 中,通常可以用到 + - == if。但是在 ClojureScript 和其他基于 Lisp 的语言里,没有特殊的操作符,只有常规的函数。

If 表达式是一个函数:

user=> (if true "do true stuff here" "do false stuff here")  "do true stuff here"

数学表达式也是函数,如下所示:

user=> (+ 2 3)  5  user=> (* 2 3)  6

关于更多的 Javascript 与 ClojureScript 的等价代码的示例,可以参考这个网站

创建一个 Node.js-Clojure 项目

创建一个 ClojureScript 项目是十分方便的,Leningen 提供了项目模版来激起你的兴趣并使你能迅速地掌握怎么运行一个样板化项目。

模版是一种让我们实验并熟悉一些功用和设置的很好的资源。Clojars.org上有许多的模版,同时,你也可以在网上找到其他一些。在我们的项目当中,我们将使用 Nodejs Figwheel 项目模版

首先,让我们在命令行界面中运行如下命令来开始:

$ lein new figwheel-node hello-world

这会在 ./hello-world 目录下创建一个新的 ClojureScript 项目。这篇文章的余下部分将假设项目名称是 hello-world,当然了,如果你想的话你可以使用其他名字,但我建议你保留这个项目名,如此一来你可以跟着文章走并且不需要害怕有什么东西会出错。

接下来,进到我们创建的目录当中并安装 npm 依赖项:

$ cd hello-world  $ npm install

Points of Interest

The project folder contains a few files. In this section I want to highlight some important concepts about them:

  • package.json: This should be familiar from Node.js projects. Our npm dependencies will be added here.

  • project.clj: This file is the ClojureScript project configuration file. This is ClojureScript’s version of package.json, where we configure Clojure dependencies and compilation targets. This file also contains project details like title and description.

  • figwheel.js: This file is specific to Figweel projects. It is the bootstrap file for our project. It points Figwheel to our source code so that it can monitor it for updates. We’ll be running it with node figwheel.js.

  • ./src/hello-world/core.cljs: This is our entry-point source file. This is where we’ll start the project. Think of it similar to an index.js file in a Node.js project.

The core.cljs file contains the following content. I’ve added comments to it so that you can understand what’s going on:

;; This defines a namespace and necesscary dependencies for the current file  (ns hello-world.core    (:require [cljs.nodejs :as nodejs]))    ;; This updates the default println method to write to Node.js stdout  (nodejs/enable-util-print!)    ;; The main function of the module  ;; it prints "Hello World!" to stdout  (defn -main []    (println "Hello world!"))    ;; *main-cli-fn* is a semi-magic var that's used to set the entry  ;; *point for a node app  (set! *main-cli-fn* -main)

Running the Project

To execute the current project, open a terminal window and move to our hello-world project directory. Then, execute the following:

lein figwheel

This will start Figwheel waiting for updates to build. Leave this terminal running and open a separate terminal. In this new terminal move again to the project directory and execute the command:

node figwheel.js

You should see the output “Hello world” as reported below:

$ node figwheel.js  Hello world!  Figwheel: trying to open cljs reload socket  Figwheel: socket connection established

Using Express.js for a Webserver

Now that we have the base of a ClojureScript project setup, let’s start working with some familiar libraries in a new terminal. In our hello_world directory execute the command:

npm install --save express

Then we need to update ./src/hello-world/core.cljs as follows:

(ns hello-world.core    (:require [cljs.nodejs :as nodejs]              [clojure.string :as string]))    (nodejs/enable-util-print!)    (defonce express (nodejs/require "express"))  (defonce http (nodejs/require "http"))  (defonce server-port 3000)    (def app (express))    (. app (get "/hello"        (fn [req res] (. res (send "Hello world")))))    (def -main    (fn []      (doto (.createServer http #(app %1 %2))        (.listen server-port))))        (.listen server))))      (println (string/join " " ["Server running on" server-port]) )          (set! *main-cli-fn* -main)

Now when you run node figwheel.js on the project, you should see an output saying running on 3000. If you visit the URL http://localhost:3000/hello, you should see the outcome of the express route saying “Hello world.”

Conclusions

In this article we’ve discussed how to setup a new ClojureScript project and install a popular Node dependency in it. This gives us a great base to get more familiar with ClojureScript as a language. I’ve also put together the source code for this project that you can find on Github. It goes a bit beyond this article and demonstrates how to integrate React server side rendering.