shadow-cljs 2.x 使用教程

2019年12月05日 阅读数:32
这篇文章主要向大家介绍shadow-cljs 2.x 使用教程,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

shadow-cljs 是一个新开发的 ClojureScript 开发和编译工具.
之前编译主要是 lein-cljsbuild, boot-cljs, lein-figwheel,
如今新的工具 Lumo 和 shadow-cljs 也能够完成编译工做了.
特别是 shadow-cljs 的功能覆盖开发当中不少场景, 对 JavaScript 开发者更友好.
对于前端开发者来讲, shadow-cljs 上手也很是简单, 不须要去管 JVM 的事情.
安装 shadow-cljs 很是简单, 经过 npm 的命令来就行了:前端

npm install -g shadow-cljs

好比你有一个 ClojureScript 项目, 命名空间叫 app, 对应目录结构:node

$ tree src/
src/
└── app
    ├── lib.cljs
    └── main.cljs

如何编译

就像 Webpack 同样, 编译以前须要有一些配置, 源码在哪里, 编译到哪里, 之类的,
因为 ClojureScript 有着本身的依赖管理工具, 因此依赖也要写在这个文件里:webpack

{:source-paths ["src/"]
 :dependencies []
 :builds {:app {:output-dir "target/"
                :asset-path "."
                :target :browser
                :modules {:main {:entries [app.main]}}
                :devtools {:after-load app.main/reload!}}}}

几个关键的参数大概的意思:git

  • :target 表示编译目标, 这里选择 :browser, 生成代码用于在浏览器当中运行.
  • :devtools 表示开发环境的设置, 这里我设置了热替换完成以后执行函数 app.main/reload!
  • :asset-path 资源存储的路径, 相对于 target/, 也影响到网页上引用代码. 默认彷佛是 ./.

更多的参数能够到文档站点上查阅: http://doc.shadow-cljs.org/github

配置完成以后能够用 shadow-cljs 命令来编译代码, 经常使用的子命令有:web

shadow-cljs compile app # 每一个文件直接编译到对应一个 js 文件
shadow-cljs release app # 编译, 进行代码合并和优化, 以及清除 dead code

其中的 app 就是配置里的 :app, 也叫 build id. 也就是说会有多个 build id 能够配置.shell

开发过程当中, 最经常使用的是 watch 命令, 它就像 webpack-dev-server,
ClojureScript 相比 js 来讲每一个函数反作用更少, 因此更适合进行热替换,
基于上边的配置, 在每次文件更新, 浏览器就会进行代码热替换, 而后会触发 app.main/reload! 函数:npm

shadow-cljs watch app # 启动编译器, 监视文件更新自动编译

ClojureScript 有一些基础的静态检查功能, 至关于增强的 lint 工具,
因此编译当中会检查代码代码并打印警告, 以及的浏览器当中弹出警告内容.json

此外命令行工具还提供了其余一些开发当中用到的功能:浏览器

shadow-cljs cljs-repl app # 有 watch 服务的状况下, 再启动一个链接到浏览器的 REPL
shadow-cljs check app # 进行 release 以前能够作一些检查
shadow-cljs release app --debug # 生成 release 的代码, 同时生成 SourceMaps 等用于调试

更多的子命令能够查阅 http://doc.shadow-cljs.org/

编译目标

shadow-cljs 支持多个编译目标, 也就是对应 :target 的配置, 通常有:

  • :browser 运行在浏览器的代码
  • :node-script 运行在 Node.js 的代码
  • :node-library 能够被 Node.js JavaScript 代码调用的模块
  • :npm-module 遵循 CommonJS 语法的独立的 js 文件

我使用最可能是 :browser, 功能完善, 已经可以胜任目前网页应用的开发需求了,
并且 :browser 模式的打包也逐渐成熟了, 补上了一些 Webpack 中的经常使用功能.
在某些只能经过 Webpack 打包状况下, 可使用 :npm-module 做为一种兼容模式,
:npm-module 模式编译的代码符合 CommonJS 规范, 能够被 Webpack 用于打包(注意这样打包带上 ClojureScript 的 runtime 代码是挺大的).

:node-script 用于开发 Node.js 脚本, 这里热替换也是基本一致的配置.
至于 :node-library 我还没用过, 参考文档应该是暴露结构给 Node.js 脚本调用.

关于这些模式具体的用途, 我搜集了一些例子, 能够参考:

配置项

除了上面的例子, shadow-cljs 的配置项还有很多, 我拿本身的脚手架配置做为例子:

{:source-paths ["src"]
 :dependencies [[mvc-works/hsl          "0.1.2"]
                [mvc-works/shell-page   "0.1.3"]
                [mvc-works/verbosely    "0.1.0-rc"]
                [respo/ui               "0.1.9"]
                [respo/reel             "0.2.0-alpha3"]
                [respo                  "0.6.4"]]
 :http {:host "localhost" :port 8081}
 :open-file-command ["subl" ["%s:%s:%s" :file :line :column]]
 :builds {:browser {:target :browser
                    :output-dir "target/browser"
                    :asset-path "/browser"
                    :modules {:main {:entries [app.main]
                                     :depends-on #{:lib}}
                              :lib {:entries [respo.core respo.macros
                                              respo.comp.inspect]}}
                    :devtools {:after-load app.main/reload!
                               :http-root "target"
                               :http-port 7000}
                    :release {:output-dir "dist/"
                              :module-hash-names true
                              :build-options {:manifest-name "cljs-manifest.json"}}}
          :ssr {:target :node-script
                :output-to "target/ssr.js"
                :main app.render/main!
                :devtools {:after-load app.render/main!}}}}

其中出现了些前面没有有道的配置, 我拎出来解释一下:

shadow-cljs 内置了一个 HTTP 服务器用于网页的调试,
须要在 :devtools 的配置当中添加 HTTP 相关的配置:

:devtools {:http-root "target"
           :http-port 7000}

watch 模式当中遇到代码存在顺发错误, 浏览器上会有界面显示 warning,
shadow-cljs 支持点击代码打开编辑器对应的行, 经过配置打开文件的命令,
传输我用 Sublime Text 打开, 这个命令当中精确到行列:

 :open-file-command ["subl" ["%s:%s:%s" :file :line :column]]

前端单页面应用倾向于生成代码到 vendor.jsmain.js 两个文件,
shadow-cljs 支持将生成代码拆分为多个文件, 这里就拆分红了 main.jslib.js,
而且, 其中指定了 mainlib 的依赖, 以及 lib 包含哪些命名空间:

:modules {:main {:entries [app.main]
                 :depends-on #{:lib}}
          :lib {:entries [respo.core respo.macros
                          respo.comp.inspect]}}

开发环境的配置, 除了经常使用的 :after-load, 还有 :before-load 等:

:devtools {:after-load app.main/reload!}

注意 :release 的配置是写在 :browser 配置内部的, 表示覆盖重复的配置,
这是 shadow-cljs 提供的一个简写, 你也能够本身专门写一遍 :release 的配置.
好比说 :output-dir "dist/" 就覆盖了外面的配置 :output-dir "target/".
:module-hash-names 声明对生成的文件名加上 MD5 方便放 CDN.
最后一行的配置是重命名 manifest.json 文件, 其中包含前面生成的带 MD5 的文件名,:

:release {:output-dir "dist/"
          :module-hash-names true
          :build-options {:manifest-name "cljs-manifest.json"}}}

:release 的配置可也支持别的配置, 好比这里的 8 表示 Hash 的长度,
manifest 文件除了 JSON, 也能够经过文件后缀支持生成 EDN 文件:

:release {:output-dir "dist/"
          :module-hash-names 8
          :build-options {:manifest-name "assets.edn"}}}

能够看不少随着 Webpack 而在前端普遍使用的功能, 在 shadow-cljs 当中作了很多的支持.
代码拆包之后, shadow-cljs 很差作异步加载, 这个是有些不足, 能够向官方反馈.

npm 模块

shadow-cljs 2.x 版本带来了在 :browser 编译目标的 npm 模块的支持, 注意写法:

(ns app.main
  (:require ["hsl" :as hsl]))

(hsl 200 80 80)

:node-script 编译目标或者 :npm-module 当中也支持这样写:

(def hsl (js/require "hsl"))

(hsl 200 80 80)

由于 require 在 Node 当中直接是函数, 在前端也能够被 Webpack 进一步处理.
大部分 npm 模块均可以直接用到的 ClojureScript 项目当中.
除此以外, Lumo 和官方的 ClojureScript 编译器也改善了对 npm 模块支持.

小结

shadow-cljs 的文章已经作得比较完善, 能够访问 http://doc.shadow-cljs.org 查阅.
若是遇到问题或者想要反馈, 能够经过下面两个地址提交:
https://github.com/thheller/s...
https://clojureverse.org/c/pr...
英语够好的话甚至直接到聊天室上找到做者, 做者在欧洲, 注意时差:
https://clojurians.slack.com/...
2017 年秋天至今 shadow-cljs 做者都在很积极更新功能,
不少的 bug 都以很是快的速度修复了, 让 shadow-cljs 更加友好.